Интернационализация и локализация
Лекции прошлых лет:
LecturesCMC/LinuxApplicationDevelopment2012/09
на сайте esyr.org (на фото — не лектор )
Термины
- Локализация (l10n) — адаптация культурных и языковых предпочтений ПО
- Интернационализация (i18n) — приведение исходного кода в готовое для локализации состояние
Объекты ПО
- Сообщения и тексты в составе ПО
- Размер и оформление интерфейсных элементов (RTL, например)
- Изображения: текст на них и культурная атрибуция
Управляющие клавиши (иероглифы, ага )
- Параметры, чувствительные к локали (LANG=ru_RU.KOI8-R LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION LC_ALL)
- Ссылки на внешние ресурсы (поддержка, издатель и пр.; телефоны, url и пр.)
- (в сложных случаях) Шрифты
Другие объекты продукта
- Электронная документация
- Непрограммные данные (шаблоны документов и форм и т. п.)
- Бумажная документация и др. сопутствующие предметы
Использование GNU Gettext
В случае Python (в скобках указаны названия утилит, конкретные действия будут показаны позже)
Создание перевода
- I18n:
Подключим в текущий namespace волшебную функцию «_()» — «перевести»
- Обмажем этой функцией все строки, нуждающиеся в переводе
Внимание! Новомодные (им, вообще-то уже больше года) f-strings (форматные строки вида «f"...{имя}..."») по понятным причинам в python gettext не поддерживаются. Пользуйтесь методом .format()!
Не удаляйте комментарий #, python-brace-format из po-файла, он нужен:)
(xgettext) Создадим на основе i18n-ванного исходного кода шаблон перевода, файл dopmain.pot (domain — это довольно произвольное название того, что мы переводим)
(msginit) Создадим прототип перевода (для русского — ru.po)
(текстовый редактор или специализированный инструмент редактирования .po) Переведём все строки в ru.po
(msgfmt) Скомпилируем перевод в файл ru.mo
Обновление перевода
Вышла новая версия программы (или сами поправили). Там «поехали» строки с сообщениями (появились новые, пропали/изменились/переместились старые).
(xgettext) Сгенерируем новый шаблон
(msgmerge) Обновим содержимое ru.po на основании шаблона и старого ru.po. У msgmerge много искусственного мозга:
- Не теряет старые переводы (только комментирует)
Размечает новые сообщения возможными переводами из старых/законмментированных
Допереводим ru.po
(msgfmt) Компилируем новый перевод
Процесс локализации
Попробуем перевести пример из документации TkInter
Первичный перевод
ещё упростим и добавим картинку (её тоже надо переводить!)
вот такое добавление gettext ничего в работе не поменяет: перевода не найдётся, но функция _() появится, и будет возвращать непереведённые строки
Обратите внимание на то, что все строки, которые нужно переводить, заданы как "что-то там", а которые не нужно — как 'что-то там'
например, в vim достаточно такой команды :
:1,$s/\("[^"]*"\)/_(\1)/g
Используем GNU getttext или простой аналог на python3 (в каталоге /usr/lib64/python3.7/Tools/i18n)
Вынимаем из .py-файлов все конструкции вида _("строка") и формируем файл app.pot (шаблон перевода):
$ /usr/lib64/python3.7/Tools/i18n/pygettext.py -d app *.py
(примерно то же самое получается при использовании утилиты xgettext)
Получится файл, в котором некоторые части надо ещё заполнить (раскраска редактора vim):
Из этого файла можно сделать заготовку для перевода — app.po, в которой часть поле заполнит для нас утилита msginit, которая отыщет .pot-файл самостоятельно:
$ msginit -l ru_RU.UTF8 В новом каталоге сообщений должен быть указан адрес вашей электронной почты, чтобы пользователи могли присылать свои замечания по поводу ваших переводов, а также чтобы сопроводители программ могли связаться с вами на случай, если возникнут непредвиденные технические проблемы. Which is your email address? 1 george@altlinux.org 2 george@inspiron Please choose the number, or enter your email address. frbrgeorge@gmail.com A translation team for your language (ru) does not exist yet. If you want to create a new translation team for ru, please visit http://www.iro.umontreal.ca/contrib/po/HTML/teams.html http://www.iro.umontreal.ca/contrib/po/HTML/leaders.html http://www.iro.umontreal.ca/contrib/po/HTML/index.html Создано ru.po.
Ключ -d domain на самом деле определяет не имя файла, а т. н. домен — один из трёх ключей, (язык, тип, домен), по которым происходит поиск перевода. Просто в Linux это превращается в путь к файлу /usr/share/locale/язык/тип/домен.mo. Наша программа не будет ходить в /usr/share/locale, вместо этого переводы ожидаются в подкаталоге ru/LC_MESSAGES того каталога, откуда запущена программа (см. объект datapath)
- Создадим этот подкаталог и скомпилируем туда наш перевод
$ mkdir -p ru/LC_MESSAGES $ /usr/lib64/python3.7/Tools/i18n/msgfmt.py -o ru/LC_MESSAGES/app.mo ru.po
(можно было воспользоваться утилитой msgfmt)
- Создадим этот подкаталог и скомпилируем туда наш перевод
Редактировать .po-файл перевод можно очень разными редакторами (многие IDE поддерживают работу с переводами). Я пользуюсь vim + `vim-plugin-po-after-ftplugin
Обратите внимание на то, что имя файла с картинкой также «переведено» (русифицированный вариант картинки)
Обновление перевода
Допустим, мы изменили сообщение а программе
При этом .pot-файл тоже изменился:
#: app.py:19 -msgid "Feet to Meters" +msgid "Feet to meters" msgstr ""
Для сборки обновлённого перевода из (1) имеющегося перевода и (2) нового шаблона используется утилита msgmerge:
$ msgmerge -Uv ru.po app.pot .. Прочитано сообщений: старых 1 + новых 1; объединено 6, помечено неточными 1, недостающих 0, недействительных 0.
Т. н. «неточные переводы» помечаются значащим комментарием fuzzy.
При компиляции эти переводы не попадают в .mo-файл, но и не исчезают
- Неиспользованные переводы (которые были в старой версии, но отсутствуют в шаблоне), не удаляются, а комментируются
Дальнейшая работа с переводом — повторение цикла pygettext (или xgettext) → msgmerge → правка ru.po → msgfmt
Про память переводов
Очень важно соблюдать единообразие при переводе одинаковых надписей. Для этого все переводы по проекту можно сложить в т. н. compendium (но же translation memory, она же память переводов) — большой .po-файл, получаемый утилитой msgcat из всех имеющихся
Если в исходном проекте одинаковые надписи значат разное (например, «Copy» в одном месте — «Копировать», а в другом «Копия») — это плохой дизайн ,надписи надо менять
- Надписи типа «Save as…» или «Press button to continue» не придётся переводить по многу раз
- Появится больше автоматически подобранных fuzzy-переводов
- Фактически это будет ещё и словарь терминологии, который можно править при необходимости
Множественные формы
Наш перевод не слишком-то хорош: футы и метры показываются во множественном числе, независимо от значения полей. На самом деле это свойство не перевода, а исходного текста. Используем gettext для исправления ситуации.
Подготовим код: вынесем обновление меток в отдельную функцию
- Файл перевода сильно изменился, но ни одной строчки не «отъехало»
Воспользуемся функцией ngettext(), которая в английском варианте различает форму единственного и множественного числа (эту функцию надо отдельно попросить добавить в пространство имён при gettext.install())
- Убедимся, что в локали «C» для 1 фута выводится «foot», для 4 футов — «meter»)
$ LC_ALL=C python3 app.py
- Убедимся, что в локали «C» для 1 фута выводится «foot», для 4 футов — «meter»)
К сожалению, pygettext не умеет распознавать множественные формы, так что воспользуемся, наконец, утилитой xgettext:
$ xgettext -d app -o app.pot *.py $ msgmerge -Uv ru.po app.pot .. Прочитано сообщений: старых 1 + новых 1; объединено 5, помечено неточными 1, недостающих 1, недействительных 1. $ msgfmt -o ru/LC_MESSAGES/app.mo ru.po
Обратите внимание на работу «мозга» msgmerge: часть сообщений было сочтено fuzzy (и справедливо!), а часть — непереведёнными
Русский перевод имеет 3 множественные формы — единственное число, немного (2-4), и много (5 и больше), что и отразилось в ru.po
допереведём и проверим, что всё работает (футы: 0, 1, 2, 7, 777)
Описание того, какие числа какой множественной форме соответствуют содержится в заголовке ru.po (который нам сгенерировал msginit). Это описание компилируется в некоторый код .mo-файла, который gettext интерпретирует (замечание vslutov@, спасибо).
Д/З
В семестровом проекте должен присутствовать перевод. Если Вы пишете какой-то модуль/бот/библиотеку без UI, переведите документацию (см. Руководство по переводу)