Интернационализация и локализация
Лекции прошлых лет:
2007, на сайте esyr.org (лекцию читал Андрей Черепанов, а на фото — не он ☺)
Разработка на Python 2024 (по Sphinx)
Термины
- Интернационализация (i18n) — приведение исходного кода в готовое для локализации состояние
- Локализация (l10n) — адаптация культурных и языковых предпочтений ПО
Объекты ПО
- Сообщения и тексты в составе ПО
- Словоформы (в первую очередь — множественные)
- Размер и оформление интерфейсных элементов (RTL, например)
- Изображения: текст на них и культурная атрибуция
Управляющие клавиши (иероглифы, ага )
Параметры, чувствительные к локали (команда locale)
- Ссылки на внешние ресурсы (поддержка, издатель и пр.; телефоны, url и пр.)
- (в сложных случаях) Шрифты
Другие объекты продукта
- Электронная документация
- Непрограммные данные (шаблоны документов и форм и т. п.)
- Бумажная документация и др. сопутствующие предметы
Локализация: инструменты
- Общие словари (?)
- Память перевода (локальные общие словари)
- Нечёткий перевод (при лёгком изменении ресурса или при добавлении похожего)
- … что ещё?
GNU Gettext
Создание перевода
I18n: Обмажем функцией gettext() все строки, нуждающиеся в переводе
Есть ещё ngettext(), см. ниже
xgettext: Создадим на основе i18n-ванного исходного кода шаблон перевода, файл domain.pot (domain — это довольно произвольное название того, что мы переводим)
Не удаляйте сгенерированные комментарии из файла, это не комментарии☺!
msginit: Создадим прототип перевода (для русского — ru.po)
(текстовый редактор или специализированный инструмент редактирования .po) Переведём все строки в ru.po
msgfmt: Скомпилируем перевод в файл ru.mo
Обновление перевода
Вышла новая версия программы (или сами поправили). Там «поехали» строки с сообщениями (появились новые, пропали/изменились/переместились старые).
xgettext: Сгенерируем новый шаблон
msgmerge: Обновим содержимое ru.po на основании шаблона и старого ru.po. У msgmerge много искусственного мозга:
- Не теряет старые переводы (только комментирует)
Размечает новые сообщения возможными (fuzzy) переводами из старых/закомментированных
Умеет в примитивную память переводов
Допереводим ru.po
msgfmt: Компилируем новый перевод
Есть ещё вспомогательные инструменты:
$ rpm -ql gettext-tools | grep /bin/ | cut -d/ -f4 | xargs -n1 whatis autopoint (1) - copies standard gettext infrastructure gettextize (1) - install or upgrade gettext infrastructure msgattrib (1) - attribute matching and manipulation on message catalog msgcat (1) - combines several message catalogs msgcat (n) - Tcl message catalog msgcmp (1) - compare message catalog and template msgcomm (1) - match two message catalogs msgconv (1) - character set conversion for message catalog msgen (1) - create English message catalog msgexec (1) - process translations of message catalog msgfilter (1) - edit translations of message catalog msgfmt (1) - compile message catalog to binary format msggrep (1) - pattern matching on message catalog msginit (1) - initialize a message catalog msgmerge (1) - merge message catalog and template msgunfmt (1) - uncompile message catalog from binary format msguniq (1) - unify duplicate translations in message catalog recode-sr-latin (1) - convert Serbian text from Cyrillic to Latin script xgettext (1) - extract gettext strings from source
Ход работы
Преобразовать текст на Си (собственно i18n)
Традиционный макрос _() (не обязателен)
#include <libintl.h> #include <locale.h> #define _(STRING) gettext(STRING)
В main()-е
setlocale (LC_ALL, "") bindtextdomain ("домен", "путь к локализации"); textdomain ("домен");
Во всей программе заменить строки "Something..." на _("Something...") (вызов gettext())
- Создание шаблона (шаблон — это генерат)
$ xgettext --keyword=_ исходник -o шаблон.pot
Документация xgettext
Если не использовать «_», то и --keyword не нужен
- Создание первоначального пустого перевода
$ msginit -i шаблон.pot -l локаль -o перевод.po
Документация msginit
Большинство комментариев — значащие (например, "#, c-format"), лучше их не трогать
- Некоторые поля следует заполнить заранее (vim выделяет их жёлтым)
- Перевод
Строки msgstr — это переводы (поначалу пустые)
- Компиляция перевода (нужна для интерпретации формулы множественного числа)
$ msgfmt -o скомпилированный_перевод.mo перевод.po
- Обновление перевода
Перегенерация шаблона (см. выше, точно той же командой xgettext)
- Обновление старого перевода на основании нового шаблона
$ msgmerge -U старый_перевод.po новый_щаблон.pot
- При этом;
- Совсем удалённые сообщения в переводе комментируются (но не удаляются — это translation memory!)
Некоторые новые сообщения не переведены, для других подобраны похожие переводы, и помечены #, fuzzy. Fuzzy-строки считаются непереведёнными, для того, чтобы они вступили в силу, эту часть комментария (и только ее!) надо удалить
Варианты «пути к локализации»
Локальный — положить рядом с бинарником или в подкаталог po/
Так в Linux не принято!
Системный — положить в /usr/share/locale и/или в /usr/local/share/locale
Требует прав администратора и средств деинсталляции
Микс — предусмотреть два варианта сборки, определить переменную в configure и т. д.
С относительным путём: сформировать путь относительно местоположения бинарника
если бинарник ставится в /usr/bin, а переводы — в /usr/share/locale
то путь получится ../share/locale
выполнить тестовую установку куда-нибудь в /tmp или /home/пользователь/что-то-там
при этом бинарник должен ставиться в /home/пользователь/что-то-там/bin/программа
- оттуда и запускать
а путь до локали /home/пользователь/что-то-там/share/locale/… остаётся тот же самый: ../share/locale
Использование множественных форм
В английском их две, в русском — три: 1¹ / 2,3,4² / 5-20³ / 21¹ и т. д.
функция ngettext("Строка", "Строка для множественной формы", сколько)
После обновления в русском переводе — три msgstr[] — для каждой из трёх форм
Страшная формула в строке Plural-Forms:
Общий вид: "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2\n"
nplurals=3 три словоформы, далее идёт C-style выражение:
plural=n%10==1 && n%100!=11 ? 0 — единственное число
: n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 — малое число
: 2 — иначе большое число
Если увеличить nplurals до 4 и добавить в начало формулы n==1? 3 :, получим четвёртую словоформу — для ровно одного объекта (в таких случаях можно число 1 не выводить)
Translation memory
Закомментированные переводы тоже используются (при полном совпадении или как fuzzy при неполном)
При составлении перевода msgmerge можно подключать файлы с другими переводами (ключ -C)
gettext и autotools
Есть поддержка, она в первую очередь имеет смысл для больших проектов
Требует установки пакета intltool
- Приносит довольно много заданного заранее workflow (как и любой мета инструмент сборки)
Вся работа с переводом заключается в запуске intltool-update язык, и каталог po полностью управляется автоматикой
Используется относительная схема размещения локализации (см. выше)
Используется autopoint (это «auto po intl», а не «auto point» ☺)
TODO написать методичку из примера ниже
Если увидите методичку с упоминанием явного запуска утилиты gettextize — она устарела
Что хранить и что не хранить в GIT-е
Пример
Для ALT надо ставить gettext и gettext-tools
Для поддержки со стороны autotools — ещё intltool
В этом примере изначально autotools использовались для всего, кроме переводов.
Так проще объяснить, что происходит, все команды видны в Makefile.am
- Так легче читать (и писать тоже, пока у вас один файл на си и один перевод!)
Формально говоря, это неправильный пример: общим каталогом для данных всего проекта объявляется каталог для русских переводов ☺
Под конец пример переключается на autotools
- Пример стал универсальным, можно добавлять переводы
- В качестве бонуса реализовано единственное число (по методу выше)
После каждого коммита я делал
git clean -fd
autoreconf -fisv && ./configure && make && ./ahello
Поддержка Gettext в других системах сборки
Д/З
За неимение лучшего почитать пример
Написать довольно тупую программу на Си, которая угадывает число 1…100 методом половинного деления
- Первым делом просит загадать число от 1 до 100
- Затем спрашивает «число больше 50?» (ответ тоже надо переводить)
- На основании ответа выбирает пол-интервала и повторяет его по циклу
- Когда догадается — отвечает
Желающие могут реализовать проверку ошибок ввода (подсказка: почитайте scanf на предмет возвращаемого значения)
- Оформить автосборку вокруг неё (autotools/CMake/Meson/как хотите)
- Добавить перевод
- Добавить
- Либо поддержку перевода с помощью autotools как в конце репозитория с примерами
- Либо автоматическое обновление/пересборку перевода и удаление генератов (как ранее в том же репозитории)
Обратите внимание: .po-файл (а в примере до перехода на autotools ещё заголовочная часть .pot-файла) — не генераты; .mo, временный .pot и прочее — генераты
Положить результат в каталог (извините! ☺) 08_I18n отчётного репозитория