Рубрики
Без рубрики

gettext – Каталоги сообщений

Автор оригинала: Doug Hellmann.

Цель:

API каталога сообщений для интернационализации.

Модуль gettext предоставляет реализацию на чистом Python, совместимую с библиотекой GNU gettext для перевода сообщений и управления каталогом. Инструменты, доступные с исходным кодом Python, позволяют извлекать сообщения из набора исходных файлов, создавать каталог сообщений, содержащий переводы, и использовать этот каталог сообщений для отображения соответствующего сообщения пользователю во время выполнения.

Каталоги сообщений могут использоваться для обеспечения интернационализированных интерфейсов для программы, показывая сообщения на языке, подходящем для пользователя. Их также можно использовать для других настроек сообщений, включая «создание скинов» интерфейса для различных оболочек или партнеров.

Примечание

Хотя в стандартной документации библиотеки говорится, что все необходимые инструменты включены в Python, pygettext.py не удалось извлечь сообщения, заключенные в вызов ngettext , даже с соответствующими параметрами командной строки. В этих примерах вместо этого используется xgettext из набора инструментов GNU gettext .

Обзор рабочего процесса перевода

Процесс настройки и использования переводов состоит из пяти шагов.

Определите и пометьте буквальные строки в исходном коде, содержащие сообщения для перевода.

Начните с определения сообщений в исходном коде программы, которые необходимо перевести, и маркировки буквальных строк, чтобы программа извлечения могла их найти.

Извлеките сообщения.

После того как переводимые строки в источнике определены, используйте xgettext , чтобы извлечь их и создать файл .pot или шаблон перевода . Шаблон представляет собой текстовый файл с копиями всех идентифицированных строк и заполнителями для их переводов.

Переведите сообщения

Передайте копию файла .pot переводчику, изменив расширение на .po . Файл .po – это редактируемый исходный файл, используемый в качестве входных данных для этапа компиляции. Переводчик должен обновить текст заголовка в файле и предоставить переводы для всех строк.

“Составьте” каталог сообщений из перевода.

Когда переводчик отправит обратно заполненный файл .po , скомпилируйте текстовый файл в формате двоичного каталога с помощью msgfmt . Двоичный формат используется кодом поиска в каталоге среды выполнения.

Загрузите и активируйте соответствующий каталог сообщений во время выполнения.

Последний шаг – добавить в приложение несколько строк для настройки и загрузки каталога сообщений и установки функции перевода. Есть несколько способов сделать это с соответствующими компромиссами.

В оставшейся части этого раздела мы рассмотрим эти шаги более подробно, начиная с необходимых модификаций кода.

Создание каталогов сообщений из исходного кода

gettext работает путем поиска буквенных строк в базе данных переводов и извлечения соответствующей переведенной строки. Обычный шаблон – привязать соответствующую функцию поиска к имени « _ » (одиночный символ подчеркивания), чтобы код не был загроможден множеством вызовов функций с более длинными именами.

Программа извлечения сообщений, xgettext , ищет сообщения, встроенные в вызовы функций поиска в каталоге. Он понимает разные исходные языки и для каждого использует соответствующий синтаксический анализатор. Если функции поиска имеют псевдонимы или добавляются дополнительные функции, дайте xgettext имена дополнительных символов, которые следует учитывать при извлечении сообщений.

В этом сценарии есть одно сообщение, готовое к переводу.

gettext_example.py

import gettext

# Set up message catalog access
t  gettext.translation(
    'example_domain', 'locale',
    fallbackTrue,
)
_  t.gettext

print(_('This message is in the script.'))

Текст «Это сообщение находится в скрипте.» – это сообщение, которое нужно заменить из каталога. Включен резервный режим, поэтому, если сценарий запускается без каталога сообщений, печатается встроенное сообщение.

$ python3 gettext_example.py

This message is in the script.

Следующим шагом является извлечение сообщения и создание файла .pot с помощью pygettext.py или xgettext .

$ xgettext -o example.pot gettext_example.py

Созданный выходной файл содержит следующее содержимое.

example.pot

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR , YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-03-18 16:20-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain;>\n"
"Content-Transfer-Encoding: 8bit\n"

#: gettext_example.py:19
msgid "This message is in the script."
msgstr ""

Каталоги сообщений устанавливаются в каталоги, организованные по домену и языку . Домен предоставляется приложением или библиотекой и обычно представляет собой уникальное значение, такое как имя приложения. В этом случае домен в gettext_example.py – это example_domain . Значение языка предоставляется средой пользователя во время выполнения через одну из переменных среды LANGUAGE , LC_ALL , LC_MESSAGES или LANG. в зависимости от их конфигурации и платформы. Все эти примеры были выполнены с языком, установленным на en_US .

