Public release from ruodoo-project: 19.0 - 2026-05-31 21:19:12 UTC
This commit is contained in:
59
translation_helper/README.md
Normal file
59
translation_helper/README.md
Normal file
@ -0,0 +1,59 @@
|
||||
# Модуль переводов в реальном времени
|
||||
|
||||
⚠️ **ВНИМАНИЕ:** модуль находится на стадии бета-тестирования. Использование в промышленной эксплуатации **не рекомендуется**.
|
||||
|
||||
Модуль реализует **прямой перевод терминов в режиме реального времени** прямо в пользовательском интерфейсе Odoo.
|
||||
|
||||
---
|
||||
|
||||
## Краткое руководство пользователя
|
||||
|
||||
1. Установите модуль.
|
||||
|
||||
2. Перейдите в настройки и активируйте **«Режим переводчика»** — он работает аналогично режиму разработчика.
|
||||
|
||||
3. В правом верхнем углу экрана появится дополнительная иконка. Нажмите на неё, чтобы открыть меню доступных действий.
|
||||
|
||||
4. У каждого пункта меню появится иконка для перевода. При изменении перевода он будет применён, и страница автоматически перезагрузится.
|
||||
|
||||
5. У каждого поля появится кнопка для его перевода. Вы можете перевести:
|
||||
|
||||
* Название поля
|
||||
* Подсказку (tooltip), которая отображается при наведении на знак вопроса
|
||||
Перевод применяется **немедленно**.
|
||||
|
||||
6. Все переводы сохраняются:
|
||||
|
||||
* В базу данных
|
||||
* В `.po`-файлы выбранного языка, с именем, соответствующим модулю, в котором был объявлен термин
|
||||
Например, если вы перевели термин из модуля `crm`, будет создан файл:
|
||||
|
||||
```
|
||||
translation_helper/translations/{код_языка}/crm.po
|
||||
```
|
||||
|
||||
7. В этот файл выгружаются все переведённые термины.
|
||||
|
||||
---
|
||||
|
||||
## Применение переводов на новой базе
|
||||
|
||||
Если вы создадите новую базу с установленным модулем, то при выборе, например, русского языка, система будет **на лету** подменять обращения к файлу:
|
||||
|
||||
```
|
||||
odoo/addons/crm/i18n/ru.po
|
||||
```
|
||||
|
||||
на ваш собственный файл:
|
||||
|
||||
```
|
||||
translation_helper/translations/ru/crm.po
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Преимущества
|
||||
|
||||
* Вы можете **накапливать собственные переводы**, которые не зависят от внешних переводов.
|
||||
* Вы **полностью контролируете** систему локализации.
|
||||
* Перенос переводов между инстансами Odoo выполняется **простым копированием модуля**.
|
||||
134
translation_helper/README.rst
Normal file
134
translation_helper/README.rst
Normal file
@ -0,0 +1,134 @@
|
||||
==========================
|
||||
Помощник по переводам
|
||||
==========================
|
||||
|
||||
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
||||
:target: https://odoo-community.org/page/development-status
|
||||
:alt: Beta
|
||||
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
|
||||
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
|
||||
:alt: License: LGPL-3
|
||||
|
||||
|badge1| |badge2|
|
||||
|
||||
Модуль предоставляет инструменты для ручного перевода полей, представлений и меню Odoo
|
||||
с сохранением переводов в локальные PO-файлы модулей.
|
||||
|
||||
**Содержание**
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Возможности
|
||||
===========
|
||||
|
||||
* Ручной перевод полей через контекстное меню
|
||||
* Перевод нескольких полей одновременно
|
||||
* Поддержка переводимых полей типа char, text и html
|
||||
* Поддержка глоссариев для согласованной терминологии
|
||||
* Память переводов для повторного использования
|
||||
* Интеграция с режимом разработчика
|
||||
* Сохранение переводов в локальные PO-файлы модулей
|
||||
* Перевод меню и представлений
|
||||
|
||||
Настройка
|
||||
=========
|
||||
|
||||
Для настройки модуля необходимо:
|
||||
|
||||
#. Перейти в *Настройки > Общие настройки*
|
||||
#. Найти раздел "Помощник по переводам"
|
||||
#. Настроить параметры перевода:
|
||||
|
||||
* Выбрать исходный и целевые языки
|
||||
* При необходимости загрузить файлы глоссариев
|
||||
|
||||
#. Сохранить настройки
|
||||
|
||||
Настройка глоссариев:
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Файлы глоссариев размещаются в директории ``translations/{lang}/``:
|
||||
|
||||
* ``glossary.txt``: Пары терминов для согласованного перевода
|
||||
* ``{module}.po``: Переводы для конкретного модуля
|
||||
|
||||
Использование
|
||||
=============
|
||||
|
||||
Перевод полей:
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
#. Включите режим разработчика
|
||||
#. Откройте любую форму с переводимыми полями
|
||||
#. Нажмите кнопку "Перевести" в меню отладки
|
||||
#. Выберите поля для перевода
|
||||
#. Введите переводы вручную
|
||||
#. Нажмите "Сохранить"
|
||||
|
||||
Модуль выполнит следующие действия:
|
||||
|
||||
* Определит исходный язык из текущих значений полей
|
||||
* Применит термины из глоссария для согласованности
|
||||
* Использует память переводов для повторяющихся фраз
|
||||
* Обновит переводы полей для всех настроенных языков
|
||||
* Сохранит переводы в локальные PO-файлы модулей
|
||||
|
||||
Перевод меню:
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
#. Перейдите к любому пункту меню
|
||||
#. Нажмите иконку перевода в меню
|
||||
#. Выберите целевые языки
|
||||
#. Введите переводы
|
||||
#. Подтвердите сохранение
|
||||
|
||||
Перевод представлений:
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Модуль позволяет переводить:
|
||||
|
||||
* Метки полей
|
||||
* Текст подсказок
|
||||
* Текст-заполнитель
|
||||
* Метки кнопок
|
||||
* Заголовки групп
|
||||
|
||||
Известные проблемы / Планы развития
|
||||
====================================
|
||||
|
||||
* Добавить поддержку дополнительных сервисов перевода
|
||||
* Пакетный перевод для нескольких записей
|
||||
* Оценка качества переводов
|
||||
* Контекстно-зависимые переводы
|
||||
* Рабочий процесс проверки переводов
|
||||
|
||||
Отслеживание ошибок
|
||||
===================
|
||||
|
||||
Ошибки отслеживаются на `GitHub Issues <https://github.com/OCA/server-tools/issues>`_.
|
||||
В случае проблем, пожалуйста, проверьте, не была ли ваша проблема уже зарегистрирована.
|
||||
|
||||
Авторы
|
||||
======
|
||||
|
||||
Авторы
|
||||
~~~~~~
|
||||
|
||||
* MK.Lab, RuOdoo
|
||||
|
||||
Участники
|
||||
~~~~~~~~~
|
||||
|
||||
* MK.Lab, RuOdoo
|
||||
|
||||
Сопровождающие
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Модуль сопровождается MK.Lab, RuOdoo.
|
||||
|
||||
.. image:: https://ruodoo.ru/logo.png
|
||||
:alt: MK.Lab, RuOdoo
|
||||
:target: https://ruodoo.ru
|
||||
|
||||
Модуль является частью проекта инструментов локализации и перевода.
|
||||
80
translation_helper/__init__.py
Normal file
80
translation_helper/__init__.py
Normal file
@ -0,0 +1,80 @@
|
||||
# This is monkeypatch for changing default get_po_paths function
|
||||
# from odoo.tools.translate import get_po_paths
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
from contextlib import suppress
|
||||
from os.path import join
|
||||
from pathlib import Path
|
||||
|
||||
from odoo.tools.misc import file_path
|
||||
|
||||
dir_path = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
|
||||
def get_po_paths_fixed(module_name: str, lang: str):
|
||||
lang_base = lang.split("_")[0]
|
||||
if lang_base == "es" and lang != "es_ES":
|
||||
# force es_419 as fallback language for the spanish variations
|
||||
if lang == "es_419":
|
||||
langs = ["es_419"]
|
||||
else:
|
||||
langs = ["es_419", lang]
|
||||
else:
|
||||
langs = [lang_base, lang]
|
||||
|
||||
po_paths = [
|
||||
path
|
||||
for lang_ in langs
|
||||
for dir_ in ("i18n", "i18n_extra")
|
||||
if (path := join(module_name, dir_, lang_ + ".po"))
|
||||
]
|
||||
po_files_dir = os.path.join(dir_path, "translations", lang_base.strip())
|
||||
list_of_supported_modules = []
|
||||
if os.path.exists(po_files_dir):
|
||||
list_of_supported_modules = [
|
||||
Path(file_name).stem for file_name in os.listdir(po_files_dir)
|
||||
]
|
||||
for path in po_paths:
|
||||
if module_name in list_of_supported_modules:
|
||||
path = join(po_files_dir, module_name + ".po")
|
||||
with suppress(FileNotFoundError):
|
||||
yield file_path(path)
|
||||
|
||||
|
||||
def _push_translation_fixed(
|
||||
self, module, ttype, name, res_id, source, comments=None, record_id=None, value=None
|
||||
):
|
||||
"""Insert a translation that will be used in the file generation
|
||||
In po file will create an entry
|
||||
#: <ttype>:<name>:<res_id>
|
||||
#, <comment>
|
||||
msgid "<source>"
|
||||
record_id is the database id of the record being translated
|
||||
"""
|
||||
# empty and one-letter terms are ignored, they probably are not meant to be
|
||||
# translated, and would be very hard to translate anyway.
|
||||
|
||||
sanitized_term = (source or "").strip()
|
||||
# remove non-alphanumeric chars
|
||||
sanitized_term = re.sub(r"\W+", "", sanitized_term)
|
||||
if not sanitized_term or len(sanitized_term) < 1:
|
||||
return
|
||||
self._to_translate.append(
|
||||
(module, source, name, res_id, ttype, tuple(comments or ()), record_id, value)
|
||||
)
|
||||
|
||||
|
||||
module = sys.modules["odoo.addons.base.models.ir_module"]
|
||||
module.get_po_paths = get_po_paths_fixed
|
||||
|
||||
module = sys.modules["odoo.tools.translate"]
|
||||
module.get_po_paths = get_po_paths_fixed
|
||||
|
||||
module = sys.modules["odoo.tools.translate"]
|
||||
module.TranslationReader._push_translation = _push_translation_fixed
|
||||
|
||||
|
||||
from . import models # noqa
|
||||
from . import wizards # noqa
|
||||
54
translation_helper/__manifest__.py
Normal file
54
translation_helper/__manifest__.py
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "Translation Helper",
|
||||
"summary": "Помощник для ручного перевода полей и представлений Odoo",
|
||||
"description": """
|
||||
Помощник по переводам
|
||||
=====================
|
||||
|
||||
Инструмент для ручного перевода полей, представлений и меню Odoo с поддержкой
|
||||
глоссариев, памяти переводов и сохранением в локальные PO-файлы модулей.
|
||||
|
||||
Возможности:
|
||||
------------
|
||||
* Ручной перевод полей через контекстное меню
|
||||
* Перевод нескольких полей одновременно
|
||||
* Поддержка переводимых полей типа char, text и html
|
||||
* Поддержка глоссариев для согласованной терминологии
|
||||
* Память переводов для повторного использования
|
||||
* Интеграция с режимом разработчика
|
||||
* Настраиваемые параметры перевода
|
||||
* Поддержка перевода меню и представлений
|
||||
* Сохранение переводов в локальные PO-файлы модулей
|
||||
|
||||
Технические детали:
|
||||
-------------------
|
||||
* Расширяет модели ir.http, ir.qweb, base
|
||||
* JavaScript-компоненты для интеграции с UI
|
||||
* Слой абстракции сервисов перевода
|
||||
* Парсинг PO-файлов и управление глоссариями
|
||||
* Контекстно-зависимый перевод с метаданными полей
|
||||
""",
|
||||
"author": "MK.Lab, RuOdoo",
|
||||
"website": "https://ruodoo.ru",
|
||||
"category": "Productivity/Translations",
|
||||
"version": "19.0.1.0.1",
|
||||
"depends": ["web"],
|
||||
"external_dependencies": {
|
||||
"python": ["siphashc", "polib"],
|
||||
},
|
||||
"license": "LGPL-3",
|
||||
"data": [
|
||||
"security/ir.model.access.csv",
|
||||
"views/res_config_settings_views.xml",
|
||||
"views/templates.xml",
|
||||
"wizards/translation_helper_wizard.xml",
|
||||
],
|
||||
"assets": {
|
||||
"web.assets_backend": [
|
||||
"translation_helper/static/src/**/*",
|
||||
],
|
||||
},
|
||||
"installable": True,
|
||||
"application": False,
|
||||
"maintainers": ["mklab", "ruodoo"],
|
||||
}
|
||||
85
translation_helper/docs/readme.md
Normal file
85
translation_helper/docs/readme.md
Normal file
@ -0,0 +1,85 @@
|
||||
Переведенные поля больше не используют модель `ir.translation`. Теперь они хранят все свои значения как `JSON` и сохраняют их в столбцах `JSONB` в
|
||||
таблице соответствующей модели. Значение столбца поля либо `NULL`, либо `JSON dict`, где ключом является код языка, а его значение и есть термин. Ключ
|
||||
`en_US` всегда ставится по умолчанию, в тех случаях когда нет других ключей Пустой текст допускается в значениях перевода, но не `NULL`.
|
||||
|
||||
Вот пример поля с параметром `translate=True`:
|
||||
|
||||
```json
|
||||
NULL
|
||||
{"en_US": "Foo"}
|
||||
{"en_US": "Foo", "fr_FR": "Bar", "nl_NL": "Baz"}
|
||||
{"en_US": "Foo", "fr_FR": "", "nl_NL": "Baz"}
|
||||
```
|
||||
|
||||
Как и ранее, если полю установить значение `False` , то на уровне БД оно будет `NULL`, т. е. `False` для всех языков. Однако если установить значение
|
||||
`""` (пустая строка) система делает его значение пустым на текущем языке, но не отменяет значения на других языках.
|
||||
|
||||
Вот пример поля с параметром `translate=xml_translate`:
|
||||
|
||||
```json
|
||||
NULL
|
||||
{"en_US": "<div>Foo<p>Bar</p></div>", "fr_FR": "<div>Fou<p>Barre</p></div>"}
|
||||
```
|
||||
|
||||
Изменение для полей `callable(translate)`: теперь в таком поле можно записать любое значение на любом языке. Новое значение будет адаптировано для
|
||||
всех языков на основе сопоставления терминов между языками в старых значениях. В основном структура значения должна оставаться одинаковой для всех
|
||||
языков, как и раньше.
|
||||
|
||||
Чтение переведенного поля теперь проще и быстрее, чем в предыдущей реализации. Мы извлекаем значение поля на текущем языке, объединяя его значение со
|
||||
значением поля `en_US`:
|
||||
|
||||
```sql
|
||||
SELECT id, COALESCE(name->>'fr_FR', name->>'en_US') AS name ...
|
||||
```
|
||||
|
||||
Необработанный кэш поля содержит в базе данных (за исключением отсутствующих языков) либо `None`, либо `dict`, который концептуально является
|
||||
подмножеством значения `JSON` . Для простоты большинство операций кэширования работают с `dict` и возвращают текстовое значение на текущем языке.
|
||||
|
||||
Индексы триграмм адаптированы к новой стратегии хранения и должны позволять поиск на любом языке. До этого изменения индексировалось только исходное
|
||||
значение поля `en_US`.
|
||||
|
||||
Хранимые вычисляемые переведенные поля не поддерживаются фреймворком из-за сложности самого вычисления: поле должно быть вычислено на всех активных
|
||||
языках. Мы решили не предоставлять никаких хуков для вычисления поля на всех языках одновременно, и фреймворк всегда вызывает метод вычисления один
|
||||
раз, чтобы пересчитать его.
|
||||
|
||||
Переводы кода больше не хранятся в базе данных. Они становятся статическими и извлекаются из файлов PO по мере необходимости. Worker просто использует
|
||||
кэш с извлеченными переводами кода для производительности. Это разумно, так как переводы кода `fr_FR` для всех модулей занимают около 2 МБ памяти, и
|
||||
кэш может быть общим для всех реестров в Worker. Изменение переводов кода требует обновления соответствующего файла PO и перезагрузки Worker.
|
||||
|
||||
Сводка по производительности: (+) чтение переведенных полей `model` происходит быстрее (+) чтение переведенных полей `model_terms` происходит намного
|
||||
быстрее (нет необходимости вводить переводы в исходное значение) (+) поиск переведенных полей с оператором `ilike` происходит намного быстрее, если
|
||||
поле индексировано с помощью `trigram` (+) обновление переведенных полей требует меньше очистки `ORM` (-) импорт переводов из файлов `PO` происходит в
|
||||
2 раза медленнее
|
||||
|
||||
Несколько экстра исправлений: сделать поле `name` модели `ir.actions.actions` переведенным; из-за наследования `PG` это необходимо для того, чтобы
|
||||
сделать определение столбца согласованным во всех моделях, которые наследуются от `ir.actions.actions`. добавить внутренний API для
|
||||
веб-клиента/клиента веб-сайта для редактирования переводов переместить методы `get_field_string()` в модель `ir.model.fields` переместить
|
||||
`_load_module_terms` в модель `ir.module.module` адаптировать тесты в `test_impex`, `test_new_api` поскольку `env.lang` внедряется в запросы `SQL`,
|
||||
его возвращаемое значение теперь гарантированно соответствует допустимому активному языку или `None` удалить мастер для вставки отсутствующих
|
||||
переводов (больше не имеет смысла)
|
||||
|
||||
task-id: 2081307
|
||||
|
||||
Резюме по исследованию формирования переводов для представлений: Переводы для имен полей формируются следующим образом:
|
||||
|
||||
1. Необходимо иметь перевод в PO файле, тогда при обновлении перевода как такового, применится новый перевод
|
||||
2. Для перевода имени поля создается в модели `ir.model.fields` создано поле `field_description` которое и хранит его перевод
|
||||
3. Тем не менее данный перевод будет применен только после перезагрузке системы
|
||||
4. Это связано с тем, что при обновлении страницы вызывается метод `get_views` из модели `ir.ui.view` в ответе которого есть ключ `models`, и в нем
|
||||
содержатся переводы для имен полей. Но происходит это не через переводы, а через сбор атрибутов поля Есть предположение что для конкретного языка в
|
||||
памяти создается единожды класс для каждого поля с переведенными атрибутами и именно по этому помогает только перезагрузка.
|
||||
|
||||
Дополнительные сведения полученные экспериментальным путем
|
||||
|
||||
1. Если в каталоге i18n есть pot файл, то если в нем не указаны какие либо термины для перевода, то эти термины будут игнорироваться во всех po файлах
|
||||
этих переводов. Т.е для того, чтобы переводы из po файла применились нужно указать термины в pot файле, либо удалить pot файл из каталог
|
||||
2. Часть терминов берется напрямую из po файлов при обновлении страницы и на фронт загружаются все термины, которые помечены комментарием
|
||||
`#. odoo-javascript`
|
||||
3.
|
||||
|
||||
В системе есть как минимум 3 больших блока для которых необходимо сформировать инструменты для перевода:
|
||||
|
||||
1. Атрибуты полей
|
||||
2. Термины для перводов во вьюхах в том числе и Search View
|
||||
3. Термины для переводов в js коде
|
||||
4. Меню?
|
||||
435
translation_helper/i18n/ru.po
Normal file
435
translation_helper/i18n/ru.po
Normal file
@ -0,0 +1,435 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * translation_helper
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 17.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-05-25 16:08+0000\n"
|
||||
"PO-Revision-Date: 2025-05-25 16:08+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "(change)"
|
||||
msgstr "(изменить)"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "(create)"
|
||||
msgstr "(создать)"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/settings/res_config_dev_tool.xml:0
|
||||
#, python-format
|
||||
msgid "Activate the translate mode"
|
||||
msgstr "Активируйте режим переводчика"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_res_config_settings__translation_weblate_server_address
|
||||
msgid "Address"
|
||||
msgstr "Адрес"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model_terms:ir.ui.view,arch_db:translation_helper.res_config_settings_view_form_budget
|
||||
msgid "Address of your Weblate server"
|
||||
msgstr "Адрес вашего weblate сервера"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model_terms:ir.ui.view,arch_db:translation_helper.res_config_settings_view_form_budget
|
||||
msgid "Alias of your Weblate project"
|
||||
msgstr "Алиас вашего Weblate проекта"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "All users"
|
||||
msgstr "Все пользователи"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model_terms:ir.ui.view,arch_db:translation_helper.translation_helper_wizard_form
|
||||
msgid "Apply"
|
||||
msgstr "Применить"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model,name:translation_helper.model_base
|
||||
msgid "Base"
|
||||
msgstr "База"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model_terms:ir.ui.view,arch_db:translation_helper.translation_helper_wizard_form
|
||||
msgid "Cancel"
|
||||
msgstr "Отменить"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu.js:0
|
||||
#, python-format
|
||||
msgid "Choose a translate command..."
|
||||
msgstr "Выберите команду перевода..."
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "Close"
|
||||
msgstr "Закрыть"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "Condition:"
|
||||
msgstr "Состояние"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model,name:translation_helper.model_res_config_settings
|
||||
msgid "Config Settings"
|
||||
msgstr "Параметры конфигурации"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__create_uid
|
||||
msgid "Created by"
|
||||
msgstr "Создал"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__create_date
|
||||
msgid "Created on"
|
||||
msgstr "Создано"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "Creation Date:"
|
||||
msgstr "Дата создания"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "Creation User:"
|
||||
msgstr "Кто создал:"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/settings/res_config_dev_tool.xml:0
|
||||
#, python-format
|
||||
msgid "Deactivate the translate mode"
|
||||
msgstr "Деактивируйте режим переводчика"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "Default:"
|
||||
msgstr "По-умолчанию:"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__display_name
|
||||
msgid "Display Name"
|
||||
msgstr "Оторажаемое имя"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model,name:translation_helper.model_ir_model_fields
|
||||
msgid "Fields"
|
||||
msgstr "Поля"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_items.js:0
|
||||
#, python-format
|
||||
msgid "Get View"
|
||||
msgstr "Получить представление"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields.selection,name:translation_helper.selection__res_config_settings__translation_weblate_server_protocol__http
|
||||
msgid "HTTP"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model,name:translation_helper.model_ir_http
|
||||
msgid "HTTP Routing"
|
||||
msgstr "Маршрутизация HTTP"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields.selection,name:translation_helper.selection__res_config_settings__translation_weblate_server_protocol__https
|
||||
msgid "HTTPS"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/field_translation.xml:0
|
||||
#, python-format
|
||||
msgid "Help"
|
||||
msgstr "Атрибут поля Help"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__id
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "ID:"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__modules
|
||||
msgid "In Apps"
|
||||
msgstr "В приложениях"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__language_id
|
||||
msgid "Language"
|
||||
msgstr "Язык"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model_terms:ir.ui.view,arch_db:translation_helper.res_config_settings_view_form_budget
|
||||
msgid "Language of your Weblate project"
|
||||
msgstr "Язык для вашего Weblate проекта"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__write_uid
|
||||
msgid "Last Updated by"
|
||||
msgstr "Последний раз обновил"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__write_date
|
||||
msgid "Last Updated on"
|
||||
msgstr "Последний раз обновлялось"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "Latest Modification Date:"
|
||||
msgstr "Дата последнего изменения"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "Latest Modification by:"
|
||||
msgstr "Кто последний изменил:"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.js:0
|
||||
#, python-format
|
||||
msgid "Leave the Translate Tools"
|
||||
msgstr "Выйти из режима переводчика"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,help:translation_helper.field_translation_helper_wizard__weblate_link
|
||||
msgid "Link to current term on Weblate server"
|
||||
msgstr "Ссылка на текущйи термин в вашем Weblate сервере"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,help:translation_helper.field_translation_helper_wizard__modules
|
||||
msgid "List of modules in which the translation term is defined"
|
||||
msgstr "Список модулей в которых определен данный термин"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__metadata
|
||||
#: model:ir.model.fields,help:translation_helper.field_translation_helper_wizard__metadata
|
||||
msgid "Metadata"
|
||||
msgstr "Метаданные"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model,name:translation_helper.model_ir_module_module
|
||||
msgid "Module"
|
||||
msgstr "Модуль"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "No Update:"
|
||||
msgstr "Нельзя обновить:"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu.js:0
|
||||
#, python-format
|
||||
msgid "No translate command found"
|
||||
msgstr "Не найдено никакой команды для режима переводчика"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "Only you"
|
||||
msgstr "Только вы"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu.xml:0
|
||||
#, python-format
|
||||
msgid "Open translate tools"
|
||||
msgstr "Открыть инструменты переводчика"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_res_config_settings__translation_weblate_project_alias
|
||||
msgid "Project Alias"
|
||||
msgstr "Алиас проекта"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_res_config_settings__translation_weblate_project_language
|
||||
msgid "Project Language"
|
||||
msgstr "Язык проекта"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_res_config_settings__translation_weblate_server_protocol
|
||||
msgid "Protocol"
|
||||
msgstr "Протокол"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model_terms:ir.ui.view,arch_db:translation_helper.res_config_settings_view_form_budget
|
||||
msgid "Protocol of your Weblate server"
|
||||
msgstr "Протокол вашего Weblate сервера"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model,name:translation_helper.model_ir_qweb
|
||||
msgid "Qweb"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "Save default"
|
||||
msgstr "Сохранить параметры по умолчанию"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,help:translation_helper.field_res_config_settings__translation_weblate_project_language
|
||||
msgid "Select your Weblate project alias"
|
||||
msgstr "Выберите алиас вашего Weblate проекта"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,help:translation_helper.field_res_config_settings__translation_weblate_server_protocol
|
||||
msgid "Select your Weblate server protocol"
|
||||
msgstr "Выберите протокол вашего Weblate сервера"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/field_translation.xml:0
|
||||
#, python-format
|
||||
msgid "String"
|
||||
msgstr "Строка"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__term_value
|
||||
msgid "Term"
|
||||
msgstr "Термин"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,help:translation_helper.field_translation_helper_wizard__term_value
|
||||
msgid "Term value to translate"
|
||||
msgstr "Значение терминя для перевода"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,help:translation_helper.field_translation_helper_wizard__language_id
|
||||
msgid "The language for which the translation will be applied"
|
||||
msgstr "Язык для которго будет прменяться перевод"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_items.js:0
|
||||
#, python-format
|
||||
msgid "Translate SearchView"
|
||||
msgstr "Перевести SearchView"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_items.js:0
|
||||
#, python-format
|
||||
msgid "Translate View: "
|
||||
msgstr "Перевести представление:"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu.js:0
|
||||
#, python-format
|
||||
msgid "Translate tools..."
|
||||
msgstr "Инструменты переводчика..."
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/fields/translation_dialog.js:0
|
||||
#, python-format
|
||||
msgid "Translate: %s"
|
||||
msgstr "Перевести: %s"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__translation_value
|
||||
#: model_terms:ir.ui.view,arch_db:translation_helper.res_config_settings_view_form_budget
|
||||
msgid "Translation"
|
||||
msgstr "Перевод"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model,name:translation_helper.model_translation_service
|
||||
#: model_terms:ir.ui.view,arch_db:translation_helper.res_config_settings_view_form_budget
|
||||
msgid "Translation Helper"
|
||||
msgstr "Помощник для переводчика"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model,name:translation_helper.model_translation_helper_wizard
|
||||
msgid "Translation Helper Wizard"
|
||||
msgstr "Помощник для переводчика"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,help:translation_helper.field_translation_helper_wizard__translation_value
|
||||
msgid "Translation for term value"
|
||||
msgstr "Перевод для значения термина"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/settings/res_config_dev_tool.xml:0
|
||||
#, python-format
|
||||
msgid "Translator Tools"
|
||||
msgstr "Инструменты переводчика"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__weblate_link
|
||||
msgid "Weblate Link"
|
||||
msgstr "Ссылка на Weblate"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,help:translation_helper.field_res_config_settings__translation_weblate_project_alias
|
||||
msgid "Write your Weblate project alias"
|
||||
msgstr "Запишите алиас вашего Weblate проекта"
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,help:translation_helper.field_res_config_settings__translation_weblate_server_address
|
||||
msgid "Write your Weblate server address"
|
||||
msgstr "Запишите адрес вашего Weblate сервера"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-python
|
||||
#: code:addons/translation_helper/models/translation_service.py:0
|
||||
#, python-format
|
||||
msgid "Write your translation"
|
||||
msgstr "Запишите ваш перевод"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "XML ID:"
|
||||
msgstr ""
|
||||
435
translation_helper/i18n/translation_helper.pot
Normal file
435
translation_helper/i18n/translation_helper.pot
Normal file
@ -0,0 +1,435 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * translation_helper
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 17.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-05-25 16:08+0000\n"
|
||||
"PO-Revision-Date: 2025-05-25 16:08+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "(change)"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "(create)"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/settings/res_config_dev_tool.xml:0
|
||||
#, python-format
|
||||
msgid "Activate the translate mode"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_res_config_settings__translation_weblate_server_address
|
||||
msgid "Address"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model_terms:ir.ui.view,arch_db:translation_helper.res_config_settings_view_form_budget
|
||||
msgid "Address of your Weblate server"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model_terms:ir.ui.view,arch_db:translation_helper.res_config_settings_view_form_budget
|
||||
msgid "Alias of your Weblate project"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "All users"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model_terms:ir.ui.view,arch_db:translation_helper.translation_helper_wizard_form
|
||||
msgid "Apply"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model,name:translation_helper.model_base
|
||||
msgid "Base"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model_terms:ir.ui.view,arch_db:translation_helper.translation_helper_wizard_form
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu.js:0
|
||||
#, python-format
|
||||
msgid "Choose a translate command..."
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "Condition:"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model,name:translation_helper.model_res_config_settings
|
||||
msgid "Config Settings"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__create_uid
|
||||
msgid "Created by"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__create_date
|
||||
msgid "Created on"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "Creation Date:"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "Creation User:"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/settings/res_config_dev_tool.xml:0
|
||||
#, python-format
|
||||
msgid "Deactivate the translate mode"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "Default:"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__display_name
|
||||
msgid "Display Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model,name:translation_helper.model_ir_model_fields
|
||||
msgid "Fields"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_items.js:0
|
||||
#, python-format
|
||||
msgid "Get View"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields.selection,name:translation_helper.selection__res_config_settings__translation_weblate_server_protocol__http
|
||||
msgid "HTTP"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model,name:translation_helper.model_ir_http
|
||||
msgid "HTTP Routing"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields.selection,name:translation_helper.selection__res_config_settings__translation_weblate_server_protocol__https
|
||||
msgid "HTTPS"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/field_translation.xml:0
|
||||
#, python-format
|
||||
msgid "Help"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__id
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "ID:"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__modules
|
||||
msgid "In Apps"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__language_id
|
||||
msgid "Language"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model_terms:ir.ui.view,arch_db:translation_helper.res_config_settings_view_form_budget
|
||||
msgid "Language of your Weblate project"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__write_uid
|
||||
msgid "Last Updated by"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__write_date
|
||||
msgid "Last Updated on"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "Latest Modification Date:"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "Latest Modification by:"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.js:0
|
||||
#, python-format
|
||||
msgid "Leave the Translate Tools"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,help:translation_helper.field_translation_helper_wizard__weblate_link
|
||||
msgid "Link to current term on Weblate server"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,help:translation_helper.field_translation_helper_wizard__modules
|
||||
msgid "List of modules in which the translation term is defined"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__metadata
|
||||
#: model:ir.model.fields,help:translation_helper.field_translation_helper_wizard__metadata
|
||||
msgid "Metadata"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model,name:translation_helper.model_ir_module_module
|
||||
msgid "Module"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "No Update:"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu.js:0
|
||||
#, python-format
|
||||
msgid "No translate command found"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "Only you"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu.xml:0
|
||||
#, python-format
|
||||
msgid "Open translate tools"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_res_config_settings__translation_weblate_project_alias
|
||||
msgid "Project Alias"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_res_config_settings__translation_weblate_project_language
|
||||
msgid "Project Language"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_res_config_settings__translation_weblate_server_protocol
|
||||
msgid "Protocol"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model_terms:ir.ui.view,arch_db:translation_helper.res_config_settings_view_form_budget
|
||||
msgid "Protocol of your Weblate server"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model,name:translation_helper.model_ir_qweb
|
||||
msgid "Qweb"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "Save default"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,help:translation_helper.field_res_config_settings__translation_weblate_project_language
|
||||
msgid "Select your Weblate project alias"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,help:translation_helper.field_res_config_settings__translation_weblate_server_protocol
|
||||
msgid "Select your Weblate server protocol"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/field_translation.xml:0
|
||||
#, python-format
|
||||
msgid "String"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__term_value
|
||||
msgid "Term"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,help:translation_helper.field_translation_helper_wizard__term_value
|
||||
msgid "Term value to translate"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,help:translation_helper.field_translation_helper_wizard__language_id
|
||||
msgid "The language for which the translation will be applied"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_items.js:0
|
||||
#, python-format
|
||||
msgid "Translate SearchView"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_items.js:0
|
||||
#, python-format
|
||||
msgid "Translate View: "
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu.js:0
|
||||
#, python-format
|
||||
msgid "Translate tools..."
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/fields/translation_dialog.js:0
|
||||
#, python-format
|
||||
msgid "Translate: %s"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__translation_value
|
||||
#: model_terms:ir.ui.view,arch_db:translation_helper.res_config_settings_view_form_budget
|
||||
msgid "Translation"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model,name:translation_helper.model_translation_service
|
||||
#: model_terms:ir.ui.view,arch_db:translation_helper.res_config_settings_view_form_budget
|
||||
msgid "Translation Helper"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model,name:translation_helper.model_translation_helper_wizard
|
||||
msgid "Translation Helper Wizard"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,help:translation_helper.field_translation_helper_wizard__translation_value
|
||||
msgid "Translation for term value"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/settings/res_config_dev_tool.xml:0
|
||||
#, python-format
|
||||
msgid "Translator Tools"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,field_description:translation_helper.field_translation_helper_wizard__weblate_link
|
||||
msgid "Weblate Link"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,help:translation_helper.field_res_config_settings__translation_weblate_project_alias
|
||||
msgid "Write your Weblate project alias"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#: model:ir.model.fields,help:translation_helper.field_res_config_settings__translation_weblate_server_address
|
||||
msgid "Write your Weblate server address"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-python
|
||||
#: code:addons/translation_helper/models/translation_service.py:0
|
||||
#, python-format
|
||||
msgid "Write your translation"
|
||||
msgstr ""
|
||||
|
||||
#. module: translation_helper
|
||||
#. odoo-javascript
|
||||
#: code:addons/translation_helper/static/src/translate/translate_menu_items.xml:0
|
||||
#, python-format
|
||||
msgid "XML ID:"
|
||||
msgstr ""
|
||||
7
translation_helper/models/__init__.py
Normal file
7
translation_helper/models/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
from . import base
|
||||
from . import translation_service
|
||||
from . import res_config_settings
|
||||
from . import ir_module
|
||||
from . import ir_model_fields
|
||||
from . import ir_http
|
||||
from . import ir_qweb
|
||||
87
translation_helper/models/base.py
Normal file
87
translation_helper/models/base.py
Normal file
@ -0,0 +1,87 @@
|
||||
import logging
|
||||
|
||||
from odoo import models
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Model(models.AbstractModel):
|
||||
_inherit = "base"
|
||||
|
||||
def _get_record_modules(self):
|
||||
"""Возвращает список модулей, в которых определены текущие записи."""
|
||||
xml_items = (
|
||||
self.env["ir.model.data"]
|
||||
.sudo()
|
||||
.search(
|
||||
[
|
||||
("res_id", "in", self.ids),
|
||||
("model", "=", self._name),
|
||||
]
|
||||
)
|
||||
)
|
||||
return list({item.module for item in xml_items if item.module})
|
||||
|
||||
def update_field_translations(self, field_name, translations):
|
||||
"""OVERRIDE
|
||||
Update the values of a translated field.
|
||||
|
||||
:param str field_name: field name
|
||||
:param dict translations: if the field has ``translate=True``, it should be a dictionary
|
||||
like ``{lang: new_value}``; if ``translate`` is a callable, it should be like
|
||||
``{lang: {old_term: new_term}}``
|
||||
|
||||
Odoo 19: calls super().update_field_translations() to preserve original behaviour,
|
||||
which internally dispatches to _update_field_translations.
|
||||
"""
|
||||
modules = self._get_record_modules()
|
||||
if not modules:
|
||||
_logger.warning(
|
||||
"Не удалось определить модуль для модели %s (ids: %s)",
|
||||
self._name,
|
||||
self.ids,
|
||||
)
|
||||
return super().update_field_translations(field_name, translations)
|
||||
|
||||
original_value = self.with_context(lang=None)[field_name]
|
||||
if not original_value:
|
||||
_logger.warning(
|
||||
"Оригинальное значение поля '%s' пустое для модели %s (ids: %s)",
|
||||
field_name,
|
||||
self._name,
|
||||
self.ids,
|
||||
)
|
||||
return super().update_field_translations(field_name, translations)
|
||||
|
||||
# Обновляем перевод для каждой локали
|
||||
for lang, translated_value in translations.items():
|
||||
if not translated_value:
|
||||
_logger.info("Пропущен пустой перевод для языка: %s", lang)
|
||||
continue
|
||||
|
||||
lang_record = (
|
||||
self.env["res.lang"].sudo().search([("code", "=", lang)], limit=1)
|
||||
)
|
||||
iso_code = lang_record.iso_code or lang.split("_")[0]
|
||||
source_value = original_value
|
||||
translated_value_string = translated_value
|
||||
if isinstance(translated_value, dict):
|
||||
source_value = translated_value.get("source", original_value)
|
||||
translated_value_string = list(translated_value.values())[0]
|
||||
|
||||
self.env[
|
||||
"translation.helper.wizard"
|
||||
].sudo().update_term_translation_in_module(
|
||||
modules[0], source_value, translated_value_string, iso_code
|
||||
)
|
||||
_logger.info(
|
||||
"Обновлен перевод для '%s' (%s -> %s) [lang: %s]",
|
||||
field_name,
|
||||
original_value,
|
||||
translated_value,
|
||||
lang,
|
||||
)
|
||||
|
||||
# Odoo 19: use super() instead of calling _update_field_translations directly,
|
||||
# as the internal method signature may have changed between versions.
|
||||
return super().update_field_translations(field_name, translations)
|
||||
41
translation_helper/models/ir_http.py
Normal file
41
translation_helper/models/ir_http.py
Normal file
@ -0,0 +1,41 @@
|
||||
from odoo import models
|
||||
from odoo.http import request
|
||||
from odoo.tools.misc import str2bool
|
||||
|
||||
ALLOWED_TRANSLATE_MODES = ["", "1"]
|
||||
|
||||
|
||||
class IrHttp(models.AbstractModel):
|
||||
_inherit = "ir.http"
|
||||
|
||||
@classmethod
|
||||
def _handle_translate(cls):
|
||||
"""Reads ?translate= from request params and stores in session['translate'].
|
||||
Only updates session if the parameter is explicitly present in the request.
|
||||
"""
|
||||
translate = request.httprequest.args.get("translate")
|
||||
if translate is not None:
|
||||
request.session["translate"] = ",".join(
|
||||
mode
|
||||
if mode in ALLOWED_TRANSLATE_MODES
|
||||
else "1"
|
||||
if str2bool(mode, mode)
|
||||
else ""
|
||||
for mode in (translate or "").split(",")
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _pre_dispatch(cls, rule, args):
|
||||
"""OVERRIDE: calls _handle_translate after super() to store translate in session."""
|
||||
res = super()._pre_dispatch(rule, args)
|
||||
cls._handle_translate()
|
||||
return res
|
||||
|
||||
def session_info(self):
|
||||
"""OVERRIDE: adds translate to bundle_params in session_info."""
|
||||
session_info = super().session_info()
|
||||
translate = request.session.get("translate", "")
|
||||
if "bundle_params" not in session_info:
|
||||
session_info["bundle_params"] = {}
|
||||
session_info["bundle_params"]["translate"] = translate
|
||||
return session_info
|
||||
20
translation_helper/models/ir_model_fields.py
Normal file
20
translation_helper/models/ir_model_fields.py
Normal file
@ -0,0 +1,20 @@
|
||||
from odoo import api, models, tools
|
||||
|
||||
|
||||
class IrModelFields(models.Model):
|
||||
_inherit = "ir.model.fields"
|
||||
|
||||
@api.model
|
||||
@tools.ormcache("model_name")
|
||||
def get_field_string(self, model_name):
|
||||
"""
|
||||
Переопределение функции нам нужно для того, чтобы обойти ограничение платформы.
|
||||
В нашем случае при вызове оригинальной функции перевод берется из кэша системы,
|
||||
что приводи к тому, что для его появления на экране нужно перезагружать экземпляр.
|
||||
Для того, чтобы обойти это мы очищаем кэш.
|
||||
|
||||
"""
|
||||
# TODO Необходимо будет сделать сброс кэша не безусловным, а по какому либо флагу,
|
||||
# например сделать режим переводчика, по аналогии с режимом разработчика
|
||||
self.env.registry.clear_cache()
|
||||
return super().get_field_string(model_name)
|
||||
47
translation_helper/models/ir_module.py
Normal file
47
translation_helper/models/ir_module.py
Normal file
@ -0,0 +1,47 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
from odoo import api, models
|
||||
from odoo.modules import get_module_path
|
||||
from odoo.tools.translate import TranslationImporter
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
module_lname = os.path.basename(
|
||||
os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
)
|
||||
|
||||
|
||||
class Module(models.Model):
|
||||
_inherit = "ir.module.module"
|
||||
|
||||
@api.model
|
||||
def _load_module_terms(self, modules, langs, overwrite=False):
|
||||
if module_lname not in modules:
|
||||
return super()._load_module_terms(modules, langs, overwrite=overwrite)
|
||||
# load i18n files
|
||||
translation_importer = TranslationImporter(self.env.cr, verbose=False)
|
||||
for module_name in modules:
|
||||
modpath = get_module_path(module_name)
|
||||
if not modpath:
|
||||
continue
|
||||
for lang in langs:
|
||||
po_paths = []
|
||||
for subdir in ("i18n", "i18n_extra"):
|
||||
po_path = os.path.join(modpath, subdir, "%s.po" % lang)
|
||||
if os.path.exists(po_path):
|
||||
po_paths.append(po_path)
|
||||
for po_path in po_paths:
|
||||
_logger.info(
|
||||
"module %s: loading translation file %s for language %s",
|
||||
module_name,
|
||||
po_path,
|
||||
lang,
|
||||
)
|
||||
translation_importer.load_file(po_path, lang)
|
||||
if lang != "en_US" and not po_paths:
|
||||
_logger.info(
|
||||
"module %s: no translation for language %s", module_name, lang
|
||||
)
|
||||
|
||||
translation_importer.save(overwrite=overwrite, force_overwrite=overwrite)
|
||||
13
translation_helper/models/ir_qweb.py
Normal file
13
translation_helper/models/ir_qweb.py
Normal file
@ -0,0 +1,13 @@
|
||||
from odoo import models
|
||||
from odoo.http import request
|
||||
|
||||
|
||||
class IrQWeb(models.AbstractModel):
|
||||
_inherit = "ir.qweb"
|
||||
|
||||
def _prepare_environment(self, values):
|
||||
result = super()._prepare_environment(values)
|
||||
if not values.get("minimal_qcontext"):
|
||||
translate = request.session.get("translate", "") if request else ""
|
||||
values.setdefault("translate", translate)
|
||||
return result
|
||||
33
translation_helper/models/res_config_settings.py
Normal file
33
translation_helper/models/res_config_settings.py
Normal file
@ -0,0 +1,33 @@
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = "res.config.settings"
|
||||
|
||||
translation_weblate_server_address = fields.Char(
|
||||
string="Адрес",
|
||||
help="Запишите адрес вашего Weblate сервера",
|
||||
config_parameter="translation_helper.translation_weblate_server_address",
|
||||
)
|
||||
translation_weblate_server_protocol = fields.Selection(
|
||||
string="Протокол",
|
||||
help="Выберите протокол вашего Weblate сервера",
|
||||
selection=[
|
||||
("http", "HTTP"),
|
||||
("https", "HTTPS"),
|
||||
],
|
||||
config_parameter="translation_helper.translation_weblate_server_protocol",
|
||||
)
|
||||
|
||||
translation_weblate_project_alias = fields.Char(
|
||||
string="Алиас проекта",
|
||||
help="Запишите алиас вашего Weblate проекта",
|
||||
config_parameter="translation_helper.translation_weblate_project_alias",
|
||||
)
|
||||
|
||||
translation_weblate_project_language = fields.Many2one(
|
||||
string="Язык проекта",
|
||||
help="Выберите язык вашего алиаса проекта",
|
||||
comodel_name="res.lang",
|
||||
config_parameter="translation_helper.translation_weblate_project_language",
|
||||
)
|
||||
135
translation_helper/models/translation_service.py
Normal file
135
translation_helper/models/translation_service.py
Normal file
@ -0,0 +1,135 @@
|
||||
from siphashc import siphash
|
||||
|
||||
from odoo import _, api, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
DEFAULT_WEBLATE_ADDRESS = "weblate.rudoo.ru"
|
||||
DEFAULT_WEBLATE_PROTOCOL = "https"
|
||||
DEFAULT_WEBLATE_PROJECT_ALIAS = "testovyj-proekt"
|
||||
|
||||
|
||||
def raw_hash(*parts: str) -> int:
|
||||
"""Calculate checksum identifying translation."""
|
||||
if not parts:
|
||||
data = ""
|
||||
elif len(parts) == 1:
|
||||
data = parts[0]
|
||||
else:
|
||||
data = "".join(part for part in parts)
|
||||
return siphash("Weblate Sip Hash", data)
|
||||
|
||||
|
||||
def calculate_checksum(*parts: str):
|
||||
"""Calculate siphashc checksum for given strings."""
|
||||
return format(raw_hash(*parts), "016x")
|
||||
|
||||
|
||||
class TranslationHelper(models.AbstractModel):
|
||||
_name = "translation.service"
|
||||
_description = "Translation Helper"
|
||||
|
||||
@api.model
|
||||
def get_action(self, data=None):
|
||||
model_name = data.get("resModel")
|
||||
field_name = data.get("field", {}).get("name")
|
||||
# Use current user's language as the default language (Odoo 19 compatible)
|
||||
default_lang = self.env.user.lang
|
||||
query = """
|
||||
SELECT field_description
|
||||
FROM ir_model_fields
|
||||
WHERE model = %s AND name = %s
|
||||
"""
|
||||
self.env.cr.execute(query, [model_name, field_name])
|
||||
translation_field_data = self.env.cr.fetchone()
|
||||
current_user_language = self.env.context.get("lang", default_lang)
|
||||
translation_value = translation_field_data[0].get(current_user_language)
|
||||
term_to_translate = translation_field_data[0].get(default_lang)
|
||||
if not term_to_translate:
|
||||
term_to_translate = data.get("label")
|
||||
data["translation_field_data"] = translation_field_data[0]
|
||||
|
||||
field = (
|
||||
self.env["ir.model.fields"]
|
||||
.sudo()
|
||||
.search([("model", "=", model_name), ("name", "=", field_name)])
|
||||
)
|
||||
if not field:
|
||||
raise UserError(
|
||||
_("Field '%s' not found on model '%s'.") % (field_name, model_name)
|
||||
)
|
||||
# field.modules may be empty or a comma-separated string in Odoo 19
|
||||
if field.modules:
|
||||
module_names_list = field.modules.split(", ")
|
||||
else:
|
||||
module_names_list = []
|
||||
checksum = calculate_checksum(term_to_translate)
|
||||
|
||||
weblate_address = (
|
||||
self.env["ir.config_parameter"]
|
||||
.sudo()
|
||||
.get_param(
|
||||
"translation_helper.translation_weblate_server_address",
|
||||
default=DEFAULT_WEBLATE_ADDRESS,
|
||||
)
|
||||
)
|
||||
weblate_protocol = (
|
||||
self.env["ir.config_parameter"]
|
||||
.sudo()
|
||||
.get_param(
|
||||
"translation_helper.translation_weblate_server_protocol",
|
||||
default=DEFAULT_WEBLATE_PROTOCOL,
|
||||
)
|
||||
)
|
||||
weblate_project_alias = (
|
||||
self.env["ir.config_parameter"]
|
||||
.sudo()
|
||||
.get_param(
|
||||
"translation_helper.translation_weblate_project_alias",
|
||||
default=DEFAULT_WEBLATE_PROJECT_ALIAS,
|
||||
)
|
||||
)
|
||||
weblate_project_language = (
|
||||
self.env["res.lang"]
|
||||
.sudo()
|
||||
.search(
|
||||
[
|
||||
("code", "=", current_user_language),
|
||||
"|",
|
||||
("active", "=", True),
|
||||
("active", "=", False),
|
||||
]
|
||||
)
|
||||
)
|
||||
for module_name in module_names_list:
|
||||
url_for_module = f"{weblate_protocol}://{weblate_address}/translate/{weblate_project_alias}/{module_name}/{weblate_project_language.iso_code}/?checksum={checksum}"
|
||||
wizard_record = (
|
||||
self.env["translation.helper.wizard"]
|
||||
.sudo()
|
||||
.create(
|
||||
{
|
||||
"weblate_link": url_for_module,
|
||||
"term_value": term_to_translate,
|
||||
"language_id": weblate_project_language.id,
|
||||
"translation_value": translation_value,
|
||||
"modules": field.modules,
|
||||
"metadata": data,
|
||||
}
|
||||
)
|
||||
)
|
||||
window_action = {
|
||||
"name": _("Write your translation"),
|
||||
"target": "new",
|
||||
"view_mode": "form",
|
||||
"res_model": "translation.helper.wizard",
|
||||
"type": "ir.actions.act_window",
|
||||
"res_id": wizard_record.id,
|
||||
"views": [
|
||||
[
|
||||
self.env.ref(
|
||||
"translation_helper.translation_helper_wizard_form"
|
||||
).id,
|
||||
"form",
|
||||
]
|
||||
],
|
||||
}
|
||||
return window_action
|
||||
3
translation_helper/pyproject.toml
Normal file
3
translation_helper/pyproject.toml
Normal file
@ -0,0 +1,3 @@
|
||||
[build-system]
|
||||
requires = ["whool"]
|
||||
build-backend = "whool.buildapi"
|
||||
2
translation_helper/security/ir.model.access.csv
Normal file
2
translation_helper/security/ir.model.access.csv
Normal file
@ -0,0 +1,2 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_translation_helper_wizard,translation.helper.wizard,model_translation_helper_wizard,base.group_user,1,1,1,1
|
||||
|
BIN
translation_helper/static/description/icon.png
Normal file
BIN
translation_helper/static/description/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
13
translation_helper/static/src/env.js
Normal file
13
translation_helper/static/src/env.js
Normal file
@ -0,0 +1,13 @@
|
||||
/** @odoo-module **/
|
||||
import * as envModule from "@web/env";
|
||||
import {patch} from "@web/core/utils/patch";
|
||||
|
||||
const originalMakeEnv = envModule.makeEnv;
|
||||
|
||||
patch(envModule, {
|
||||
async makeEnv() {
|
||||
const env = await originalMakeEnv(...arguments);
|
||||
env.translate = odoo.translate;
|
||||
return env;
|
||||
},
|
||||
});
|
||||
54
translation_helper/static/src/field_translation.js
Normal file
54
translation_helper/static/src/field_translation.js
Normal file
@ -0,0 +1,54 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import {Dropdown} from "@web/core/dropdown/dropdown";
|
||||
import {DropdownItem} from "@web/core/dropdown/dropdown_item";
|
||||
import {FormLabel} from "@web/views/form/form_label";
|
||||
import {TranslationDialog} from "@web/views/fields/translation_dialog";
|
||||
import {patch} from "@web/core/utils/patch";
|
||||
import {browser} from "@web/core/browser/browser";
|
||||
|
||||
patch(FormLabel, {
|
||||
components: {
|
||||
...FormLabel.components,
|
||||
Dropdown,
|
||||
DropdownItem,
|
||||
},
|
||||
});
|
||||
|
||||
patch(FormLabel.prototype, {
|
||||
setup() {
|
||||
super.setup(...arguments);
|
||||
this.hasTranslation = this.getHasTranslation();
|
||||
},
|
||||
getHasTranslation() {
|
||||
return Boolean(odoo.translate);
|
||||
},
|
||||
|
||||
async onClickTranslate(attributeName) {
|
||||
const modelId = await this.env.services.orm.searchRead("ir.model", [["model", "=", this.props.record.resModel]], ["id"]);
|
||||
const fieldRecordId = await this.env.services.orm.searchRead(
|
||||
"ir.model.fields",
|
||||
[
|
||||
["model_id", "=", modelId[0].id],
|
||||
["name", "=", this.props.fieldName],
|
||||
],
|
||||
["id"]
|
||||
);
|
||||
|
||||
this.env.services.dialog.add(TranslationDialog, {
|
||||
fieldName: attributeName,
|
||||
resId: fieldRecordId[0].id,
|
||||
resModel: "ir.model.fields",
|
||||
userLanguageValue: "",
|
||||
userUserCurrentLang: true,
|
||||
isComingFromTranslationAlert: false,
|
||||
onSave: async () => {
|
||||
this.env.bus.trigger("CLEAR-CACHES");
|
||||
// Full page reload — preserves current URL including ?translate=1
|
||||
browser.location.reload();
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
FormLabel.template = "translation_link_generation.FormLabel";
|
||||
20
translation_helper/static/src/field_translation.xml
Normal file
20
translation_helper/static/src/field_translation.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="translation_link_generation.FormLabel" t-inherit="web.FormLabel">
|
||||
<xpath expr="//sup" position="after">
|
||||
<Dropdown t-if="hasTranslation">
|
||||
<button type="button" class="btn btn-link btn-primary py-0 px-1">
|
||||
<i class="fa fa-language" />
|
||||
</button>
|
||||
<t t-set-slot="content">
|
||||
<t t-if="tooltipHelp">
|
||||
<DropdownItem onSelected="() => this.onClickTranslate('help')">Help</DropdownItem>
|
||||
</t>
|
||||
<DropdownItem onSelected="() => this.onClickTranslate('field_description')">String</DropdownItem>
|
||||
</t>
|
||||
</Dropdown>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
96
translation_helper/static/src/fields/translation_dialog.js
Normal file
96
translation_helper/static/src/fields/translation_dialog.js
Normal file
@ -0,0 +1,96 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import {_t, loadLanguages} from "@web/core/l10n/translation";
|
||||
import {jsToPyLocale} from "@web/core/l10n/utils";
|
||||
import {TranslationDialog} from "@web/views/fields/translation_dialog";
|
||||
import {user} from "@web/core/user";
|
||||
import {onWillStart} from "@odoo/owl";
|
||||
import {patch} from "@web/core/utils/patch";
|
||||
import {useService} from "@web/core/utils/hooks";
|
||||
|
||||
// Add userUserCurrentLang prop that doesn't exist in Odoo 19's TranslationDialog
|
||||
TranslationDialog.props = {
|
||||
...TranslationDialog.props,
|
||||
userUserCurrentLang: {type: Boolean, optional: true},
|
||||
};
|
||||
|
||||
patch(TranslationDialog.prototype, {
|
||||
setup() {
|
||||
this.title = _t("Translate: %s", this.props.fieldName);
|
||||
this.user = user;
|
||||
this.orm = useService("orm");
|
||||
this.terms = [];
|
||||
this.updatedTerms = {};
|
||||
this.translateIsCallable = false;
|
||||
|
||||
onWillStart(async () => {
|
||||
const allLanguages = await loadLanguages(this.orm);
|
||||
const languages = this.props.userUserCurrentLang
|
||||
? allLanguages.filter((l) => l[0] === jsToPyLocale(user.lang))
|
||||
: allLanguages;
|
||||
|
||||
const [translations, context] = await this.loadTranslations(languages);
|
||||
this.translateIsCallable = Boolean(context.translation_show_source);
|
||||
|
||||
let id = 1;
|
||||
translations.forEach((t) => (t.id = id++));
|
||||
this.props.isText = context.translation_type === "text";
|
||||
this.props.showSource = context.translation_show_source;
|
||||
|
||||
this.terms = translations.map((term) => {
|
||||
const relatedLanguage = languages.find((l) => l[0] === term.lang);
|
||||
const termInfo = {
|
||||
...term,
|
||||
langName: relatedLanguage ? relatedLanguage[1] : term.lang,
|
||||
value: term.value || "",
|
||||
};
|
||||
if (
|
||||
term.lang === jsToPyLocale(user.lang) &&
|
||||
!this.props.showSource &&
|
||||
!this.props.isComingFromTranslationAlert &&
|
||||
this.props.userLanguageValue
|
||||
) {
|
||||
this.updatedTerms[term.id] = this.props.userLanguageValue;
|
||||
termInfo.value = this.props.userLanguageValue;
|
||||
}
|
||||
return termInfo;
|
||||
});
|
||||
this.terms.sort((a, b) => a.langName.localeCompare(b.langName));
|
||||
});
|
||||
},
|
||||
|
||||
async loadTranslations(languages) {
|
||||
const langs = languages.map((l) => l[0]);
|
||||
return this.orm.call(
|
||||
this.props.resModel,
|
||||
"get_field_translations",
|
||||
[[this.props.resId], this.props.fieldName, langs]
|
||||
);
|
||||
},
|
||||
|
||||
async onSave() {
|
||||
const translations = {};
|
||||
this.terms.forEach((term) => {
|
||||
const updatedTermValue = this.updatedTerms[term.id];
|
||||
if (term.id in this.updatedTerms && term.value !== updatedTermValue) {
|
||||
if (this.translateIsCallable) {
|
||||
if (!translations[term.lang]) {
|
||||
translations[term.lang] = {};
|
||||
}
|
||||
const oldTermValue = term.value || term.source;
|
||||
translations[term.lang][oldTermValue] = updatedTermValue || term.source;
|
||||
translations[term.lang].source = term.source;
|
||||
} else {
|
||||
translations[term.lang] = updatedTermValue || false;
|
||||
}
|
||||
}
|
||||
});
|
||||
await this.orm.call(
|
||||
this.props.resModel,
|
||||
"update_field_translations",
|
||||
[[this.props.resId], this.props.fieldName, translations]
|
||||
);
|
||||
await this.props.onSave();
|
||||
this.props.close();
|
||||
},
|
||||
});
|
||||
1
translation_helper/static/src/images/translate.svg
Normal file
1
translation_helper/static/src/images/translate.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m3 5h12m-6-2v2m1.0482 9.5c-1.52737-1.5822-2.76747-3.4435-3.63633-5.5m6.08813 9h7m-8.5 3 5-10 5 10m-8.2489-16c-.968 5.7702-4.68141 10.6095-9.7511 13.129" stroke="#4a5568" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/></svg>
|
||||
|
After Width: | Height: | Size: 345 B |
53
translation_helper/static/src/menus/dropdown.js
Normal file
53
translation_helper/static/src/menus/dropdown.js
Normal file
@ -0,0 +1,53 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
// In Odoo 19, Dropdown uses an inline xml`` template (no QWeb template named web.Dropdown).
|
||||
// MenuTranslationButton is injected into DropdownItem via JS patch + XML template inheritance
|
||||
// of web.DropdownItem (NOT web.NavBar.DropdownItem which is unused as a component template).
|
||||
// NavBar is also patched to support translate buttons on section buttons with children.
|
||||
|
||||
import {DropdownItem} from "@web/core/dropdown/dropdown_item";
|
||||
import {NavBar} from "@web/webclient/navbar/navbar";
|
||||
import {MenuTranslationButton} from "./menu_translation_button";
|
||||
import {patch} from "@web/core/utils/patch";
|
||||
|
||||
// Patch DropdownItem — for leaf menu items (no children)
|
||||
patch(DropdownItem, {
|
||||
components: {
|
||||
...DropdownItem.components,
|
||||
MenuTranslationButton,
|
||||
},
|
||||
});
|
||||
|
||||
patch(DropdownItem.prototype, {
|
||||
setup() {
|
||||
super.setup(...arguments);
|
||||
this.hasTranslation = this.getHasTranslation();
|
||||
},
|
||||
|
||||
onClick(ev) {
|
||||
if (ev.target.classList.contains("translate-middle-y")) {
|
||||
return;
|
||||
}
|
||||
super.onClick(ev);
|
||||
},
|
||||
|
||||
getHasTranslation() {
|
||||
return Boolean(odoo.translate);
|
||||
},
|
||||
});
|
||||
|
||||
// Patch NavBar — for section buttons with children (Dropdown-based)
|
||||
patch(NavBar, {
|
||||
components: {
|
||||
...NavBar.components,
|
||||
MenuTranslationButton,
|
||||
},
|
||||
});
|
||||
|
||||
patch(NavBar.prototype, {
|
||||
setup() {
|
||||
super.setup(...arguments);
|
||||
// expose to template via __translate__ variable
|
||||
this.__translate__ = Boolean(odoo.translate);
|
||||
},
|
||||
});
|
||||
18
translation_helper/static/src/menus/dropdown.xml
Normal file
18
translation_helper/static/src/menus/dropdown.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<!-- Patch SectionsMenu: add translate button inside Dropdown-based section buttons (items with children) -->
|
||||
<t t-name="translation_helper.NavBar.SectionsMenu" t-inherit="web.NavBar.SectionsMenu" t-inherit-mode="extension">
|
||||
<xpath expr="//button[@t-att-data-menu-xmlid]/span" position="after">
|
||||
<MenuTranslationButton t-if="__translate__" menuXmlId="section.xmlid" />
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
<!-- Patch MenuSlot: add translate button after group headers (items with children inside dropdown) -->
|
||||
<t t-name="translation_helper.NavBar.SectionsMenu.Dropdown.MenuSlot" t-inherit="web.NavBar.SectionsMenu.Dropdown.MenuSlot" t-inherit-mode="extension">
|
||||
<xpath expr="//div[hasclass('dropdown-menu_group')]" position="after">
|
||||
<MenuTranslationButton t-if="__translate__ and item.xmlid" menuXmlId="item.xmlid" />
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
10
translation_helper/static/src/menus/dropdown_item.xml
Normal file
10
translation_helper/static/src/menus/dropdown_item.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="translation_helper.DropdownItem" t-inherit="web.DropdownItem" t-inherit-mode="extension">
|
||||
<xpath expr="//t[@t-slot='default']" position="after">
|
||||
<MenuTranslationButton t-if="hasTranslation and props.attrs and props.attrs['data-menu-xmlid']" menuXmlId="props.attrs['data-menu-xmlid']" />
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@ -0,0 +1,92 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import {Component, useState} from "@odoo/owl";
|
||||
import {useOwnedDialogs, useService} from "@web/core/utils/hooks";
|
||||
import {TranslationDialog} from "@web/views/fields/translation_dialog";
|
||||
|
||||
/**
|
||||
* @param {Object} env - OWL environment from the component (this.env)
|
||||
* @returns {Function} openTranslationDialog
|
||||
*/
|
||||
export function useTranslationDialog(env) {
|
||||
const addDialog = useOwnedDialogs();
|
||||
|
||||
async function openTranslationDialog({name, id}) {
|
||||
addDialog(TranslationDialog, {
|
||||
fieldName: "name",
|
||||
resId: id,
|
||||
resModel: "ir.ui.menu",
|
||||
userLanguageValue: name || "",
|
||||
isComingFromTranslationAlert: false,
|
||||
userUserCurrentLang: true,
|
||||
onSave: async () => {
|
||||
env.bus.trigger("CLEAR-CACHES");
|
||||
await env.services.action.doAction({
|
||||
type: "ir.actions.client",
|
||||
tag: "reload",
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return openTranslationDialog;
|
||||
}
|
||||
|
||||
export class MenuTranslationButton extends Component {
|
||||
setup() {
|
||||
this.orm = useService("orm");
|
||||
this.state = useState({record: {}});
|
||||
this.translationDialog = useTranslationDialog(this.env);
|
||||
}
|
||||
|
||||
async onClick() {
|
||||
const xmlId = this.props.menuXmlId;
|
||||
const [module, name] = xmlId.split(".");
|
||||
if (!module || !name) {
|
||||
console.error("Неверный формат menuXmlId");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const [menuIdRecord] = await this.orm.searchRead(
|
||||
"ir.model.data",
|
||||
[
|
||||
["model", "=", "ir.ui.menu"],
|
||||
["name", "=", name],
|
||||
["module", "=", module],
|
||||
],
|
||||
["res_id"]
|
||||
);
|
||||
|
||||
if (!menuIdRecord) {
|
||||
console.error("Меню не найдено по XML ID");
|
||||
return;
|
||||
}
|
||||
|
||||
const menuId = menuIdRecord.res_id;
|
||||
const [menuRecord] = await this.orm.searchRead("ir.ui.menu", [["id", "=", menuId]], ["id", "name", "action"]);
|
||||
|
||||
if (!menuRecord) {
|
||||
console.error("Запись меню не найдена");
|
||||
return;
|
||||
}
|
||||
|
||||
this.state.record = menuRecord;
|
||||
|
||||
this.translationDialog({
|
||||
name: menuRecord.name,
|
||||
id: menuRecord.id,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Ошибка при получении меню:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MenuTranslationButton.template = "translation_helper.MenuTranslationButton";
|
||||
MenuTranslationButton.props = {
|
||||
menuXmlId: {
|
||||
type: String,
|
||||
optional: false,
|
||||
},
|
||||
};
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="translation_helper.MenuTranslationButton">
|
||||
<i class="fa fa-language translate-middle-y" style="margin-left: 5px;" aria-hidden="true" t-on-click.prevent.stop="onClick" />
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
14
translation_helper/static/src/redefine_view_hook.js
Normal file
14
translation_helper/static/src/redefine_view_hook.js
Normal file
@ -0,0 +1,14 @@
|
||||
/** @odoo-module **/
|
||||
import * as viewHookModule from "@web/views/view_hook";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { useComponent } from "@odoo/owl";
|
||||
import { useTranslateCategory } from "@translation_helper/translate/translate_context";
|
||||
|
||||
const originalUseSetupView = viewHookModule.useSetupView;
|
||||
|
||||
patch(viewHookModule, {
|
||||
useSetupView(params) {
|
||||
useTranslateCategory("view", { component: useComponent() });
|
||||
return originalUseSetupView(params);
|
||||
},
|
||||
});
|
||||
12
translation_helper/static/src/response_from_wizard.js
Normal file
12
translation_helper/static/src/response_from_wizard.js
Normal file
@ -0,0 +1,12 @@
|
||||
/** @odoo-module **/
|
||||
import {registry} from "@web/core/registry";
|
||||
|
||||
async function handleResponseFromWizard(env) {
|
||||
const actionService = env.services.action;
|
||||
env.bus.trigger("CLEAR-CACHES");
|
||||
await actionService.doAction({
|
||||
type: "ir.actions.client",
|
||||
tag: "soft_reload",
|
||||
});
|
||||
}
|
||||
registry.category("actions").add("translation_helper.response_from_wizard", handleResponseFromWizard);
|
||||
@ -0,0 +1,16 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import {ResConfigDevTool} from "@web/webclient/settings_form_view/widgets/res_config_dev_tool";
|
||||
import {patch} from "@web/core/utils/patch";
|
||||
import {router} from "@web/core/browser/router";
|
||||
|
||||
patch(ResConfigDevTool.prototype, {
|
||||
setup() {
|
||||
super.setup(...arguments);
|
||||
this.isTranslate = Boolean(odoo.translate);
|
||||
},
|
||||
|
||||
activateTranslate(value) {
|
||||
router.pushState({translate: value}, {reload: true});
|
||||
},
|
||||
});
|
||||
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<templates xml:space="preserve">
|
||||
<t t-inherit="res_config_dev_tool" t-inherit-mode="extension">
|
||||
<xpath expr="//div[@id='developer_tool']" position="after">
|
||||
<div id="translator_tool">
|
||||
<SettingsBlock title.translate="Translator Tools">
|
||||
<Setting addLabel="false">
|
||||
<a t-if="!isTranslate" class="d-block" t-on-click.prevent="() => this.activateTranslate(1)" href="#">Activate the translate mode</a>
|
||||
<a t-if="isTranslate" class="d-block" t-on-click.prevent="() => this.activateTranslate(0)" href="#">Deactivate the translate mode</a>
|
||||
</Setting>
|
||||
</SettingsBlock>
|
||||
</div>
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
4
translation_helper/static/src/translate/__init__.js
Normal file
4
translation_helper/static/src/translate/__init__.js
Normal file
@ -0,0 +1,4 @@
|
||||
/** @odoo-module **/
|
||||
export * from "./translate_context";
|
||||
export { TranslateMenu } from "./translate_menu";
|
||||
export { TranslateMenuItems } from "./translate_menu_items";
|
||||
6
translation_helper/static/src/translate/debug_menu.scss
Normal file
6
translation_helper/static/src/translate/debug_menu.scss
Normal file
@ -0,0 +1,6 @@
|
||||
.o_dialog {
|
||||
.o_translate_manager .dropdown-toggle {
|
||||
padding: 0 4px;
|
||||
margin: 2px 10px 2px 0;
|
||||
}
|
||||
}
|
||||
57
translation_helper/static/src/translate/translate_contex.js
Normal file
57
translation_helper/static/src/translate/translate_contex.js
Normal file
@ -0,0 +1,57 @@
|
||||
/** @odoo-module **/
|
||||
import { useEffect, useEnv, useSubEnv } from "@odoo/owl";
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
const translateRegistry = registry.category("translate");
|
||||
const translateContextSymbol = Symbol("translateContext");
|
||||
|
||||
class TranslateContext {
|
||||
constructor(env, defaultCategories = []) {
|
||||
this.env = env;
|
||||
this.categories = new Map(defaultCategories.map(cat => [cat, [{}]]));
|
||||
}
|
||||
|
||||
activateCategory(category, context) {
|
||||
const contexts = this.categories.get(category) || new Set();
|
||||
contexts.add(context);
|
||||
this.categories.set(category, contexts);
|
||||
return () => {
|
||||
contexts.delete(context);
|
||||
if (!contexts.size) this.categories.delete(category);
|
||||
};
|
||||
}
|
||||
|
||||
async getItems() {
|
||||
return [...this.categories.entries()]
|
||||
.flatMap(([category, contexts]) =>
|
||||
translateRegistry
|
||||
.category(category)
|
||||
.getAll()
|
||||
.map(factory => factory({ env: this.env, ...Object.assign({}, ...contexts) }))
|
||||
)
|
||||
.filter(Boolean)
|
||||
.sort((a, b) => (a.sequence || 1000) - (b.sequence || 1000));
|
||||
}
|
||||
}
|
||||
|
||||
export function createTranslateContext(env, { categories = [] } = {}) {
|
||||
return { [translateContextSymbol]: new TranslateContext(env, categories) };
|
||||
}
|
||||
|
||||
export function useOwnTranslateContext({ categories = [] } = {}) {
|
||||
useSubEnv(createTranslateContext(useEnv(), { categories }));
|
||||
}
|
||||
|
||||
export function useEnvTranslateContext() {
|
||||
const translateContext = useEnv()[translateContextSymbol];
|
||||
if (!translateContext) throw new Error("No translate context in environment");
|
||||
return translateContext;
|
||||
}
|
||||
|
||||
export function useTranslateCategory(category, context = {}) {
|
||||
const env = useEnv();
|
||||
if (env.translate) {
|
||||
const translateContext = useEnvTranslateContext();
|
||||
useEffect(() => translateContext.activateCategory(category, context), () => []);
|
||||
}
|
||||
}
|
||||
89
translation_helper/static/src/translate/translate_context.js
Normal file
89
translation_helper/static/src/translate/translate_context.js
Normal file
@ -0,0 +1,89 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import {useEffect, useEnv, useSubEnv} from "@odoo/owl";
|
||||
import {memoize} from "@web/core/utils/functions";
|
||||
import {registry} from "@web/core/registry";
|
||||
|
||||
const translateRegistry = registry.category("translate");
|
||||
|
||||
const getAccessRights = memoize(async function getAccessRights(orm) {
|
||||
const rightsToCheck = {
|
||||
"ir.ui.view": "write",
|
||||
"ir.rule": "read",
|
||||
"ir.model.access": "read",
|
||||
};
|
||||
const proms = Object.entries(rightsToCheck).map(([model, operation]) => {
|
||||
return orm.call(model, "check_access_rights", [], {
|
||||
operation,
|
||||
raise_exception: false,
|
||||
});
|
||||
});
|
||||
const [canEditView, canSeeRecordRules, canSeeModelAccess] = await Promise.all(proms);
|
||||
const accessRights = {canEditView, canSeeRecordRules, canSeeModelAccess};
|
||||
return accessRights;
|
||||
});
|
||||
|
||||
class TranslateContext {
|
||||
constructor(env, defaultCategories) {
|
||||
this.orm = env.services.orm;
|
||||
this.categories = new Map(defaultCategories.map((cat) => [cat, new Set()]));
|
||||
}
|
||||
|
||||
activateCategory(category, context) {
|
||||
const contexts = this.categories.get(category) || new Set();
|
||||
contexts.add(context);
|
||||
this.categories.set(category, contexts);
|
||||
|
||||
return () => {
|
||||
contexts.delete(context);
|
||||
if (contexts.size === 0) {
|
||||
this.categories.delete(category);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async getItems(env) {
|
||||
const accessRights = await getAccessRights(this.orm);
|
||||
return [...this.categories.entries()]
|
||||
.flatMap(([category, contexts]) => {
|
||||
return translateRegistry
|
||||
.category(category)
|
||||
.getAll()
|
||||
.map((factory) => factory(Object.assign({env, accessRights}, ...contexts)));
|
||||
})
|
||||
.filter(Boolean)
|
||||
.sort((x, y) => {
|
||||
const xSeq = x.sequence || 1000;
|
||||
const ySeq = y.sequence || 1000;
|
||||
return xSeq - ySeq;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const translateContextSymbol = Symbol("translateContext");
|
||||
export function createTranslateContext(env, {categories = []} = {}) {
|
||||
return {[translateContextSymbol]: new TranslateContext(env, categories)};
|
||||
}
|
||||
|
||||
export function useOwnTranslateContext({categories = []} = {}) {
|
||||
useSubEnv(createTranslateContext(useEnv(), {categories}));
|
||||
}
|
||||
|
||||
export function useEnvTranslateContext() {
|
||||
const translateContext = useEnv()[translateContextSymbol];
|
||||
if (!translateContext) {
|
||||
throw new Error("There is no translate context available in the current environment.");
|
||||
}
|
||||
return translateContext;
|
||||
}
|
||||
|
||||
export function useTranslateCategory(category, context = {}) {
|
||||
const env = useEnv();
|
||||
if (env.translate) {
|
||||
const translateContext = useEnvTranslateContext();
|
||||
useEffect(
|
||||
() => translateContext.activateCategory(category, context),
|
||||
() => []
|
||||
);
|
||||
}
|
||||
}
|
||||
82
translation_helper/static/src/translate/translate_items.js
Normal file
82
translation_helper/static/src/translate/translate_items.js
Normal file
@ -0,0 +1,82 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import {Component} from "@odoo/owl";
|
||||
import {Dialog} from "@web/core/dialog/dialog";
|
||||
import {_t} from "@web/core/l10n/translation";
|
||||
import {editModelTranslate} from "@translation_helper/translate/translate_utils";
|
||||
import {registry} from "@web/core/registry";
|
||||
|
||||
const translateRegistry = registry.category("translate");
|
||||
|
||||
function viewSeparator() {
|
||||
return {type: "separator", sequence: 300};
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Get view
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
class GetViewDialog extends Component {
|
||||
setup() {
|
||||
this.title = _t("Get View");
|
||||
}
|
||||
}
|
||||
GetViewDialog.template = "web.TranslateMenu.GetViewDialog";
|
||||
GetViewDialog.components = {Dialog};
|
||||
GetViewDialog.props = {
|
||||
arch: {type: Element},
|
||||
close: {type: Function},
|
||||
};
|
||||
|
||||
// // ------------------------------------------------------------------------------
|
||||
// // Edit View
|
||||
// // ------------------------------------------------------------------------------
|
||||
|
||||
export function editView({accessRights, component, env}) {
|
||||
if (!accessRights.canEditView) {
|
||||
return null;
|
||||
}
|
||||
const {viewId, viewType: type} = component.env.config || {};
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
const displayName = type[0].toUpperCase() + type.slice(1);
|
||||
const description = _t("Translate View: ") + displayName;
|
||||
return {
|
||||
type: "item",
|
||||
description,
|
||||
callback: () => {
|
||||
editModelTranslate(env, description, "ir.ui.view", viewId, "soft_reload");
|
||||
},
|
||||
sequence: 350,
|
||||
};
|
||||
}
|
||||
|
||||
translateRegistry.category("view").add("editView", editView);
|
||||
|
||||
translateRegistry.category("view").add("viewSeparator", viewSeparator);
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Edit SearchView
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
export function editSearchView({accessRights, component, env}) {
|
||||
if (!accessRights.canEditView) {
|
||||
return null;
|
||||
}
|
||||
const {searchViewId} = component.props.info || {};
|
||||
if (searchViewId === undefined) {
|
||||
return null;
|
||||
}
|
||||
const description = _t("Translate SearchView");
|
||||
return {
|
||||
type: "item",
|
||||
description,
|
||||
callback: () => {
|
||||
editModelTranslate(env, description, "ir.ui.view", searchViewId, "reload");
|
||||
},
|
||||
sequence: 350,
|
||||
};
|
||||
}
|
||||
|
||||
translateRegistry.category("view").add("editSearchView", editSearchView);
|
||||
61
translation_helper/static/src/translate/translate_menu.js
Normal file
61
translation_helper/static/src/translate/translate_menu.js
Normal file
@ -0,0 +1,61 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import {Dropdown} from "@web/core/dropdown/dropdown";
|
||||
import {DropdownItem} from "@web/core/dropdown/dropdown_item";
|
||||
import {TranslateMenuBasic} from "@translation_helper/translate/translate_menu_basic";
|
||||
import {_t} from "@web/core/l10n/translation";
|
||||
import {useCommand} from "@web/core/commands/command_hook";
|
||||
import {useEnvTranslateContext} from "./translate_context";
|
||||
import {useService} from "@web/core/utils/hooks";
|
||||
|
||||
export class TranslateMenu extends TranslateMenuBasic {
|
||||
setup() {
|
||||
super.setup();
|
||||
const translateContext = useEnvTranslateContext();
|
||||
this.command = useService("command");
|
||||
useCommand(
|
||||
_t("Translate tools..."),
|
||||
async () => {
|
||||
const items = await translateContext.getItems(this.env);
|
||||
let index = 0;
|
||||
const defaultCategories = items.filter((item) => item.type === "separator").map(() => (index += 1));
|
||||
const provider = {
|
||||
async provide() {
|
||||
const categories = [...defaultCategories];
|
||||
let category = categories.shift();
|
||||
const result = [];
|
||||
items.forEach((item) => {
|
||||
if (item.type === "item") {
|
||||
result.push({
|
||||
name: item.description.toString(),
|
||||
action: item.callback,
|
||||
category,
|
||||
});
|
||||
} else if (item.type === "separator") {
|
||||
category = categories.shift();
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
};
|
||||
const configByNamespace = {
|
||||
default: {
|
||||
categories: defaultCategories,
|
||||
emptyMessage: _t("No translate command found"),
|
||||
placeholder: _t("Choose a translate command..."),
|
||||
},
|
||||
};
|
||||
const commandPaletteConfig = {
|
||||
configByNamespace,
|
||||
providers: [provider],
|
||||
};
|
||||
return commandPaletteConfig;
|
||||
},
|
||||
{
|
||||
category: "translate",
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
TranslateMenu.components = {Dropdown, DropdownItem};
|
||||
TranslateMenu.props = {};
|
||||
26
translation_helper/static/src/translate/translate_menu.xml
Normal file
26
translation_helper/static/src/translate/translate_menu.xml
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="web.TranslateMenu">
|
||||
<Dropdown
|
||||
class="'o_translate_manager'"
|
||||
beforeOpen="getElements"
|
||||
position="'bottom-end'"
|
||||
menuClass="env.inDialog ? 'btn btn-link' : ''"
|
||||
>
|
||||
<button type="button" class="o-dropdown--narrow btn btn-link">
|
||||
<i class="fa fa-language" role="img" aria-label="Open translate tools" />
|
||||
</button>
|
||||
<t t-set-slot="content">
|
||||
<t t-foreach="elements" t-as="element" t-key="element_index">
|
||||
<DropdownItem t-if="element.type == 'item'" onSelected="element.callback" t-att-attrs="element.href ? {href: element.href} : undefined">
|
||||
<t t-esc="element.description" />
|
||||
</DropdownItem>
|
||||
<div t-if="element.type == 'separator'" role="separator" class="dropdown-divider" />
|
||||
<t t-if="element.type == 'component'" t-component="element.Component" t-props="element.props" />
|
||||
</t>
|
||||
</t>
|
||||
</Dropdown>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@ -0,0 +1,21 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import {Component} from "@odoo/owl";
|
||||
import {Dropdown} from "@web/core/dropdown/dropdown";
|
||||
import {DropdownItem} from "@web/core/dropdown/dropdown_item";
|
||||
import {useEnvTranslateContext} from "./translate_context";
|
||||
|
||||
export class TranslateMenuBasic extends Component {
|
||||
setup() {
|
||||
const translateContext = useEnvTranslateContext();
|
||||
// Needs to be bound to this for use in template
|
||||
this.getElements = async () => {
|
||||
this.elements = await translateContext.getItems(this.env);
|
||||
};
|
||||
}
|
||||
}
|
||||
TranslateMenuBasic.components = {
|
||||
Dropdown,
|
||||
DropdownItem,
|
||||
};
|
||||
TranslateMenuBasic.template = "web.TranslateMenu";
|
||||
@ -0,0 +1,21 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import {_t} from "@web/core/l10n/translation";
|
||||
import {browser} from "@web/core/browser/browser";
|
||||
import {registry} from "@web/core/registry";
|
||||
import {routeToUrl} from "@web/core/browser/router";
|
||||
|
||||
function leaveTranslateMode({env}) {
|
||||
return {
|
||||
type: "item",
|
||||
description: _t("Leave the Translate Tools"),
|
||||
callback: () => {
|
||||
const route = env.services.router.current;
|
||||
route.search.translate = "";
|
||||
browser.location.href = browser.location.origin + routeToUrl(route);
|
||||
},
|
||||
sequence: 450,
|
||||
};
|
||||
}
|
||||
|
||||
registry.category("translate").category("default").add("leaveTranslateMode", leaveTranslateMode);
|
||||
119
translation_helper/static/src/translate/translate_menu_items.xml
Normal file
119
translation_helper/static/src/translate/translate_menu_items.xml
Normal file
@ -0,0 +1,119 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="web.TranslateMenu.SetDefaultDialog">
|
||||
<Dialog title="title">
|
||||
<table style="width: 100%">
|
||||
<tr>
|
||||
<td>
|
||||
<label for="formview_default_fields" class="oe_label oe_align_right">
|
||||
Default:
|
||||
</label>
|
||||
</td>
|
||||
<td class="oe_form_required">
|
||||
<select id="formview_default_fields" class="o_input" t-model="state.fieldToSet">
|
||||
<option value="" />
|
||||
<option t-foreach="defaultFields" t-as="field" t-att-value="field.name" t-key="field.name">
|
||||
<t t-esc="field.string" /> = <t t-esc="field.displayed" />
|
||||
</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr t-if="conditions.length">
|
||||
<td>
|
||||
<label for="formview_default_conditions" class="oe_label oe_align_right">
|
||||
Condition:
|
||||
</label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="formview_default_conditions" class="o_input" t-model="state.condition">
|
||||
<option value="" />
|
||||
<option t-foreach="conditions" t-as="cond" t-att-value="cond.name + '=' + cond.value" t-key="cond.name">
|
||||
<t t-esc="cond.string" />=<t t-esc="cond.displayed" />
|
||||
</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<input type="radio" id="formview_default_self" value="self" name="scope" t-model="state.scope" />
|
||||
<label for="formview_default_self" class="oe_label" style="display: inline;">
|
||||
Only you
|
||||
</label>
|
||||
<br />
|
||||
<input type="radio" id="formview_default_all" value="all" name="scope" t-model="state.scope" />
|
||||
<label for="formview_default_all" class="oe_label" style="display: inline;">
|
||||
All users
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<t t-set-slot="footer">
|
||||
<button class="btn btn-secondary" t-on-click="props.close">Close</button>
|
||||
<button class="btn btn-secondary" t-on-click="saveDefault">Save default</button>
|
||||
</t>
|
||||
</Dialog>
|
||||
</t>
|
||||
|
||||
<t t-name="web.TranslateMenu.GetMetadataDialog">
|
||||
<Dialog title="title">
|
||||
<table class="table table-sm table-striped">
|
||||
<tr>
|
||||
<th>ID:</th>
|
||||
<td><t t-esc="state.id" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>XML ID:</th>
|
||||
<td>
|
||||
<t t-if='state.xmlids.length > 1'>
|
||||
<t t-foreach="state.xmlids" t-as="imd" t-key="imd['xmlid']">
|
||||
<div
|
||||
t-att-class='"p-0 " + (imd["xmlid"] === state.xmlid ? "fw-bolder " : "") + (imd["noupdate"] === true ? "fst-italic " : "")'
|
||||
t-esc="imd['xmlid']"
|
||||
/>
|
||||
</t>
|
||||
</t>
|
||||
<t t-elif="state.xmlid" t-esc="state.xmlid" />
|
||||
<t t-else="">
|
||||
/ <a t-on-click="onClickCreateXmlid"> (create)</a>
|
||||
</t>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>No Update:</th>
|
||||
<td>
|
||||
<t t-esc="state.noupdate" />
|
||||
<t t-if="state.xmlid">
|
||||
<a t-on-click="toggleNoupdate"> (change)</a>
|
||||
</t>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Creation User:</th>
|
||||
<td><t t-esc="state.creator" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Creation Date:</th>
|
||||
<td><t t-esc="state.createDate" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Latest Modification by:</th>
|
||||
<td><t t-esc="state.lastModifiedBy" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Latest Modification Date:</th>
|
||||
<td><t t-esc="state.writeDate" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
</Dialog>
|
||||
</t>
|
||||
|
||||
<t t-name="web.TranslateMenu.GetViewDialog">
|
||||
<Dialog title="title">
|
||||
<pre t-esc="props.arch.outerHTML" />
|
||||
<t t-set-slot="footer">
|
||||
<button class="btn btn-primary o-default-button" t-on-click="() => props.close()">Close</button>
|
||||
</t>
|
||||
</Dialog>
|
||||
</t>
|
||||
</templates>
|
||||
21
translation_helper/static/src/translate/translate_utils.js
Normal file
21
translation_helper/static/src/translate/translate_utils.js
Normal file
@ -0,0 +1,21 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import {TranslationDialog} from "@web/views/fields/translation_dialog";
|
||||
|
||||
export function editModelTranslate(env, title, model, id, reloadType) {
|
||||
env.services.dialog.add(TranslationDialog, {
|
||||
fieldName: "arch_db",
|
||||
resId: id,
|
||||
resModel: model,
|
||||
userLanguageValue: env.services.user.lang,
|
||||
userUserCurrentLang: true,
|
||||
isComingFromTranslationAlert: false,
|
||||
onSave: async () => {
|
||||
env.bus.trigger("CLEAR-CACHES");
|
||||
await env.services.action.doAction({
|
||||
type: "ir.actions.client",
|
||||
tag: reloadType,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
23
translation_helper/static/src/webclient.js
Normal file
23
translation_helper/static/src/webclient.js
Normal file
@ -0,0 +1,23 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import {TranslateMenu} from "@translation_helper/translate/translate_menu";
|
||||
import {WebClient} from "@web/webclient/webclient";
|
||||
import {patch} from "@web/core/utils/patch";
|
||||
import {registry} from "@web/core/registry";
|
||||
import {useOwnTranslateContext} from "@translation_helper/translate/translate_context";
|
||||
|
||||
patch(WebClient.prototype, {
|
||||
setup() {
|
||||
super.setup();
|
||||
useOwnTranslateContext({categories: ["default"]});
|
||||
if (this.env.translate) {
|
||||
registry.category("systray").add(
|
||||
"web.translate_mode_menu",
|
||||
{
|
||||
Component: TranslateMenu,
|
||||
},
|
||||
{sequence: 200}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
19652
translation_helper/translations/ru/account.po
Normal file
19652
translation_helper/translations/ru/account.po
Normal file
File diff suppressed because it is too large
Load Diff
682
translation_helper/translations/ru/base_setup.po
Normal file
682
translation_helper/translations/ru/base_setup.po
Normal file
@ -0,0 +1,682 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 17.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-01-25 18:36+0000\n"
|
||||
"PO-Revision-Date: 2025-12-03 08:31+0000\n"
|
||||
"Last-Translator: \"Anastasiia Koroleva (koan)\" <koan@odoo.com>\n"
|
||||
"Language-Team: Russian <https://translate.odoo.com/projects/odoo-19/base_setup/ru/>\n"
|
||||
"Language: ru\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
|
||||
"X-Generator: Weblate 5.14.3\n"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "<span class=\"fa fa-lg fa-users\" aria-label=\"Number of active users\"/>"
|
||||
msgstr "<span class=\"fa fa-lg fa-users\" aria-label=\"Number of active users\"/>"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid ""
|
||||
"<span class=\"o_form_label\" invisible=\"active_user_count > 1\">\n"
|
||||
" Active User\n"
|
||||
" </span>\n"
|
||||
" <span class=\"o_form_label\" invisible=\"active_user_count <= 1\">\n"
|
||||
" Active Users\n"
|
||||
" </span>"
|
||||
msgstr ""
|
||||
"<span class=\"o_form_label\" invisible=\"active_user_count > 1\">\n"
|
||||
" Активный пользователь\n"
|
||||
" </span>\n"
|
||||
" <span class=\"o_form_label\" invisible=\"active_user_count <= 1\">\n"
|
||||
" Активные пользователи\n"
|
||||
" </span>"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid ""
|
||||
"<span class=\"o_form_label\" invisible=\"company_count > 1\">\n"
|
||||
" Company\n"
|
||||
" </span>\n"
|
||||
" <span class=\"o_form_label\" invisible=\"company_count <= 1\">\n"
|
||||
" Companies\n"
|
||||
" </span>\n"
|
||||
" <br/>"
|
||||
msgstr ""
|
||||
"<span class=\"o_form_label\" invisible=\"company_count > 1\">\n"
|
||||
" Компания\n"
|
||||
" </span>\n"
|
||||
" <span class=\"o_form_label\" invisible=\"company_count <= 1\">\n"
|
||||
" Компании\n"
|
||||
" </span>\n"
|
||||
" <br/>"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid ""
|
||||
"<span class=\"o_form_label\" invisible=\"language_count > 1\">\n"
|
||||
" Language\n"
|
||||
" </span>\n"
|
||||
" <span class=\"o_form_label\" invisible=\"language_count <= 1\">\n"
|
||||
" Languages\n"
|
||||
" </span>"
|
||||
msgstr ""
|
||||
"<span class=\"o_form_label\" invisible=\"language_count > 1\">\n"
|
||||
" Язык\n"
|
||||
" </span>\n"
|
||||
" <span class=\"o_form_label\" invisible=\"language_count <= 1\">\n"
|
||||
" Языки\n"
|
||||
" </span>"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid ""
|
||||
"<strong>Save</strong> this page and come back here to choose your Geo "
|
||||
"Provider."
|
||||
msgstr ""
|
||||
"<strong>Сохраните</strong> эту страницу и вернитесь сюда, чтобы выбрать Geo "
|
||||
"Provider."
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid ""
|
||||
"<strong>Save</strong> this page and come back here to set up Cloudflare "
|
||||
"turnstile."
|
||||
msgstr ""
|
||||
"<strong>Сохраните</strong> эту страницу и вернитесь сюда, чтобы настроить "
|
||||
"Cloudflare Turnstile."
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid ""
|
||||
"<strong>Save</strong> this page and come back here to set up Google Places "
|
||||
"API key"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid ""
|
||||
"<strong>Save</strong> this page and come back here to set up reCaptcha."
|
||||
msgstr ""
|
||||
"<strong>Сохраните</strong> эту страницу и вернитесь сюда, чтобы настроить "
|
||||
"reCaptcha."
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid ""
|
||||
"<strong>Save</strong> this page and come back here to set up the feature."
|
||||
msgstr ""
|
||||
"<strong>Сохраните</strong> эту страницу и вернитесь сюда, чтобы настроить "
|
||||
"функцию."
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "API Keys"
|
||||
msgstr "Ключи API"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid ""
|
||||
"API Keys allow your users to access Odoo with external tools when multi-"
|
||||
"factor authentication is enabled."
|
||||
msgstr ""
|
||||
"Ключи API позволяют вашим пользователям получать доступ к Odoo с помощью "
|
||||
"внешних инструментов, если включена многофакторная аутентификация."
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "About"
|
||||
msgstr "О программе"
|
||||
|
||||
#. module: base_setup
|
||||
#. odoo-python
|
||||
#: code:addons/base_setup/controllers/main.py:0
|
||||
msgid "Access Denied"
|
||||
msgstr "Доступ запрещен"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Add Languages"
|
||||
msgstr "Добавить языки"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Add fun feedback and motivate your employees"
|
||||
msgstr "Добавьте интересную обратную связь и мотивируйте своих сотрудников"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__module_mail_plugin
|
||||
msgid "Allow integration with the mail plugins"
|
||||
msgstr "Разрешить интеграцию с почтовыми плагинами"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__module_google_calendar
|
||||
msgid "Allow the users to synchronize their calendar with Google Calendar"
|
||||
msgstr ""
|
||||
"Разрешить пользователям синхронизировать свой календарь с Google Calendar"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__module_microsoft_calendar
|
||||
msgid "Allow the users to synchronize their calendar with Outlook Calendar"
|
||||
msgstr ""
|
||||
"Разрешить пользователям синхронизировать свой календарь с Outlook Calendar"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__module_base_import
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Allow users to import data from CSV/XLS/XLSX/ODS files"
|
||||
msgstr ""
|
||||
"Разрешить пользователям импортировать данные из файлов CSV/XLS/XLSX/ODS"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,help:base_setup.field_res_config_settings__group_multi_currency
|
||||
msgid "Allows to work in a multi currency environment"
|
||||
msgstr "Позволяет работать в мультивалютной среде"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Autocomplete partner addresses with Google Places"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Automatically enrich your contact base with company data"
|
||||
msgstr "Автоматически дополняйте контактную базу данными о компаниях"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid ""
|
||||
"By default, new users get highest access rights for all installed apps. If "
|
||||
"unchecked, new users will only have basic employee access."
|
||||
msgstr ""
|
||||
"По умолчанию новые пользователи получают максимальные права доступа ко всем "
|
||||
"установленным приложениям. Если флажок снят, новые пользователи будут иметь "
|
||||
"только базовые права сотрудника."
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Choose the layout of your documents"
|
||||
msgstr "Выберите макет ваших документов"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__module_website_cf_turnstile
|
||||
msgid "Cloudflare Turnstile"
|
||||
msgstr "Cloudflare Turnstile (капча)"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Companies"
|
||||
msgstr "Компании"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__company_id
|
||||
msgid "Company"
|
||||
msgstr "Компания"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__company_country_code
|
||||
msgid "Company Country Code"
|
||||
msgstr "Код страны компании"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__company_informations
|
||||
msgid "Company Informations"
|
||||
msgstr "Сведения о компании"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__company_name
|
||||
msgid "Company Name"
|
||||
msgstr "Название компании"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model,name:base_setup.model_res_config_settings
|
||||
msgid "Config Settings"
|
||||
msgstr "Настройки конфигурации"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Configure Document Layout"
|
||||
msgstr "Настроить макет документа"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid ""
|
||||
"Configure company rules to automatically create SO/PO when one of your "
|
||||
"company sells/buys to another of your company."
|
||||
msgstr ""
|
||||
"Настройте правила компании для автоматического создания заказов на "
|
||||
"продажу/покупку, когда одна из ваших компаний продает/покупает у другой "
|
||||
"вашей компании."
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Contacts"
|
||||
msgstr "Контакты"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__company_country_group_codes
|
||||
msgid "Country Group Codes"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid ""
|
||||
"Create corresponding in/out orders and bills when you sell/buy between your "
|
||||
"companies"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__report_footer
|
||||
msgid "Custom Report Footer"
|
||||
msgstr "Пользовательский нижний колонтитул отчета"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Default Access Rights"
|
||||
msgstr "Права доступа по умолчанию"
|
||||
|
||||
#. module: base_setup
|
||||
#. odoo-python
|
||||
#: code:addons/base_setup/models/res_config_settings.py:0
|
||||
msgid "Default access for new users"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_ir_http__display_name
|
||||
#: model:ir.model.fields,field_description:base_setup.field_kpi_provider__display_name
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__display_name
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_users__display_name
|
||||
msgid "Display Name"
|
||||
msgstr "Display Name"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Document Layout"
|
||||
msgstr "Макет документа"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__external_report_layout_id
|
||||
msgid "Document Template"
|
||||
msgstr "Шаблон документа"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Edit Layout"
|
||||
msgstr "Изменить макет"
|
||||
|
||||
#. module: base_setup
|
||||
#. odoo-python
|
||||
#: code:addons/base_setup/models/res_config_settings.py:0
|
||||
msgid "Edit new user default group"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid ""
|
||||
"Enable the profiling tool. Profiling may impact performance while being "
|
||||
"active."
|
||||
msgstr ""
|
||||
"Включите инструмент профилирования. Профилирование может повлиять на "
|
||||
"производительность во время его активности."
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Find free high-resolution images from Unsplash"
|
||||
msgstr "Найдите бесплатные изображения высокого разрешения на Unsplash"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,help:base_setup.field_res_config_settings__report_footer
|
||||
msgid "Footer text displayed at the bottom of all reports."
|
||||
msgstr "Текст нижнего колонтитула, отображаемый внизу всех отчетов."
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.ui.menu,name:base_setup.menu_config
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "General Settings"
|
||||
msgstr "Общие настройки системы"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__module_base_geolocalize
|
||||
msgid "GeoLocalize"
|
||||
msgstr "Геолокация"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Geolocate your partners"
|
||||
msgstr "Определите местоположение ваших партнеров"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Geolocation"
|
||||
msgstr "Геолокация"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__module_google_address_autocomplete
|
||||
msgid "Google Address Autocomplete"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model,name:base_setup.model_ir_http
|
||||
msgid "HTTP Routing"
|
||||
msgstr "Маршрутизация HTTP"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_ir_http__id
|
||||
#: model:ir.model.fields,field_description:base_setup.field_kpi_provider__id
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__id
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_users__id
|
||||
msgid "ID"
|
||||
msgstr "ID"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Import & Export"
|
||||
msgstr "Импорт и экспорт"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Integrate with mail client plugins"
|
||||
msgstr "Интеграция с плагинами почтовых клиентов"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Integrations"
|
||||
msgstr "Интеграции"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Inter-Company Transactions"
|
||||
msgstr "Межфирменные операции"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__is_root_company
|
||||
msgid "Is Root Company"
|
||||
msgstr "Является корневой компанией"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model,name:base_setup.model_kpi_provider
|
||||
msgid "KPI Provider"
|
||||
msgstr "Поставщик KPI"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__module_auth_ldap
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "LDAP Authentication"
|
||||
msgstr "Аутентификация LDAP"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Languages"
|
||||
msgstr "Языки"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Layout"
|
||||
msgstr "Макет"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Mail Plugin"
|
||||
msgstr "Почтовый плагин"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Manage API Keys"
|
||||
msgstr "Управление ключами API"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Manage Companies"
|
||||
msgstr "Управление компаниями"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__module_account_inter_company_rules
|
||||
msgid "Manage Inter Company"
|
||||
msgstr "Управление межкомпанийными процессами"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Manage Languages"
|
||||
msgstr "Управление языками"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Manage Users"
|
||||
msgstr "Управление пользователями"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__group_multi_currency
|
||||
msgid "Multi-Currencies"
|
||||
msgstr "Мультивалютность"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__active_user_count
|
||||
msgid "Number of Active Users"
|
||||
msgstr "Количество активных пользователей"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__company_count
|
||||
msgid "Number of Companies"
|
||||
msgstr "Количество компаний"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__language_count
|
||||
msgid "Number of Languages"
|
||||
msgstr "Количество языков"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "OAuth Authentication"
|
||||
msgstr "Аутентификация OAuth"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Odoo"
|
||||
msgstr "Odoo"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__module_partner_autocomplete
|
||||
msgid "Partner Autocomplete"
|
||||
msgstr "Автозаполнение данных партнера"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Performance"
|
||||
msgstr "Производительность"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Permissions"
|
||||
msgstr "Разрешения"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__module_voip
|
||||
msgid "Phone"
|
||||
msgstr "Телефон"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Preview Document"
|
||||
msgstr "Предпросмотр документа"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__profiling_enabled_until
|
||||
msgid "Profiling enabled until"
|
||||
msgstr "Профилирование включено до"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Progressive Web App"
|
||||
msgstr "Прогрессивное веб-приложение"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Protect your forms from spam and abuse."
|
||||
msgstr "Защитите свои формы от спама и злоупотреблений."
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Protect your forms with CF Turnstile."
|
||||
msgstr "Защитите свои формы с помощью турникета Cloudflare."
|
||||
|
||||
#. module: base_setup
|
||||
#. odoo-javascript
|
||||
#: code:addons/base_setup/static/src/views/module_views.xml:0
|
||||
msgid "Reset Updating States"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__module_sms
|
||||
msgid "SMS"
|
||||
msgstr "СМС"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Send SMS"
|
||||
msgstr "Отправить СМС"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Send texts to your contacts"
|
||||
msgstr "Отправляйте сообщения своим контактам"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Set custom access rights for new users"
|
||||
msgstr "Установите пользовательские права доступа для новых пользователей"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.actions.act_window,name:base_setup.action_general_configuration
|
||||
msgid "Settings"
|
||||
msgstr "Настройки"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__show_effect
|
||||
msgid "Show Effect"
|
||||
msgstr "Показать эффект"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,help:base_setup.field_res_config_settings__company_country_code
|
||||
msgid ""
|
||||
"The ISO country code in two chars. \n"
|
||||
"You can use this field for quick search."
|
||||
msgstr ""
|
||||
"Код страны ISO из двух символов.\n"
|
||||
"Вы можете использовать это поле для быстрого поиска."
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid ""
|
||||
"This name will be used for the application when Odoo is installed through "
|
||||
"the browser."
|
||||
msgstr ""
|
||||
"Это имя будет использоваться для приложения при установке Odoo через "
|
||||
"браузер."
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__module_web_unsplash
|
||||
msgid "Unsplash Image Library"
|
||||
msgstr "Библиотека изображений Unsplash"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Update Info"
|
||||
msgstr "Обновить информацию"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Use LDAP credentials to log in"
|
||||
msgstr "Используйте учетные данные LDAP для входа"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Use external accounts to log in (Google, Facebook, etc.)"
|
||||
msgstr ""
|
||||
"Используйте внешние учетные записи для входа (Google, Facebook и т. д.)"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__module_auth_oauth
|
||||
msgid "Use external authentication providers (OAuth)"
|
||||
msgstr "Использовать внешние провайдеры аутентификации (OAuth)"
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model,name:base_setup.model_res_users
|
||||
msgid "User"
|
||||
msgstr "Пользователь"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid "Users"
|
||||
msgstr "Пользователи"
|
||||
|
||||
#. module: base_setup
|
||||
#. odoo-python
|
||||
#: code:addons/base_setup/models/res_config_settings.py:0
|
||||
msgid "VAT"
|
||||
msgstr "НДС"
|
||||
|
||||
#. module: base_setup
|
||||
#: model_terms:ir.ui.view,arch_db:base_setup.res_config_settings_view_form
|
||||
msgid ""
|
||||
"When populating your address book, Odoo provides a list of matching "
|
||||
"companies. When selecting one item, the company data and logo are auto-"
|
||||
"filled."
|
||||
msgstr ""
|
||||
"При заполнении адресной книги Odoo предоставляет список подходящих компаний."
|
||||
" При выборе одного элемента данные компании и логотип заполняются "
|
||||
"автоматически."
|
||||
|
||||
#. module: base_setup
|
||||
#. odoo-python
|
||||
#: code:addons/base_setup/models/res_users.py:0
|
||||
msgid "You have to install the Discuss application to use this feature."
|
||||
msgstr ""
|
||||
"Для использования этой функции необходимо установить модуль Обсуждения."
|
||||
|
||||
#. module: base_setup
|
||||
#: model:ir.model.fields,field_description:base_setup.field_res_config_settings__module_google_recaptcha
|
||||
msgid "reCAPTCHA"
|
||||
msgstr "reCAPTCHA"
|
||||
|
||||
#. module: base_setup
|
||||
msgid ""
|
||||
"Automatically generate counterpart documents for orders/invoices between "
|
||||
"companies"
|
||||
msgstr ""
|
||||
"Автоматически генерировать контрагентские документы для заказов/инвойсов "
|
||||
"между компаниями"
|
||||
|
||||
#. module: base_setup
|
||||
msgid ""
|
||||
"By default, new users get highest access rights for all installed apps."
|
||||
msgstr ""
|
||||
"По умолчанию новые пользователи получают высокие права доступа для всех "
|
||||
"установленных приложений."
|
||||
|
||||
#. module: base_setup
|
||||
msgid "Default User Template not found."
|
||||
msgstr "Шаблон пользователя по умолчанию не найден."
|
||||
|
||||
#. module: base_setup
|
||||
msgid "Documentation"
|
||||
msgstr "Документация"
|
||||
|
||||
#. module: base_setup
|
||||
msgid "Geo Localization"
|
||||
msgstr "Геолокация"
|
||||
|
||||
#. module: base_setup
|
||||
msgid "GeoLocalize your partners"
|
||||
msgstr "Геолокализация ваших партнеров"
|
||||
|
||||
#. module: base_setup
|
||||
msgid "Get product pictures using barcode"
|
||||
msgstr "Получение изображений товаров с помощью штрихкода"
|
||||
|
||||
#. module: base_setup
|
||||
msgid "VoIP"
|
||||
msgstr "VoIP"
|
||||
109
translation_helper/translations/ru/contacts.po
Normal file
109
translation_helper/translations/ru/contacts.po
Normal file
@ -0,0 +1,109 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 17.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-01-25 18:36+0000\n"
|
||||
"PO-Revision-Date: 2025-11-16 15:08+0000\n"
|
||||
"Last-Translator: Weblate <noreply-mt-weblate@weblate.org>\n"
|
||||
"Language-Team: Russian <https://translate.odoo.com/projects/odoo-19/contacts/ru/>\n"
|
||||
"Language: ru\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
|
||||
"X-Generator: Weblate 5.12.2\n"
|
||||
|
||||
#. module: contacts
|
||||
#: model:ir.ui.menu,name:contacts.menu_action_res_partner_bank_form
|
||||
#: model:ir.ui.menu,name:contacts.menu_config_bank_accounts
|
||||
msgid "Bank Accounts"
|
||||
msgstr "Банковские счета"
|
||||
|
||||
#. module: contacts
|
||||
#: model:ir.ui.menu,name:contacts.menu_action_res_bank_form
|
||||
msgid "Banks"
|
||||
msgstr "Банки"
|
||||
|
||||
#. module: contacts
|
||||
#: model:ir.ui.menu,name:contacts.res_partner_menu_config
|
||||
msgid "Configuration"
|
||||
msgstr "Конфигурация"
|
||||
|
||||
#. module: contacts
|
||||
#: model:ir.model,name:contacts.model_res_partner
|
||||
msgid "Contact"
|
||||
msgstr "Контакты"
|
||||
|
||||
#. module: contacts
|
||||
#: model:ir.ui.menu,name:contacts.menu_partner_category_form
|
||||
msgid "Contact Tags"
|
||||
msgstr "Теги контакта"
|
||||
|
||||
#. module: contacts
|
||||
#: model:ir.actions.act_window,name:contacts.action_contacts
|
||||
#: model:ir.ui.menu,name:contacts.menu_contacts
|
||||
#: model:ir.ui.menu,name:contacts.res_partner_menu_contacts
|
||||
msgid "Contacts"
|
||||
msgstr "Контакт"
|
||||
|
||||
#. module: contacts
|
||||
#: model:ir.ui.menu,name:contacts.menu_country_partner
|
||||
msgid "Countries"
|
||||
msgstr "Страны"
|
||||
|
||||
#. module: contacts
|
||||
#: model:ir.ui.menu,name:contacts.menu_country_group
|
||||
msgid "Country Group"
|
||||
msgstr "Группы стран"
|
||||
|
||||
#. module: contacts
|
||||
#: model_terms:ir.actions.act_window,help:contacts.action_contacts
|
||||
msgid "Create a Contact in your address book"
|
||||
msgstr "Создать контакт в адресной книге"
|
||||
|
||||
#. module: contacts
|
||||
#: model:ir.model,website_form_label:contacts.model_res_partner
|
||||
msgid "Create a Customer"
|
||||
msgstr "Создать клиента"
|
||||
|
||||
#. module: contacts
|
||||
#: model:ir.model.fields,field_description:contacts.field_res_partner__display_name
|
||||
#: model:ir.model.fields,field_description:contacts.field_res_users__display_name
|
||||
msgid "Display Name"
|
||||
msgstr "Display Name"
|
||||
|
||||
#. module: contacts
|
||||
#: model:ir.ui.menu,name:contacts.menu_country_state_partner
|
||||
msgid "Fed. States"
|
||||
msgstr "Регионы"
|
||||
|
||||
#. module: contacts
|
||||
#: model:ir.model.fields,field_description:contacts.field_res_partner__id
|
||||
#: model:ir.model.fields,field_description:contacts.field_res_users__id
|
||||
msgid "ID"
|
||||
msgstr "ID"
|
||||
|
||||
#. module: contacts
|
||||
#: model:ir.ui.menu,name:contacts.res_partner_industry_menu
|
||||
msgid "Industries"
|
||||
msgstr "Профессия"
|
||||
|
||||
#. module: contacts
|
||||
#: model:ir.ui.menu,name:contacts.menu_localisation
|
||||
msgid "Localization"
|
||||
msgstr "Локализация"
|
||||
|
||||
#. module: contacts
|
||||
#: model_terms:ir.actions.act_window,help:contacts.action_contacts
|
||||
msgid "Odoo helps you track all activities related to your contacts."
|
||||
msgstr "Помогает отслеживать все действия, связанные с вашими контактами."
|
||||
|
||||
#. module: contacts
|
||||
#: model:ir.model,name:contacts.model_res_users
|
||||
msgid "User"
|
||||
msgstr "Пользователь"
|
||||
|
||||
#. module: contacts
|
||||
msgid "Contact Titles"
|
||||
msgstr "Названия контактов"
|
||||
4318
translation_helper/translations/ru/crm.po
Normal file
4318
translation_helper/translations/ru/crm.po
Normal file
File diff suppressed because it is too large
Load Diff
7
translation_helper/translations/ru/glossary.txt
Normal file
7
translation_helper/translations/ru/glossary.txt
Normal file
@ -0,0 +1,7 @@
|
||||
|
||||
Purchase Order Заказ поставщику.
|
||||
Stock Picking Перемещение товаров
|
||||
Bill Входящая счёт-фактура
|
||||
Sell Order Коммерческое преложение
|
||||
Invoice Исходящая счёт-фактура
|
||||
PoS Order Кассовый чек
|
||||
6145
translation_helper/translations/ru/hr.po
Normal file
6145
translation_helper/translations/ru/hr.po
Normal file
File diff suppressed because it is too large
Load Diff
5382
translation_helper/translations/ru/product.po
Normal file
5382
translation_helper/translations/ru/product.po
Normal file
File diff suppressed because it is too large
Load Diff
4382
translation_helper/translations/ru/purchase.po
Normal file
4382
translation_helper/translations/ru/purchase.po
Normal file
File diff suppressed because it is too large
Load Diff
6839
translation_helper/translations/ru/sale.po
Normal file
6839
translation_helper/translations/ru/sale.po
Normal file
File diff suppressed because it is too large
Load Diff
547
translation_helper/translations/ru/uom.po
Normal file
547
translation_helper/translations/ru/uom.po
Normal file
@ -0,0 +1,547 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * uom
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 17.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-04-21 19:52+0000\n"
|
||||
"PO-Revision-Date: 2024-04-21 19:52+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: uom
|
||||
#: model_terms:ir.ui.view,arch_db:uom.product_uom_form_view
|
||||
msgid ""
|
||||
"<span class=\"oe_grey oe_inline\">\n"
|
||||
" e.g: 1*(reference unit)=ratio*(this unit)\n"
|
||||
" </span>"
|
||||
msgstr ""
|
||||
"<span class=\"oe_grey oe_inline\">\n"
|
||||
" например: 1*(базовая единица)=коэффициент*(данная единица)\n"
|
||||
" </span>"
|
||||
|
||||
#. module: uom
|
||||
#: model_terms:ir.ui.view,arch_db:uom.product_uom_form_view
|
||||
msgid ""
|
||||
"<span class=\"oe_grey oe_inline\">\n"
|
||||
" e.g: 1*(this unit)=ratio*(reference unit)\n"
|
||||
" </span>"
|
||||
msgstr ""
|
||||
"<span class=\"oe_grey oe_inline\">\n"
|
||||
" например: 1*(текущая единица измерения)=коэффициент*(базовая единица измерения)\n"
|
||||
" </span>"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_uom__active
|
||||
msgid "Active"
|
||||
msgstr "Активный"
|
||||
|
||||
#. module: uom
|
||||
#: model_terms:ir.actions.act_window,help:uom.product_uom_form_action
|
||||
msgid "Add a new unit of measure"
|
||||
msgstr "Добавить новую единицу измерения"
|
||||
|
||||
#. module: uom
|
||||
#: model_terms:ir.actions.act_window,help:uom.product_uom_categ_form_action
|
||||
msgid "Add a new unit of measure category"
|
||||
msgstr "Добавьте новую категорию единиц измерения"
|
||||
|
||||
#. module: uom
|
||||
#: model_terms:ir.ui.view,arch_db:uom.uom_uom_view_search
|
||||
msgid "Archived"
|
||||
msgstr "Архивировано"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_uom__factor_inv
|
||||
msgid "Bigger Ratio"
|
||||
msgstr "Коэффициент Больше"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields.selection,name:uom.selection__uom_uom__uom_type__bigger
|
||||
msgid "Bigger than the reference Unit of Measure"
|
||||
msgstr "Больше, чем базовая Единица Измерения"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_uom__category_id
|
||||
#: model_terms:ir.ui.view,arch_db:uom.uom_uom_view_search
|
||||
msgid "Category"
|
||||
msgstr "Категория"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_uom__color
|
||||
msgid "Color"
|
||||
msgstr "Цвет"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_uom__ratio
|
||||
msgid "Combined Ratio"
|
||||
msgstr "Комбинированный коэффициент"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,help:uom.field_uom_uom__category_id
|
||||
msgid ""
|
||||
"Conversion between Units of Measure can only occur if they belong to the "
|
||||
"same category. The conversion will be made based on the ratios."
|
||||
msgstr ""
|
||||
"Пересчет между единицами измерения может произойти только в том случае, если"
|
||||
" они принадлежат к одной и той же категории. Пересчет будет осуществляться "
|
||||
"на основе коэффициентов."
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_category__create_uid
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_uom__create_uid
|
||||
msgid "Created by"
|
||||
msgstr "Создано"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_category__create_date
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_uom__create_date
|
||||
msgid "Created on"
|
||||
msgstr "Создано"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_day
|
||||
msgid "Days"
|
||||
msgstr "Дней"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_category__display_name
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_uom__display_name
|
||||
msgid "Display Name"
|
||||
msgstr "Отображаемое имя"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_dozen
|
||||
msgid "Dozens"
|
||||
msgstr "Дюжины"
|
||||
|
||||
#. module: uom
|
||||
#: model_terms:ir.ui.view,arch_db:uom.uom_uom_view_search
|
||||
msgid "Group By"
|
||||
msgstr "Группировать по"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_hour
|
||||
msgid "Hours"
|
||||
msgstr "Часов"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,help:uom.field_uom_uom__factor_inv
|
||||
msgid ""
|
||||
"How many times this Unit of Measure is bigger than the reference Unit of "
|
||||
"Measure in this category: 1 * (this unit) = ratio * (reference unit)"
|
||||
msgstr ""
|
||||
"Во сколько раз эта единица измерения больше, чем базовая единица измерения в"
|
||||
" этой категории: 1 * (текущая единица) = коэффициент * (базовая единица)"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,help:uom.field_uom_uom__factor
|
||||
msgid ""
|
||||
"How much bigger or smaller this unit is compared to the reference Unit of "
|
||||
"Measure for this category: 1 * (reference unit) = ratio * (this unit)"
|
||||
msgstr ""
|
||||
"Насколько больше или меньше эта единица сравнивается с базовой единицей "
|
||||
"измерения для этой категории: 1 * (базовая единица) = коэффициент * (текущая"
|
||||
" единица)"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_category__id
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_uom__id
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_litre
|
||||
msgid "L"
|
||||
msgstr "литр"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_category__write_uid
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_uom__write_uid
|
||||
msgid "Last Updated by"
|
||||
msgstr "Последнее обновление"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_category__write_date
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_uom__write_date
|
||||
msgid "Last Updated on"
|
||||
msgstr "Последнее обновление"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.category,name:uom.uom_categ_length
|
||||
msgid "Length / Distance"
|
||||
msgstr "Длина / Расстояние"
|
||||
|
||||
#. module: uom
|
||||
#: model:res.groups,name:uom.group_uom
|
||||
msgid "Manage Multiple Units of Measure"
|
||||
msgstr "Управление несколькими единицами измерения"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model,name:uom.model_uom_uom
|
||||
msgid "Product Unit of Measure"
|
||||
msgstr "Единица Измерения продукта"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model,name:uom.model_uom_category
|
||||
msgid "Product UoM Categories"
|
||||
msgstr "Категории Единиц Измерения продукта"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_uom__factor
|
||||
#: model_terms:ir.ui.view,arch_db:uom.product_uom_categ_form_view
|
||||
msgid "Ratio"
|
||||
msgstr "Коэффициент"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields.selection,name:uom.selection__uom_uom__uom_type__reference
|
||||
msgid "Reference Unit of Measure for this category"
|
||||
msgstr "Базовая единица измерения для данной категории"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_category__reference_uom_id
|
||||
msgid "Reference UoM"
|
||||
msgstr "Базовая Единица Измерения"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_uom__rounding
|
||||
msgid "Rounding Precision"
|
||||
msgstr "Точность Округления"
|
||||
|
||||
#. module: uom
|
||||
#: model_terms:ir.ui.view,arch_db:uom.uom_uom_view_search
|
||||
msgid "Search UOM"
|
||||
msgstr "Поиск Единицы Измерения"
|
||||
|
||||
#. module: uom
|
||||
#: model_terms:ir.ui.view,arch_db:uom.uom_categ_view_search
|
||||
msgid "Search UoM Category"
|
||||
msgstr "Поиск категории Единиц Измерения"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields.selection,name:uom.selection__uom_uom__uom_type__smaller
|
||||
msgid "Smaller than the reference Unit of Measure"
|
||||
msgstr "Меньше, чем базовая Единица Измерения"
|
||||
|
||||
#. module: uom
|
||||
#. odoo-python
|
||||
#: code:addons/uom/models/uom_uom.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Some critical fields have been modified on %s.\n"
|
||||
"Note that existing data WON'T be updated by this change.\n"
|
||||
"\n"
|
||||
"As units of measure impact the whole system, this may cause critical issues.\n"
|
||||
"E.g. modifying the rounding could disturb your inventory balance.\n"
|
||||
"\n"
|
||||
"Therefore, changing core units of measure in a running database is not recommended."
|
||||
msgstr ""
|
||||
"Некоторые критически важные поля были изменены на %s.\n"
|
||||
"Обратите внимание, что ранее созданные данные не будут обновлены этим изменением.\n"
|
||||
"\n"
|
||||
"Поскольку единицы измерения влияют на всю систему, это может привести к критическим проблемам.\n"
|
||||
"Например, изменение округления может нарушить баланс складских запасов.\n"
|
||||
"\n"
|
||||
"Поэтому не рекомендуется изменять основные единицы измерения в работающей базе данных."
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.category,name:uom.uom_categ_surface
|
||||
msgid "Surface"
|
||||
msgstr "Площадь"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,help:uom.field_uom_uom__rounding
|
||||
msgid ""
|
||||
"The computed quantity will be a multiple of this value. Use 1.0 for a Unit "
|
||||
"of Measure that cannot be further split, such as a piece."
|
||||
msgstr ""
|
||||
"Вычисленное количество будет кратно этому значению. Используйте 1,0 для "
|
||||
"единиц измерения, которые нельзя разделить на части, например, для таких как"
|
||||
" штука."
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.constraint,message:uom.constraint_uom_uom_factor_gt_zero
|
||||
msgid "The conversion ratio for a unit of measure cannot be 0!"
|
||||
msgstr "Коэффициент пересчета для единицы измерения не может быть равен 0!"
|
||||
|
||||
#. module: uom
|
||||
#. odoo-python
|
||||
#: code:addons/uom/models/uom_uom.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The following units of measure are used by the system and cannot be deleted: %s\n"
|
||||
"You can archive them instead."
|
||||
msgstr ""
|
||||
"Следующие единицы измерения используются системой и не могут быть удалены: %s\n"
|
||||
"Вместо этого их можно заархивировать."
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.constraint,message:uom.constraint_uom_uom_factor_reference_is_one
|
||||
msgid "The reference unit must have a conversion factor equal to 1."
|
||||
msgstr "Базовая единица должна иметь коэффициент пересчета, равный 1."
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.constraint,message:uom.constraint_uom_uom_rounding_gt_zero
|
||||
msgid "The rounding precision must be strictly positive."
|
||||
msgstr "Точность округления должна быть строго положительной."
|
||||
|
||||
#. module: uom
|
||||
#. odoo-python
|
||||
#: code:addons/uom/models/uom_uom.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The unit of measure %s defined on the order line doesn't belong to the same "
|
||||
"category as the unit of measure %s defined on the product. Please correct "
|
||||
"the unit of measure defined on the order line or on the product, they should"
|
||||
" belong to the same category."
|
||||
msgstr ""
|
||||
"Единица измерения %s, определенная в строке заказа, не принадлежит к той же "
|
||||
"категории, что и единица измерения %s, определенная в товаре. Пожалуйста, "
|
||||
"исправьте единицу измерения, определенную в строке заказа или в товаре, они "
|
||||
"должны относиться к одной и той же категории."
|
||||
|
||||
#. module: uom
|
||||
#. odoo-python
|
||||
#: code:addons/uom/models/uom_uom.py:0
|
||||
#, python-format
|
||||
msgid "The value of ratio could not be Zero"
|
||||
msgstr "Значение коэффициента не может равно нулю"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_uom__uom_type
|
||||
msgid "Type"
|
||||
msgstr "Тип"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,help:uom.field_uom_uom__active
|
||||
msgid ""
|
||||
"Uncheck the active field to disable a unit of measure without deleting it."
|
||||
msgstr ""
|
||||
"Снимите флажок поля 'Активно', чтобы отключить единицу измерения, не удаляя "
|
||||
"ее."
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.category,name:uom.product_uom_categ_unit
|
||||
msgid "Unit"
|
||||
msgstr "Штука"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_uom__name
|
||||
msgid "Unit of Measure"
|
||||
msgstr "Единица Измерения"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_category__name
|
||||
msgid "Unit of Measure Category"
|
||||
msgstr "Категория Единиц Измерения"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_unit
|
||||
msgid "Units"
|
||||
msgstr "шт."
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.actions.act_window,name:uom.product_uom_form_action
|
||||
#: model_terms:ir.ui.view,arch_db:uom.product_uom_categ_form_view
|
||||
#: model_terms:ir.ui.view,arch_db:uom.product_uom_form_view
|
||||
#: model_terms:ir.ui.view,arch_db:uom.product_uom_tree_view
|
||||
msgid "Units of Measure"
|
||||
msgstr "Единицы Измерения"
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.actions.act_window,name:uom.product_uom_categ_form_action
|
||||
msgid "Units of Measure Categories"
|
||||
msgstr "Категории Единиц Измерения"
|
||||
|
||||
#. module: uom
|
||||
#: model_terms:ir.ui.view,arch_db:uom.product_uom_categ_form_view
|
||||
#: model_terms:ir.ui.view,arch_db:uom.product_uom_categ_tree_view
|
||||
msgid "Units of Measure categories"
|
||||
msgstr "Категории единиц измерения"
|
||||
|
||||
#. module: uom
|
||||
#: model_terms:ir.actions.act_window,help:uom.product_uom_categ_form_action
|
||||
msgid ""
|
||||
"Units of measure belonging to the same category can be\n"
|
||||
" converted between each others. For example, in the category\n"
|
||||
" <i>'Time'</i>, you will have the following units of measure:\n"
|
||||
" Hours, Days."
|
||||
msgstr ""
|
||||
"Единицы измерения, относящиеся к одной и той же категории, могут\n"
|
||||
" конвертироваться друг в друга. Например, в категории\n"
|
||||
" <i>'Рабочее Время' i>, у вас будут следующие единицы измерения:\n"
|
||||
" Часы, Дни.\n"
|
||||
" </i>"
|
||||
|
||||
#. module: uom
|
||||
#. odoo-python
|
||||
#: code:addons/uom/models/uom_uom.py:0
|
||||
#, python-format
|
||||
msgid "UoM category %s must have at least one reference unit of measure."
|
||||
msgstr ""
|
||||
"Категория Единицы Измерения %s должна иметь хотя бы одну базовую единицу "
|
||||
"измерения."
|
||||
|
||||
#. module: uom
|
||||
#. odoo-python
|
||||
#: code:addons/uom/models/uom_uom.py:0
|
||||
#, python-format
|
||||
msgid "UoM category %s should have a reference unit of measure."
|
||||
msgstr ""
|
||||
"Категория Единицы Измерения %s должна иметь базовую единицу измерения."
|
||||
|
||||
#. module: uom
|
||||
#. odoo-python
|
||||
#: code:addons/uom/models/uom_uom.py:0
|
||||
#, python-format
|
||||
msgid "UoM category %s should only have one reference unit of measure."
|
||||
msgstr ""
|
||||
"Категория Единицы Измерения %s может иметь только одну базовую единицу "
|
||||
"измерения."
|
||||
|
||||
#. module: uom
|
||||
#: model:ir.model.fields,field_description:uom.field_uom_category__uom_ids
|
||||
msgid "Uom"
|
||||
msgstr "Ед. Изм."
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.category,name:uom.product_uom_categ_vol
|
||||
msgid "Volume"
|
||||
msgstr "Объем"
|
||||
|
||||
#. module: uom
|
||||
#. odoo-python
|
||||
#: code:addons/uom/models/uom_uom.py:0
|
||||
#, python-format
|
||||
msgid "Warning for %s"
|
||||
msgstr "Предупреждение для %s"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.category,name:uom.product_uom_categ_kgm
|
||||
msgid "Weight"
|
||||
msgstr "Вес"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.category,name:uom.uom_categ_wtime
|
||||
msgid "Working Time"
|
||||
msgstr "Рабочее время"
|
||||
|
||||
#. module: uom
|
||||
#: model_terms:ir.actions.act_window,help:uom.product_uom_form_action
|
||||
msgid ""
|
||||
"You must define a conversion rate between several Units of\n"
|
||||
" Measure within the same category."
|
||||
msgstr ""
|
||||
"Вы должны определить коэффициент конверсии между несколькими Единицами\n"
|
||||
" Измерения в той же категории."
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_cm
|
||||
msgid "cm"
|
||||
msgstr "см"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_floz
|
||||
msgid "fl oz (US)"
|
||||
msgstr "жидкая унция (США)"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_foot
|
||||
msgid "ft"
|
||||
msgstr "фут"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.uom_square_foot
|
||||
msgid "ft²"
|
||||
msgstr "фт²"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_cubic_foot
|
||||
msgid "ft³"
|
||||
msgstr "фут³"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_gram
|
||||
msgid "g"
|
||||
msgstr ""
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_gal
|
||||
msgid "gal (US)"
|
||||
msgstr "галлон (США)"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_inch
|
||||
msgid "in"
|
||||
msgstr "дюйм"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_cubic_inch
|
||||
msgid "in³"
|
||||
msgstr "дюйм³"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_kgm
|
||||
msgid "kg"
|
||||
msgstr "кг"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_km
|
||||
msgid "km"
|
||||
msgstr "км"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_lb
|
||||
msgid "lb"
|
||||
msgstr "фунт"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_meter
|
||||
msgid "m"
|
||||
msgstr "м"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_mile
|
||||
msgid "mi"
|
||||
msgstr "миля"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_millimeter
|
||||
msgid "mm"
|
||||
msgstr "мм"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.uom_square_meter
|
||||
msgid "m²"
|
||||
msgstr "м²"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_cubic_meter
|
||||
msgid "m³"
|
||||
msgstr "м³"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_oz
|
||||
msgid "oz"
|
||||
msgstr "унция (oz)"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_qt
|
||||
msgid "qt (US)"
|
||||
msgstr "кварта (США)"
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_ton
|
||||
msgid "t"
|
||||
msgstr ""
|
||||
|
||||
#. module: uom
|
||||
#: model:uom.uom,name:uom.product_uom_yard
|
||||
msgid "yd"
|
||||
msgstr "ярд"
|
||||
41
translation_helper/views/res_config_settings_views.xml
Normal file
41
translation_helper/views/res_config_settings_views.xml
Normal file
@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record id="res_config_settings_view_form_budget" model="ir.ui.view">
|
||||
<field name="name">res.config.settings.view.form.budget</field>
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="priority" eval="25" />
|
||||
<field name="inherit_id" ref="base_setup.res_config_settings_view_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//form" position="inside">
|
||||
<app string="Переводы" groups="base.group_system" name="translation_helper" img="/translation_helper/static/description/icon.png">
|
||||
<block title="Translation Helper" name="translation_helper_setting_container">
|
||||
<setting id="translation_weblate_server_protocol">
|
||||
<field name="translation_weblate_server_protocol" />
|
||||
<div class="text-muted">
|
||||
Протокол вашего Weblate сервера
|
||||
</div>
|
||||
</setting>
|
||||
<setting id="translation_weblate_server_address">
|
||||
<field name="translation_weblate_server_address" />
|
||||
<div class="text-muted">
|
||||
Адрес вашего weblate сервера
|
||||
</div>
|
||||
</setting>
|
||||
<setting id="translation_weblate_project_alias">
|
||||
<field name="translation_weblate_project_alias" />
|
||||
<div class="text-muted">
|
||||
Алиас вашего Weblate проекта
|
||||
</div>
|
||||
</setting>
|
||||
<setting id="translation_weblate_project_language">
|
||||
<field name="translation_weblate_project_language" />
|
||||
<div class="text-muted">
|
||||
Язык для вашего Weblate проекта
|
||||
</div>
|
||||
</setting>
|
||||
</block>
|
||||
</app>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
9
translation_helper/views/templates.xml
Normal file
9
translation_helper/views/templates.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<odoo>
|
||||
<template id="translation_helper.layout_with_translate" name="Web layout with translate" inherit_id="web.layout">
|
||||
<xpath expr="//head/script[@id='web.layout.odooscript']" position="after">
|
||||
<script id="translation_helper.layout.odooscript.add.translate" type="text/javascript">
|
||||
odoo.translate = "<t t-esc="translate" />"
|
||||
</script>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
1
translation_helper/wizards/__init__.py
Normal file
1
translation_helper/wizards/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import translation_helper_wizard
|
||||
139
translation_helper/wizards/translation_helper_wizard.py
Normal file
139
translation_helper/wizards/translation_helper_wizard.py
Normal file
@ -0,0 +1,139 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
import polib
|
||||
|
||||
from odoo import fields, models
|
||||
from odoo.modules.module import get_module_path
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
dir_path = os.path.dirname(os.path.realpath(__file__))
|
||||
dir_path = os.path.join(dir_path, "..")
|
||||
|
||||
|
||||
class TranslationHelperWizard(models.TransientModel):
|
||||
_name = "translation.helper.wizard"
|
||||
_description = "Translation Helper Wizard"
|
||||
|
||||
weblate_link = fields.Char(
|
||||
string="Weblate Link",
|
||||
help="Link to current term on Weblate server",
|
||||
)
|
||||
|
||||
term_value = fields.Char(
|
||||
string="Term",
|
||||
help="Term value to translate",
|
||||
)
|
||||
|
||||
translation_value = fields.Char(
|
||||
string="Translation",
|
||||
help="Translation for term value",
|
||||
)
|
||||
|
||||
language_id = fields.Many2one(
|
||||
string="Language",
|
||||
help="The language for which the translation will be applied",
|
||||
comodel_name="res.lang",
|
||||
)
|
||||
|
||||
modules = fields.Char(
|
||||
string="In Apps",
|
||||
help="List of modules in which the translation term is defined",
|
||||
)
|
||||
|
||||
metadata = fields.Json(
|
||||
string="Metadata",
|
||||
help="Metadata",
|
||||
)
|
||||
|
||||
def save_translation(self):
|
||||
for module_name in self.modules.split(", "):
|
||||
self.update_term_translation_in_module(
|
||||
module_name=module_name,
|
||||
term_to_translate=self.term_value,
|
||||
translation_value=self.translation_value,
|
||||
lang=self.language_id.iso_code,
|
||||
)
|
||||
model_name = self.metadata.get("resModel")
|
||||
field_name = self.metadata.get("field", {}).get("name")
|
||||
translation_field_data = self.metadata.get("translation_field_data", {})
|
||||
translation_field_data[self.language_id.code] = self.translation_value
|
||||
query = """
|
||||
UPDATE ir_model_fields SET field_description = %s
|
||||
WHERE model = %s AND name = %s
|
||||
"""
|
||||
self.env.cr.execute(
|
||||
query, [json.dumps(translation_field_data), model_name, field_name]
|
||||
)
|
||||
|
||||
return {
|
||||
"type": "ir.actions.client",
|
||||
"tag": "translation_helper.response_from_wizard",
|
||||
"name": "Translate info",
|
||||
"params": {
|
||||
"model_name": model_name,
|
||||
"field_name": field_name,
|
||||
"translation_field_data": translation_field_data,
|
||||
},
|
||||
}
|
||||
|
||||
def update_term_translation_in_module(
|
||||
self, module_name, term_to_translate, translation_value, lang
|
||||
):
|
||||
# Always write to local translations/{lang}/ directory inside translation_helper module
|
||||
local_po_dir = os.path.join(dir_path, "translations", lang.strip())
|
||||
local_po_file = os.path.join(local_po_dir, f"{module_name}.po")
|
||||
|
||||
if os.path.exists(local_po_file):
|
||||
# Local override already exists — update it
|
||||
source_pofile = polib.pofile(local_po_file)
|
||||
else:
|
||||
# Try to find the system .po file to use as a base
|
||||
source_pofile = None
|
||||
module_path = get_module_path(module_name, display_warning=False)
|
||||
if module_path:
|
||||
for subdir in ("i18n", "i18n_extra"):
|
||||
candidate = os.path.join(module_path, subdir, f"{lang}.po")
|
||||
if os.path.exists(candidate):
|
||||
source_pofile = polib.pofile(candidate)
|
||||
break
|
||||
|
||||
if source_pofile is None:
|
||||
_logger.warning(
|
||||
"translation_helper: no .po file found for module %r, lang %r",
|
||||
module_name,
|
||||
lang,
|
||||
)
|
||||
return
|
||||
|
||||
# Ensure local directory exists
|
||||
os.makedirs(local_po_dir, exist_ok=True)
|
||||
|
||||
# Build new pofile with updated translation
|
||||
new_pofile = polib.POFile()
|
||||
new_pofile.metadata = source_pofile.metadata
|
||||
module_prefix = f"module: {module_name}"
|
||||
for old_entry in source_pofile:
|
||||
msgstr = old_entry.msgstr
|
||||
if old_entry.msgid == term_to_translate:
|
||||
msgstr = translation_value
|
||||
# Odoo 19 translate.py requires "module: {name}" in every entry's comment
|
||||
comment = old_entry.comment
|
||||
if comment and "module:" not in comment:
|
||||
comment = f"module: {module_name}\n{comment}"
|
||||
elif not comment:
|
||||
comment = f"module: {module_name}"
|
||||
new_entry = polib.POEntry(
|
||||
comment=comment,
|
||||
occurrences=old_entry.occurrences,
|
||||
flags=old_entry.flags,
|
||||
msgid=old_entry.msgid,
|
||||
msgstr=msgstr,
|
||||
)
|
||||
new_pofile.append(new_entry)
|
||||
|
||||
# Always save to local path (never to system directories)
|
||||
new_pofile.save(local_po_file)
|
||||
23
translation_helper/wizards/translation_helper_wizard.xml
Normal file
23
translation_helper/wizards/translation_helper_wizard.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="translation_helper_wizard_form" model="ir.ui.view">
|
||||
<field name="name">translation.helper.wizard.form</field>
|
||||
<field name="model">translation.helper.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="weblate_link" readonly="1" widget="url" options="{'website_path': True}" />
|
||||
<field name="term_value" readonly="1" />
|
||||
<field name="translation_value" />
|
||||
<field name="language_id" readonly="1" />
|
||||
</group>
|
||||
</sheet>
|
||||
<footer>
|
||||
<button name="save_translation" string="Apply" class="oe_highlight" type="object" data-hotkey="q" />
|
||||
<button special="cancel" data-hotkey="x" string="Cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user