gettext — Многоязычные службы интернационализации

Исходный код: Lib/gettext.py


Модуль gettext обеспечивает интернационализацию (I18N) и локализация (L10N) службы для ваших модулей Python и приложений. Это поддерживает и каталог сообщения API GNU gettext и более высокий уровень, class-based API, который может более подходить для файлов Python. Интерфейс, описанный ниже, позволяет писать сообщения модуля и приложения на одном естественном языке, а также предоставлять каталог переведенных сообщений для работы под разными естественными языками.

Также приведены некоторые подсказки по локализации модулей и приложений Python.

GNU gettext API

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

gettext.bindtextdomain(domain, localedir=None)

Привязать domain к языковому каталогу localedir. Более конкретно, gettext будет искать двоичные файлы .mo для данного домена, используя путь (в Unix): localedir/language/LC_MESSAGES/domain.mo, где language является найденный для в переменных среды LANGUAGE, LC_ALL, LC_MESSAGES и LANG соответственно.

Если localedir пропущен или None, то текущий биндинг для domain является возвращенный. [1]

gettext.bind_textdomain_codeset(domain, codeset=None)

Свяжите domain с codeset, изменив кодировка байтовая строка возвращаемая функциями lgettext(), ldgettext(), lngettext() и ldngettext(). Если codeset пропущен, то возвращается текущий биндинг.

Устарело с версии 3.8, будет удалено в 3.10 версии..

gettext.textdomain(domain=None)

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

gettext.gettext(message)

Возвращает локализованный перевод message на основе текущего глобального домена, языка и языкового каталога. Эта функция обычно псевдонимуется как _() в пространстве имен локальная (см. примеры ниже).

gettext.dgettext(domain, message)

Как gettext(), но смотрите сообщение вверх в указанном domain.

gettext.ngettext(singular, plural, n)

Как и gettext(), но рассмотрим формы множественного числа. Если перевод найден, примените формулу множественного числа к n и возвращает результирующее сообщение (некоторые языки имеют более двух форм множественного числа). Если перевод не найден, возвращает singular if n равно 1; возвращает plural в противном случае.

Формула множественного числа берется из заголовка каталога. Это выражение C или Python, имеющее свободную переменную n; выражение вычисляется как индекс множественного числа в каталоге. См. документация по gettext для GNU для точного синтаксиса, чтобы быть используемый в файлах .po и формулах для множества языков.

gettext.dngettext(domain, singular, plural, n)

Как ngettext(), но смотрите сообщение вверх в указанном domain.

gettext.pgettext(context, message)
gettext.dpgettext(domain, context, message)
gettext.npgettext(context, singular, plural, n)
gettext.dnpgettext(domain, context, singular, plural, n)

Аналогично соответствующим функциям без p в префиксе (то есть gettext(), dgettext(), ngettext(), dngettext()), но трансляция ограничена данным сообщением context.

Добавлено в версии 3.8.

gettext.lgettext(message)
gettext.ldgettext(domain, message)
gettext.lngettext(singular, plural, n)
gettext.ldngettext(domain, singular, plural, n)

Эквивалентный соответствующим функциям без префикса l (gettext(), dgettext(), ngettext() и dngettext()), но перевод возвращенный как строка кодированный байта в предпочтительной системе кодировка, если никакой другой кодировка не был явно установлен с bind_textdomain_codeset().

Предупреждение

Этих функций следует избегать в Python 3, поскольку они возвращает кодированный байт. Намного лучше использовать альтернативы, какой возвращает Юникод строки вместо этого, так как большинство заявлений Python захочет управлять человекочитаемым текстом как строки вместо байтов. Далее, возможно, что вы можете получить неожиданные Юникод-связанные исключения, если есть проблемы кодировка с переведенным строки.

Устарело с версии 3.8, будет удалено в 3.10 версии..

Обратите внимание, что GNU gettext также определяет метод dcgettext(), но это считали не полезным и таким образом, он в настоящее время не осуществлен.

Вот пример типичного использования этого API:

import gettext
gettext.bindtextdomain('myapplication', '/path/to/my/language/directory')
gettext.textdomain('myapplication')
_ = gettext.gettext
# ...
print(_('This is a translatable string.'))

Основанный на классах API

class-based API модуля gettext дает вам больше гибкости и большего удобства, чем GNU gettext API. Это рекомендуемый способ локализации приложений и модулей Python. gettext определяет класс GNUTranslations, который осуществляет парсинг файлов формата GNU .mo и имеет методы для возвращения строки. сущности этого класса могут также устанавливать себя во встроенное пространство имен в качестве функции _().