Теперь, когда шаблон готов, следующим шагом будет создание необходимой структуры каталогов и копирование шаблона в нужное место. Каталог locale внутри исходного дерева PyMOTW будет служить корнем каталога каталога сообщений для этих примеров, но обычно лучше использовать каталог, доступный для всей системы, чтобы все пользователи имели доступ к каталоги сообщений. Полный путь к источнику ввода каталога – $ localedir/$ language/LC_MESSAGES/$ domain.po , а фактический каталог имеет расширение имени файла .mo .

Каталог создается путем копирования example.pot в locale/en_US/LC_MESSAGES/example.po и его редактирования для изменения значений в заголовке и установки альтернативных сообщений. Результат показан далее.

locale/en_US/LC_MESSAGES/example.po

# Messages from gettext_example.py.
# Copyright (C) 2009 Doug Hellmann
# Doug Hellmann , 2016.
#
msgid ""
msgstr ""
"Project-Id-Version: PyMOTW-3\n"
"Report-Msgid-Bugs-To: Doug Hellmann \n"
"POT-Creation-Date: 2016-01-24 13:04-0500\n"
"PO-Revision-Date: 2016-01-24 13:04-0500\n"
"Last-Translator: Doug Hellmann \n"
"Language-Team: US English \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain;>\n"
"Content-Transfer-Encoding: 8bit\n"


#: gettext_example.py:16
msgid "This message is in the script."
msgstr "This message is in the en_US catalog."

Каталог создается из файла .po с использованием msgformat .

$ cd locale/en_US/LC_MESSAGES; msgfmt -o example.mo example.po

Домен в gettext_example.py – это example_domain , но файл называется example.pot . Чтобы gettext нашел нужный файл перевода, имена должны совпадать.

gettext_example_corrected.py

t  gettext.translation(
    'example', 'locale',
    fallbackTrue,
)

Теперь при запуске скрипта вместо встроенной строки печатается сообщение из каталога.

$ python3 gettext_example_corrected.py

This message is in the en_US catalog.

Поиск каталогов сообщений во время выполнения

Как описано ранее, каталог локали , содержащий каталоги сообщений, организован на основе языка с каталогами, названными для домена программы. Различные операционные системы определяют свои собственные значения по умолчанию, но gettext не знает всех этих значений по умолчанию. Он использует каталог локали по умолчанию sys.prefix + '/share/locale' , но в большинстве случаев безопаснее всегда явно указывать значение localedir , чем зависеть по умолчанию действителен. Функция find () отвечает за поиск соответствующего каталога сообщений во время выполнения.

gettext_find.py

import gettext

catalogs  gettext.find('example', 'locale', allTrue)
print('Catalogs:', catalogs)

Языковая часть пути берется из одной из нескольких переменных среды, которые можно использовать для настройки функций локализации ( LANGUAGE , LC_ALL , LC_MESSAGES , и LANG ). Используется первая установленная переменная. Можно выбрать несколько языков, разделив значения двоеточием (: ). Чтобы увидеть, как это работает, используйте второй каталог сообщений для проведения нескольких экспериментов.

$ cd locale/en_CA/LC_MESSAGES; msgfmt -o example.mo example.po
$ cd ../../..
$ python3 gettext_find.py

Catalogs: ['locale/en_US/LC_MESSAGES/example.mo']

$

Catalogs: ['locale/en_CA/LC_MESSAGES/example.mo']

$

Catalogs: ['locale/en_CA/LC_MESSAGES/example.mo',
'locale/en_US/LC_MESSAGES/example.mo']

$

Catalogs: ['locale/en_US/LC_MESSAGES/example.mo',
'locale/en_CA/LC_MESSAGES/example.mo']

Хотя find () показывает полный список каталогов, только первый в последовательности фактически загружается для поиска сообщений.

$ python3 gettext_example_corrected.py

This message is in the en_US catalog.

$

This message is in the en_CA catalog.

$

This message is in the en_CA catalog.

$

This message is in the en_US catalog.

Множественные значения

В то время как простая подстановка сообщения удовлетворяет большинство потребностей в переводе, gettext рассматривает множественное число как особый случай. В зависимости от языка разница между единственной и множественной формами сообщения может отличаться только окончанием одного слова, или может отличаться вся структура предложения. Также могут быть разные формы в зависимости от уровня множественности. Чтобы упростить управление множественным числом (а в некоторых случаях это стало возможным), существует отдельный набор функций для запроса формы множественного числа в сообщении.

gettext_plural.py

from gettext import translation
import sys

