Spec-Zone.ru › Python 3.12

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

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

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

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

GNU gettext API

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

gettext.bindtextdomain(domain, localedir=None)

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

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

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

Обратите внимание, что 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 на основе классов

API на основе классов модуля gettext предоставляет большую гибкость и удобство по сравнению с API GNU gettext. Это рекомендуемый способ локализации ваших приложений и модулей 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)

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

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

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

Изменено в версии 3.3: IOError раньше использовался, теперь он является алиасом OSError.

Изменено в версии 3.11: Параметр codeset удален.

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

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

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

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

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

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

Изменено в версии 3.11: names теперь является только ключевым параметром.

Класс 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. Переопределяется в производных классах.

pgettext(context, message)

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

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

npgettext(context, singular, plural, n)

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

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

info()

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

charset()

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

install(names=None)

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

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

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

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

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

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

Класс GNUTranslations

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

GNUTranslations парсит необязательные метаданные из каталога переводов. У GNU gettext принято включать метаданные в качестве перевода пустой строки. Эти метаданные представлены в формате key: value пар типа RFC 822, и должны содержать ключ 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}
pgettext(context, message)

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

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

npgettext(context, singular, plural, n)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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.

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

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

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

Предположим, что ваш модуль называется «spam», а различные файлы перевода на естественные языки модуля находятся в /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().

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

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

  • Питер Функ
  • Джеймс Хенстридж
  • Хуан Давид Ибанез Паломар
  • Марк-Андре Лембёрг
  • Мартин фон Ловис
  • Франсуа Пинар
  • Барри Варса
  • Густаво Неймейер

Примечания

[1]

По умолчанию каталог локализации зависит от системы; например, в Red Hat Linux он является /usr/share/locale, но в Solaris — /usr/lib/locale. Модуль gettext не пытается поддерживать эти системные значения по умолчанию; вместо этого его значение по умолчанию sys.base_prefix/share/locale (см. sys.base_prefix). По этой причине всегда лучше вызывать bindtextdomain() с явным абсолютным путем в начале вашего приложения.

[2]

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

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

Spec-Zone.ru

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