gettext.find(domain, localedir=None, languages=None, all=False)

Эта функция реализует стандартный алгоритм поиска файлов .mo. Требуется domain, идентичное тому, что занимает textdomain(). Дополнительный localedir как в bindtextdomain(). Необязательный languages - это список строки, где каждый строка является языком код.

Если localedir не дан, то системный справочник места действия по умолчанию - используемый. [2], если languages не дан, то следующие переменные окружения - найденный: LANGUAGE, LC_ALL, LC_MESSAGES и LANG. Первая, возвращающая непустую значение, является используемый для переменной languages. Переменные среды должны содержать список языков, разделенных двоеточием, который будет разделен на двоеточие для получения ожидаемого списка языков код строки.

find() затем расширяет и нормализует языки, а затем выполняет их итерацию с поиском существующего файла, построенного из следующих компонентов:

localedir/language/LC_MESSAGES/domain.mo

Первым такое имя файла, которое существует, является возвращенный find(). Если такой файл не найден, то None является возвращенный. Если all дан, он возвращает список всех имен файлов в заказе, в котором они появляются в языковом списке или переменных окружения.

gettext.translation(domain, localedir=None, languages=None, class_=None, fallback=False, codeset=None)

Возвращает *Translations сущность на основе domain, localedir и languages, которые сначала передаются find() для получения списка связанных путей к файлу .mo. сущности с идентичными именами файлов .mo кэшируются. Фактический экземпляр класса class_, если предоставляется, в противном случае GNUTranslations. Конструктор класса должен принимать один аргумент файловый объект. В случае предоставления, codeset изменит charset используемый для кодирования преобразованных строки в методах lgettext() и lngettext().

Если несколько файлов найдены, более поздние файлы - используемый как отступления для более ранних. Чтобы разрешить настройку резервного копирования, copy.copy() используемый клонировать каждый объект перевода из кэш; фактические сущность данные по-прежнему используются совместно с кэш.

Если файл .mo не найден, эта функция вызывает OSError, если fallback имеет значение false (которое является значением по умолчанию), и возвращает NullTranslations сущность, если fallback имеет значение true.

Изменено в версии 3.3: IOError используемый будет поднят вместо OSError.

Устарело с версии 3.8, будет удалено в 3.10 версии.: Параметр codeset.

gettext.install(domain, localedir=None, codeset=None, names=None)

Это устанавливает функцию _() в пространстве имен Python’s builtins, на основе domain, localedir и codeset, которые переданы к функции translation().

Для параметра names, пожалуйста, см. описание метода объекта перевода install().

Как показано ниже, вы обычно помечаете строки в приложении, которые являются кандидатами на перевод, заключив их в вызов функции _(), как это:

print(_('This string will be translated.'))

Для удобства вы хотите, чтобы функция _() была установлена в пространстве имен Python’s builtins, таким образом, это легкодоступно во всех модулях вашего заявления.

Устарело с версии 3.8, будет удалено в 3.10 версии.: Параметр codeset.

Класс NullTranslations

Классы перевода - это то, что фактически реализует перевод сообщения исходного файла строки в переведенное сообщение строки. Базовым классом используемый по всем классам перевода является NullTranslations; это обеспечивает базовый интерфейс, который можно использовать для написания собственных специализированных классов перевода. Вот методы NullTranslations:

class gettext.NullTranslations(fp=None)

Принимает необязательный файловый объект fp, который игнорируется базовым классом. Инициализирует «защищенные» переменные сущность _info и _charset, которые установлены производными классами, а также _fallback, который установлен через add_fallback(). Затем он вызывает self._parse(fp), если fp не является None.

_parse(fp)

No-op в базовом классе этот метод принимает объект файла fp и считывает данные из файла, инициализируя его каталог сообщений. При наличии неподдерживаемого формата файла каталога сообщений следует переопределить этот метод для синтаксического анализа формата.

add_fallback(fallback)

Добавьте fallback в качестве резервного объекта для текущего объекта перевода. Объект перевода должен обращаться к резервному запросу, если он не может предоставить перевод для данного сообщения.

gettext(message)

Если отступление было установлено, отправьте gettext() отступлению. В противном случае, возвращает message. Переопределено в производных классах.

ngettext(singular, plural, n)

Если отступление было установлено, отправьте ngettext() отступлению. В противном случае, возвращает singular если n равно 1; возвращает plural в противном случае. Переопределено в производных классах.

