Учебник¶
Эта страница проходит путь от пустой директории до программы, которая здоровается по-японски. Пять шагов, опыт работы с gettext не требуется, и каждая команда показана с выводом, который она действительно производит, — так что на каждом шаге вы знаете, всё ли идёт по плану.
Нужен Python 3.14 или новее, потому что t-строки — новый синтаксис в 3.14.
Японский — язык-пример этой страницы, но от этого выбора ничего не зависит.
Чтобы взять другой язык, замените ja на шаге 4 — этот код локали
единственное, что его называет.
1. Установка¶
Extra [babel] устанавливает Babel — инструмент, который на шаге 3 собирает
ваши сообщения в файлы каталогов. Это инструмент времени разработки: код в
продакшене рендерит только средствами стандартной библиотеки.
2. Пометьте сообщение в коде¶
Создайте app.py:
t"Hello {name}" выглядит как f-строка, но префикс t сохраняет текст и
значение раздельно, вместо того чтобы сразу их объединить. Именно это
разделение позволяет tr() найти перевод целого предложения Hello {name} и
подставить значение потом.
Запустите прямо сейчас:
Переводы ещё не установлены, поэтому исходный текст выводится как есть. Программе, использующей эту библиотеку, каталог никогда не обязателен для запуска: английский (или любой другой ваш исходный язык) — встроенный fallback.
3. Извлеките сообщения¶
Переводчики обычно работают с каталогами, а не с исходным кодом, поэтому между вами и ними путешествует небольшой файл — каталог. Первый шаг к нему — собрать из кода все помеченные сообщения.
Сообщите Babel, где искать сообщения, создав babel.cfg:
Затем извлеките их в файл-шаблон (.pot):
$ mkdir -p locales
$ pybabel extract -F babel.cfg -c "Translators:" -o locales/messages.pot .
extracting messages from app.py (encoding="utf-8")
writing PO template file to locales/messages.pot
Теперь locales/messages.pot содержит по одной записи на сообщение:
msgid — ключ, по которому будет искать ваш код. Пустой msgstr — место для
перевода, но не в этом файле: .pot — это шаблон, и следующий шаг копирует
его по одному разу на язык.
4. Переведите и скомпилируйте¶
Создайте японский каталог из шаблона:
$ pybabel init -i locales/messages.pot -d locales -l ja
creating catalog locales/ja/LC_MESSAGES/messages.po based on locales/messages.pot
Откройте locales/ja/LC_MESSAGES/messages.po и заполните msgstr:
Оставьте {name} ровно таким, как есть: заполнитель — это то, как значение
находит своё место внутри переведённого предложения, и перевод волен
переместить его туда, куда требует целевой язык. В реальном проекте именно
этот .po-файл вы передаёте переводчику или загружаете на платформу
перевода; формат в обоих случаях один и тот же.
Каталоги редактируются как текст, но загружаются в двоичной форме (.mo),
поэтому скомпилируйте:
$ pybabel compile -d locales
compiling catalog locales/ja/LC_MESSAGES/messages.po to locales/ja/LC_MESSAGES/messages.mo
Эта команда — ещё и страховка. Если бы перевод повредил заполнитель —
скажем, {nome} вместо {name}, — она отказалась бы его пропустить:
$ pybabel compile -d locales
error: locales/ja/LC_MESSAGES/messages.po:24: translation does not match the
source placeholders: {name} is missing; {nome} is not in the source message
1 errors encountered.
Одна оговорка, о которой стоит знать уже сейчас: она сообщает об ошибке и
завершается с ненулевым кодом, но .mo всё равно записывает. На реальном
проекте останавливаться по этому коду возврата должен CI —
В продакшене это настраивает.
5. Запустите¶
Шаги 2–4 использовали tr(), который ищет каталог и не находит ни одного.
Теперь, когда каталог есть, загрузите его и привяжите один раз: Translator
держит каталог, чтобы места вызова не должны были его называть, а _ —
традиционное в gettext имя для результата.
Направьте app.py на скомпилированный каталог. Щёлкните по маркерам, чтобы
увидеть, что делает каждая строка:
import gettext
from gettext_tstrings import Translator
_ = Translator(gettext.translation("messages", localedir="locales", languages=["ja"])) # (1)!
name = "Ada"
print(_(t"Hello {name}")) # (2)!
- Стандартная библиотека загружает скомпилированный
.mo, аTranslatorпривязывает его к вызываемому объекту._— традиционное в gettext имя для «переведи это», короткое, потому что оно стоит у каждой видимой пользователю строки. Он выполняет тот же перевод, что иtr, привязанный к одному каталогу. - В момент вызова: текст t-строки становится ключом поиска
Hello {name}, каталог отвечаетこんにちは {name}, ответ проверяется по заполнителям источника, и только после этого подставляется значение.
Вот и весь цикл, и его стоит увидеть одной картиной:
flowchart LR
mark["1–2 пометить<br>t-строки в коде"] --> extract["3 извлечь<br>messages.pot"]
extract --> translate["4 перевести<br>ja/…/messages.po"]
translate --> compile["4 скомпилировать<br>ja/…/messages.mo"]
compile --> run["5 запустить<br>こんにちは Ada"]
Пометить → извлечь → перевести → скомпилировать → запустить. Всё остальное на этом сайте — уточнение одного из этих пяти шагов.
Что дальше¶
- Зачем нужны t-строки — от чего защищает этот дизайн по
сравнению с
%(name)s,.format()и$-строками. - Руководство — множественное число, язык запроса, отложенные строки и что происходит во время выполнения, если каталог всё-таки неверен.
- В продакшене — тот же цикл, как его ведёт команда неделя за неделей: обновление каталогов, ворота CI и платформы перевода.
- Извлечение — полный справочник по
pybabel: собственные имена функций, строгий режим для CI и проверки, охраняющие ваши каталоги. - Миграция — если в проекте, где вы на самом деле хотите всё это применить, уже есть каталоги gettext.
- Переводчикам — одна страница, которую можно вручить тому,
кто заполняет строки
msgstr.