Spec-Zone.ru › Python 3.6

gettext — Услуги многоязычной интернационализации

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

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

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

23.1.1. API GNU gettext

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

gettext.bindtextdomain(domain, localedir=None)

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

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

gettext.bind_textdomain_codeset(domain, codeset=None)

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

gettext.textdomain(domain=None)

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

gettext.gettext(message)

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

gettext.dgettext(domain, message)

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

gettext.ngettext(singular, plural, n)

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

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

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

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

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, потому что они возвращают закодированные байты. Гораздо лучше использовать альтернативы, которые возвращают строки Unicode вместо этого, поскольку большинство приложений Python будут хотеть обрабатывать читаемый человеком текст как строки вместо байтов. Кроме того, могут возникнуть неожиданные исключения, связанные с Unicode, если есть проблемы с кодировкой переведенных строк. Возможно, что функции l*() будут устаревшими в будущих версиях Python из-за их внутренних проблем и ограничений.

Обратите внимание, что 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.'))

23.1.2. API на основе классов

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

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

Эта функция реализует стандартный алгоритм поиска файлов .mo. Она принимает домен, идентичный тому, что принимает 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 на основе домена, каталога с локальными файлами и языков, которые сначала передаются в find() для получения списка соответствующих путей к файлам .mo. Экземпляры с идентичными именами файлов .mo кешируются. Фактический создаваемый класс — это class_, если он предоставлен, в противном случае — GNUTranslations. Конструктор класса должен принимать один аргумент — объект файла. Если предоставлен codeset, он изменит кодировку, используемую для кодирования переведённых строк в методах lgettext() и lngettext().

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

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

Изменено в версии 3.3: IOError ранее вызывалась вместо OSError.

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

Эта функция устанавливает функцию _() в встроенном пространстве имен Python на основе домена, каталога с локальными файлами и кодировки, которые передаются в функцию translation().

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

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

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

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

23.1.2.1. Класс NullTranslations

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

class gettext.NullTranslations(fp=None)

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

_parse(fp)

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

add_fallback(fallback)

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

gettext(message)

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

ngettext(singular, plural, n)

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

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

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

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

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

info()

Возвращает «защищённую» переменную _info.

charset()

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

output_charset()

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

set_output_charset(charset)

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

install(names=None)

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

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

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

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

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

23.1.2.2. Класс GNUTranslations

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

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

Так как идентификаторы сообщений также считываются как строки Unicode, все методы *gettext() будут предполагать идентификаторы сообщений как строки Unicode, а не строки байтов.

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

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

class gettext.GNUTranslations

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

gettext(message)

Ищет идентификатор сообщения в каталоге и возвращает соответствующую строку сообщения в виде строки Unicode. Если в каталоге нет записи для идентификатора сообщения, а кеш-замена установлена, поиск передаётся методу кеш-замены gettext(). В противном случае возвращается идентификатор сообщения.

ngettext(singular, plural, n)

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

Если идентификатор сообщения не найден в каталоге, и указана кеш-замена, запрос передаётся методу кеш-замены 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}
lgettext(message)
lngettext(singular, plural, n)

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

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

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

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

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

23.1.2.4. Конструктор каталога

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

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

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

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

23.1.3. Локализация ваших программ и модулей

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

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

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

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

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

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

(Python также включает чисто-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 в вашем коде зависит от того, интернационализируете ли вы отдельный модуль или всё ваше приложение. В следующих двух разделах будут рассмотрены каждый из этих случаев.

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

Если вы локализуете свой модуль, вы должны позаботиться о том, чтобы не вносить глобальных изменений, например, в встроенное пространство имён. Вы не должны использовать API GNU gettext , а вместо этого использовать API на основе класса.

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

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

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

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

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

import gettext
gettext.install('myapplication')

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

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

23.1.3.3. Изменение языков на лету

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

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()

23.1.3.4. Отложенные переводы

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

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().

23.1.4. Благодарности

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

  • 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.prefix/share/locale. По этой причине всегда лучше вызывать bindtextdomain() с явным абсолютным путем в начале вашего приложения.

2

См. примечание к bindtextdomain() выше.

© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.6/library/gettext.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API