pgettext(context, message)

Если отступление было установлено, отправьте pgettext() отступлению. В противном случае возвращает переведенное сообщение. Переопределено в производных классах.

Добавлено в версии 3.8.

npgettext(context, singular, plural, n)

Если отступление было установлено, отправьте npgettext() отступлению. В противном случае возвращает переведенное сообщение. Переопределено в производных классах.

Добавлено в версии 3.8.

lgettext(message)
lngettext(singular, plural, n)

Эквивалентно gettext() и ngettext(), но перевод возвращенный в качестве байта строка кодированный в предпочтительной системе кодировка, если ни один кодировка не был явно установлен с set_output_charset(). Переопределено в производных классах.

Предупреждение

Эти способы следует избегать в Python 3. См. предупреждение для функции lgettext().

Устарело с версии 3.8, будет удалено в 3.10 версии..

info()

Возвращает защищенную переменную _info, словарь, содержащий метаданные, найденные в файле каталога сообщений.

charset()

Возвращает кодировку файла каталога сообщений.

output_charset()

Возвращает кодировку используемый к возвращает перевел сообщения в lgettext() и lngettext().

Устарело с версии 3.8, будет удалено в 3.10 версии..

set_output_charset(charset)

Измените кодировка используемый на возвращает переведенных сообщений.

Устарело с версии 3.8, будет удалено в 3.10 версии..

install(names=None)

Этот метод устанавливает gettext() во встроенное пространство имен, биндинг его в _.

Если параметр names дан, это должна быть последовательность, содержащая названия функций, которые вы хотите установить в builtins пространстве имен в дополнение к _(). Поддерживаются следующие имена: 'gettext', 'ngettext', 'pgettext', 'npgettext', 'lgettext' и 'lngettext'.

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

import gettext
t = gettext.translation('mymodule', ...)
_ = t.gettext

Это помещает _() только в глобальное пространство имен модуля и поэтому влияет только на вызовы внутри этого модуля.

Изменено в версии 3.8: Добавлены 'pgettext' и 'npgettext'.

Класс GNUTranslations

Модуль gettext предоставляет один дополнительный класс, производный от NullTranslations: GNUTranslations. Этот класс отвергает _parse(), чтобы включить читать файлы GNU gettext формата .mo и в big-endian и в ittle-endian формате.

GNUTranslations анализирует дополнительные метаданные из каталога перевода. Это - конвенция с GNU gettext, чтобы включать метаданные как перевод для пустого строка. Эти метаданные находятся в RFC 822-разрабатывают пары key: value и должен содержать ключ Project-Id-Version. Если ключ Content-Type найден, свойство charset используемый инициализирует «защищенную» переменную _charset сущность, по умолчанию устанавливая значение None, если она не найдена. Если кодировка, кодировка определен, то весь ids сообщения и сообщение строки, прочитанное из каталога, преобразованы в Юникод, используя этот кодировка, еще ASCII, принята.

Так как ids сообщения прочитаны как Юникод строки также, все методы *gettext() примут ids сообщения как Юникод строки, не байт строки.

Весь набор пар key/значение помещается в словарь и устанавливается как «защищённая» переменная _info сущность.

Если магическое число файла .mo недопустимо, основной номер версии является неожиданным, или если при чтении файла возникают другие проблемы, создание экземпляра класса GNUTranslations может вызвать OSError.

class gettext.GNUTranslations

Из реализации базового класса переопределяются следующие методы:

gettext(message)

Ищите message id в каталоге и возвращает соответствующее сообщение строка как Юникод строка. Если нет никакого входа в каталоге для message id, и отступление было установлено, взгляд отправлен методу отступления gettext(). В противном случае идентификатор message имеет значение возвращенный.

ngettext(singular, plural, n)

Сделайте поиск множественных форм идентификатора сообщения. singular - используемый как идентификатор сообщения в целях поиска в каталоге, в то время как n - используемый, чтобы определить, какое множественное число формируют, чтобы использовать. Сообщение строка возвращенный - Юникод строка.

Если идентификатор сообщения не найден в каталоге и указан запасной вариант, запрос пересылается в резервный метод ngettext(). В противном случае, когда n равно 1 singular является возвращенный, а plural является возвращенный во всех остальных случаях.

Вот пример:

n = len(os.listdir('.'))
cat = GNUTranslations(somefile)
message = cat.ngettext(
    'There is %(num)d file in this directory',
    'There are %(num)d files in this directory',
    n) % {'num': n}
