Переводы¶
Исходный язык — английский, целиком: код, схемы тулов, промпты, строки интерфейса. Русский живёт рядом с кодом как каталог переводов. Плагин следует за языком интерфейса QGIS; неподдержанные локали откатываются на английский.
Как это устроено¶
Три файла, из которых руками правится только средний:
| Файл | Что это | Правят руками? |
|---|---|---|
*.py с tr("Apply") |
английский оригинал | да — это и есть код |
ai_agent/translations/ai_agent_ru.ts |
XML, английский → русский | да, только колонку перевода |
ai_agent/translations/ai_agent_ru.qm |
скомпилированный бинарник, его грузит QGIS | никогда — генерируется |
Отдельного ключа нет: ключом служит сама английская строка. Отсутствующий
перевод безвреден — tr() вернёт английский оригинал.
Рабочий цикл¶
После добавления или правки строки в tr():
python3 tools/update_translations.py
Команде не нужны зависимости. Она вытаскивает литералы из tr() / tr_n()
разбором AST, дописывает новые записи в .ts (сделанные переводы
сохраняются) и компилирует .qm собственным компилятором из tools/qm.py.
Это осознанный отход от штатного рецепта QGIS
(.pro → pylupdate5 → Qt Linguist → lrelease → .qm), и оба заменённых
шага заменены по измеренным причинам:
- Извлечение — разбором AST, а не
pylupdate5. Тот вызовыtr()находит, но читает не-ASCII в исходнике как latin-1 (…и■приезжают мусором, и поиск перевода в рантайме промахивается), вовсе не видитtr_n— множественное число теряется целиком — и пишет контекст@defaultвместоQgisAiAgent, по которому идёт поиск. - Компиляция —
tools/qm.py, а неlrelease. Этот бинарник живёт в Qt-тулзах, которых нет ни в QGIS, ни в типичной системе: ставить 349 МБ PySide6 ради одного исполняемого файла абсурдно рядом со 144-КБ плагином. Формат.qmпрост: магия, блок языка, таблицаelfHash(source) → смещение, записи сообщений и правила множественного числа. Вывод побайтово совпадает с настоящимlrelease, что закреплено эталоном:tests/data/golden_ru.qmсобран им один раз и лежит в репозитории, аtests/test_qm.pyтребует, чтобы наш компилятор его воспроизводил. Эталон намеренно покрывает то, на чём легко разойтись: не-ASCII, экранируемые в XML символы, перевод строки и все три формы множественного числа.
Дальше заполните пустые <translation> в .ts и прогоните команду ещё раз.
tests/test_i18n.py падает, пока что-то осталось непереведённым, пока
плейсхолдер ({0}, %n) потерян в переводе или каталог разошёлся с кодом.
Правила¶
tr()принимает только литерал:tr("Layer '{0}'").format(name)— не f-строку, из которой экстрактор ничего не достанет.- Пользовательский текст (
summarize_call, всё вui/) оборачивается вtr(). Модельный текст (описания тулов, схемы, ошибки) — чистый английский безtr(): пропустить его через переводчик значило бы слать модели русские схемы в русском QGIS. - Множественное число идёт через
tr_n("%n step(s)", count); у русского в каталоге все три формы.
Добавление языка¶
- Добавьте локаль в
SUPPORTED_LOCALESвai_agent/i18n.pyи вLANGUAGE_NAMESвcore/agent/prompts.py. - Добавьте её правила множественного числа в
NUMERUS_RULESвtools/qm.py— неизвестный язык компилятор не угадывает, а отказывается собирать. - Добавьте язык в
LANGUAGESвtools/update_translations.py, запустите её, переведите созданный.ts.