t  translation('plural', 'locale', fallbackFalse)
num  int(sys.argv[1])
msg  t.ngettext('{num} means singular.',
                 '{num} means plural.',
                 num)

# Still need to add the values to the message ourself.
print(msg.format(numnum))

Используйте ngettext () для доступа к подстановке множественного числа для сообщения. Аргументы – это сообщения, которые нужно перевести, и количество элементов.

$ xgettext -L Python -o plural.pot gettext_plural.py

Поскольку есть альтернативные формы для перевода, замены перечислены в массиве. Использование массива позволяет переводить на языки с несколькими формами множественного числа (например, польский язык имеет разные формы, указывающие относительное количество).

множественное число. горшок

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR , YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-03-18 16:20-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain;>\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms:>\n"

#: gettext_plural.py:15
#, python-brace-format
msgid "{num} means singular."
msgid_plural "{num} means plural."
msgstr[0] ""
msgstr[1] ""

Помимо заполнения строк перевода, библиотеке необходимо сообщить о способе формирования множественного числа, чтобы она знала, как индексировать в массив для любого заданного значения счетчика. Строка "Plural-Forms: " включает два значения, которые нужно заменить вручную. nplurals - это целое число, указывающее размер массива (количество используемых переводов) и множественное число - это выражение языка C для преобразования входящего количества в индекс в массиве при поиске перевода. Буквальная строка n заменяется количеством, переданным в ungettext () .

Например, английский язык включает две формы множественного числа. Количество 0 рассматривается как множественное число («0 бананов»). Запись Plural-Forms :

Plural-Forms: nplurals2; pluraln  1;

Тогда перевод в единственном числе будет в позиции 0, а перевод во множественном числе – в позиции 1.

locale/en_US/LC_MESSAGES/plural.po

# Messages from gettext_plural.py
# Copyright (C) 2009 Doug Hellmann
# This file is distributed under the same license 
# as the PyMOTW package.
# Doug Hellmann , 2016.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PyMOTW-3\n"
"Report-Msgid-Bugs-To: Doug Hellmann \n"
"POT-Creation-Date: 2016-01-24 13:04-0500\n"
"PO-Revision-Date: 2016-01-24 13:04-0500\n"
"Last-Translator: Doug Hellmann \n"
"Language-Team: en_US \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain;>\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: plural=n

#: gettext_plural.py:15
#, python-format
msgid "{num} means singular."
msgid_plural "{num} means plural."
msgstr[0] "In en_US, {num} is singular."
msgstr[1] "In en_US, {num} is plural."

Выполнение тестового сценария несколько раз после компиляции каталога продемонстрирует, как различные значения N преобразуются в индексы для строк перевода.

$ cd locale/en_US/LC_MESSAGES/; msgfmt -o plural.mo plural.po
$ cd ../../..
$ python3 gettext_plural.py 0

In en_US, 0 is plural.

$ python3 gettext_plural.py 1

In en_US, 1 is singular.

$ python3 gettext_plural.py 2

In en_US, 2 is plural.

Локализация приложения и модуля

Объем работ по переводу определяет, как gettext устанавливается и используется с основной частью кода.

Локализация приложений

Для переводов в масштабе приложения автор может установить такую функцию, как ngettext () , глобально с использованием пространства имен __builtins__ , поскольку он контролирует верхний уровень код приложения.

gettext_app_builtin.py

import gettext

gettext.install(
    'example',
    'locale',
    names['ngettext'],
)

print(_('This message is in the script.'))

Функция install () связывает gettext () с именем _ () в пространстве имен __builtins__ . Он также добавляет ngettext () и другие функции, перечисленные в names .

Локализация модуля

Для библиотеки или отдельного модуля изменение __builtins__ не является хорошей идеей, поскольку это может привести к конфликту с глобальным значением приложения. Вместо этого вручную импортируйте или повторно связывайте имена функций перевода в верхней части модуля.

gettext_module_global.py

import gettext

t  gettext.translation(
    'example',
    'locale',
    fallbackFalse,
)
_  t.gettext
ngettext  t.ngettext

print(_('This message is in the script.'))

Переключение переводов

Все предыдущие примеры используют один перевод на протяжении всей программы. В некоторых ситуациях, особенно в веб-приложениях, необходимо использовать разные каталоги сообщений в разное время, не выходя из среды и не перезагружая ее. В таких случаях API на основе классов, представленный в gettext , будет более удобным. Вызовы API по сути такие же, как глобальные вызовы, описанные в этом разделе, но объект каталога сообщений открыт и может управляться напрямую, так что можно использовать несколько каталогов.

Смотрите также