pgettext(context, message)

Найдите идентификатор context и message в каталоге и возвращает соответствующее сообщение строка в виде строка юникод. Если в каталоге нет записи для message id и context и установлен запасной вариант, поиск перенаправляется в резервный метод pgettext(). В противном случае идентификатор message имеет значение возвращенный.

Добавлено в версии 3.8.

npgettext(context, singular, plural, n)

Сделайте поиск множественных форм идентификатора сообщения. singular - используемый как идентификатор сообщения в целях поиска в каталоге, в то время как n - используемый, чтобы определить, какое множественное число формируют, чтобы использовать.

Если идентификатор сообщения для context не найден в каталоге и указан запасной вариант, запрос пересылается в резервный метод npgettext(). В противном случае, когда n равно 1 singular является возвращенный, а plural является возвращенный во всех остальных случаях.

Добавлено в версии 3.8.

lgettext(message)
lngettext(singular, plural, n)

Эквивалентно gettext() и ngettext(), но перевод возвращенный в качестве байта строка кодированный в предпочтительной системе кодировка, если ни один кодировка не был явно установлен с set_output_charset().

Предупреждение

Эти способы следует избегать в Python 3. См. предупреждение для функции lgettext().

Устарело с версии 3.8, будет удалено в 3.10 версии..

Поддержка каталога сообщений Solaris

Операционная система Solaris определяет собственный формат двоичного файла .mo, но так как по этому формату не удается найти никакой документации, в данный момент она не поддерживается.

Конструктор Catalog

GNOME использует версию модуля gettext James Henstridge, но у этой версии немного другой API. Задокументированное использование было:

import gettext
cat = gettext.Catalog(domain, localedir)
_ = cat.gettext
print(_('hello world'))

Для совместимости с этим более старым модулем функция Catalog() является алиас для описанной выше функции translation().

Одно различие между этим модулем и Henstridge’s: его объекты каталога поддерживали доступ через API сопоставления, но это, похоже, не используется и поэтому в настоящее время не поддерживается.

Интернационализация программ и модулей

Интернационализация (I18N) означает операцию, посредством которой программа получает информацию о множестве языков. Локализация (L10N) относится к адаптации вашей программы, когда-то интернационализированной, к локальная языку и культурным привычкам. Чтобы предоставить многоязычные сообщения для программ Python, необходимо выполнить следующие действия:

  1. подготовьте программу или модуль, специально пометив переводимые строки
  2. запустите набор инструментов над помеченными файлами для создания каталогов необработанных сообщений
  3. создание языковых переводов каталогов сообщений
  4. используйте модуль gettext, чтобы сообщение строки правильно транслировалось

Чтобы подготовить ваш код к I18N, нужно посмотреть все строки в ваших файлах. Любое строка, которое должно быть переведено, должно быть помечено, заключив его в _('...') —, то есть вызов функции _(). Например:

filename = 'mylog.txt'
message = _('writing a log message')
with open(filename, 'w') as fp:
    fp.write(message)

В этом примере строка 'writing a log message' помечен как кандидат для перевода, а строки 'mylog.txt' и 'w' - нет.

Существует несколько инструментов для извлечения строки, предназначенных для перевода. Оригинальный GNU gettext только поддержал C или источник C++ код, но его расширенная версия xgettext просматривает код, написанный на многих языках, включая Python, чтобы найти строки отмеченным как переводимый. Babel - библиотека интернационализации Python, которая включает сценарий pybabel, чтобы извлечь и собрать каталоги сообщения. Программа франсуа пинара под названием xpot выполняет аналогичную работу и доступна как часть его po-utils package.

(Python также включает в себя pure-Python версии этих программ, называемые pygettext.py и msgfmt.py; некоторые распределения Python установят их для вас. pygettext.py подобен xgettext, но только понимает источник Python код и не может обращаться с другими языками программирования, такими как C или C++. pygettext.py поддерживает интерфейс командной строки, подобный xgettext; для получения более подробной информации о его использовании выполните команду pygettext.py --help. msgfmt.py двоично совместим с GNU msgfmt. С этими двумя программами вам, возможно, не понадобится пакет GNU gettext, чтобы интернационализировать ваши заявления Python.)

Инструменты xgettext, pygettext и подобные инструменты создают файлы .po, являющиеся каталогами сообщений. Они структурированы человекочитаемые файлы, которые содержат каждый отмеченный строка в источнике код, наряду с заполнителем для переведенных версий этих строки.

Копии этих файлов .po тогда переданы отдельным людям-переводчикам, которые пишут переводы для каждого поддержанного естественного языка. Они передают законченные определенные для языка версии обратно как файл <language-name>.po, это собрано в машиночитаемый двойной файл каталога .mo, используя программу msgfmt. Файлы .mo - используемый модулем gettext для фактической обработки перевода во времени выполнения.

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

Локализация вашего модуля

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

Скажем, ваш модуль называют «спамом» и различным переводом естественного языка модуля, файлы .mo находятся в /usr/share/locale в формате GNU gettext. Вот что бы вы поставили в верхней части модуля:

import gettext
t = gettext.translation('spam', '/usr/share/locale')
_ = t.gettext

Локализация вашего приложения

При локализации приложения функцию _() можно установить глобально во встроенное пространство имен, обычно в основной файл драйвера приложения. Это позволит всем файлам приложения просто использовать _('...') без необходимости явной установки в каждом файле.

В этом случае в основной файл драйвера приложения необходимо добавить только следующий бит код:

import gettext
gettext.install('myapplication')

Если необходимо установить каталог языкового стандарта, его можно передать в функцию install():

import gettext
gettext.install('myapplication', '/usr/share/locale')

Смена языков на лету

Если программа должна поддерживать несколько языков одновременно, может потребоваться создать несколько сущности перевода, а затем переключиться между ними явно, например:

import gettext

lang1 = gettext.translation('myapplication', languages=['en'])
lang2 = gettext.translation('myapplication', languages=['fr'])
lang3 = gettext.translation('myapplication', languages=['de'])

# start by using language1
lang1.install()

# ... time goes by, user selects language 2
lang2.install()

# ... more time goes by, user selects language 3
lang3.install()

Отсроченные переводы

В большинстве ситуаций кодирования строки преобразуются там, где они кодируются. Однако иногда для перевода нужно отметить строки, но отложить фактический перевод на более поздний срок. Классический пример:

animals = ['mollusk',
           'albatross',
           'rat',
           'penguin',
           'python', ]
# ...
for a in animals:
    print(a)

Здесь вы хотите отметить строки в списке animals как переводимые, но вы не хотите переводить их до тех пор, пока они не будут напечатаны.

Вот один из способов справиться с этой ситуацией:

def _(message): return message

animals = [_('mollusk'),
           _('albatross'),
           _('rat'),
           _('penguin'),
           _('python'), ]

del _

# ...
for a in animals:
    print(_(a))

Это работает потому, что фиктивное определение _() просто возвращает строка без изменений. И это фиктивное определение временно переопределит любое определение _() во встроенном пространстве имен (до команды del). Берегите себя, если у вас есть предыдущее определение _() в пространстве имен локальная.

Следует отметить, что второе использование _() не идентифицирует «a» как переводимое в программу gettext, поскольку параметр не является строка литерал.

Другой способ справиться с этим - со следующим примером:

def N_(message): return message

animals = [N_('mollusk'),
           N_('albatross'),
           N_('rat'),
           N_('penguin'),
           N_('python'), ]

# ...
for a in animals:
    print(_(a))

В этом случае переводимые строки помечаются функцией N_(), которая не конфликтует ни с одним определением _(). Тем не менее, вам нужно будет научить программу извлечения сообщений искать переводимые строки, помеченные N_(). xgettext, pygettext, pybabel extract и xpot все поддерживают это с помощью переключателя командной строки -k. Выбор N_() здесь совершенно произвольный; это, возможно, так же, как легко был MarkThisStringForTranslation().

Выражение признательности

Следующие люди внесли код, обратную связь, предложения дизайна, предыдущие внедрения и ценный опыт к созданию этого модуля:

  • Peter Funk
  • James Henstridge
  • Juan David Ibáñez Palomar
  • Marc-André Lemburg
  • Martin von Löwis
  • François Pinard
  • Barry Warsaw
  • Gustavo Niemeyer

Сноски

[1]Каталог локали по умолчанию зависит от системы; например, на RedHat Linux это /usr/share/locale, но на Solaris это /usr/lib/locale. Модуль gettext не пытается поддерживать эти зависящие от системы значения по умолчанию; вместо этого его значение по умолчанию равно sys.base_prefix/share/locale (см. sys.base_prefix). По этой причине всегда лучше вызывать bindtextdomain() с явным абсолютным путем в начале приложения.
[2]См. сноску для bindtextdomain() выше.