gettext
— Услуги по многоязычной интернационализации¶
Исходный код: Lib/gettext.py
Модуль gettext
предоставляет услуги интернационализации (I18N) и локализации (L10N) для ваших модулей и приложений на Python. Он поддерживает как API каталога сообщений GNU gettext, так и API более высокого уровня, основанный на классах, который может быть более подходящим для файлов Python. Описанный ниже интерфейс позволяет вам писать сообщения вашего модуля и приложения на одном естественном языке и предоставлять каталог переведенных сообщений для запуска на разных естественных языках.
Также даны некоторые советы по локализации ваших модулей и приложений на Python.
GNU gettext API¶
Модуль gettext
определяет следующий API, который очень похож на GNU gettext API. Если вы будете использовать этот API, это повлияет на перевод всего вашего приложения в глобальном масштабе. Часто это именно то, что вам нужно, если ваше приложение является одноязычным, и выбор языка зависит от языкового стандарта вашего пользователя. Если вы локализуете модуль Python или если вашему приложению необходимо переключать языки на лету, вы, вероятно, захотите использовать API на основе классов.
- gettext.bindtextdomain(domain, localedir=None)¶
Привяжите домен к каталогу локали localedir. Более конкретно,
gettext
будет искать двоичные.mo
файлы для данного домена, используя путь (в Unix):localedir/language/LC_MESSAGES/domain.mo
, где язык ищется в переменных окруженияLANGUAGE
,LC_ALL
,LC_MESSAGES
, иLANG
соответственно.Если localedir опущен или
None
, то возвращается текущая привязка для domain. [1]
- gettext.textdomain(domain=None)¶
Измените или запросите текущий глобальный домен. Если значение domain равно
None
, то возвращается текущий глобальный домен, в противном случае для глобального домена устанавливается значение domain, которое и возвращается.
- gettext.gettext(message)¶
Возвращает локализованный перевод message на основе текущего каталога глобального домена, языка и локали. Эта функция обычно имеет псевдоним
_()
в локальном пространстве имен (см. примеры ниже).
- gettext.ngettext(singular, plural, n)¶
Например,
gettext()
, но учитывайте формы множественного числа. Если найден перевод, примените формулу множественного числа к n и верните полученное сообщение (в некоторых языках существует более двух форм множественного числа). Если перевод не найден, верните единственное число, если n равно 1; в противном случае верните множественное число.Формула множественного числа взята из заголовка каталога. Это выражение на C или Python, которое имеет свободную переменную n; значение выражения равно индексу множественного числа в каталоге. Смотрите the GNU gettext documentation для получения точного синтаксиса, который будет использоваться в файлах
.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()
),, но перевод ограничен данное сообщение контекст.Добавлено в версии 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
. Для этого требуется домен, идентичный тому, который используется вtextdomain()
. Необязательный localedir такой же, как вbindtextdomain()
. Необязательные языки - это список строк, где каждая строка представляет собой код языка.Если localedir не указан, то используется системный каталог локали по умолчанию. [2] Если параметр языки не указан, то выполняется поиск по следующим переменным среды:
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
. Конструктор класса должен принимать единственный аргумент file object.Если найдено несколько файлов, более поздние файлы используются в качестве резервных для более ранних. Чтобы разрешить установку резервного варианта,
copy.copy()
используется для клонирования каждого объекта трансляции из кэша; фактические данные экземпляра по-прежнему используются совместно с кэшем.Если файл
.mo
не найден, эта функция вызываетOSError
, если значение fallback равно false (которое используется по умолчанию), и возвращает экземплярNullTranslations
, если значение fallback равно true.Изменено в версии 3.11: параметр codeset удален.
- gettext.install(domain, localedir=None, *, names=None)¶
При этом функция
_()
устанавливается во встроенном пространстве имен Python на основе domain и localedir, которые передаются в функциюtranslation()
.Что касается параметра names, пожалуйста, ознакомьтесь с описанием метода
install()
объекта перевода.Как показано ниже, вы обычно помечаете строки в своем приложении, которые являются кандидатами на перевод, заключая их в вызов функции
_()
, вот так:print(_('This string will be translated.'))
Для удобства вы хотите, чтобы функция
_()
была установлена в пространстве имен builtins Python, чтобы она была легко доступна во всех модулях вашего приложения.Изменено в версии 3.11: names теперь является параметром только для ключевых слов.
Класс NullTranslations
¶
Классы перевода - это то, что фактически реализует перевод строк сообщений исходного файла в переведенные строки сообщений. Базовый класс, используемый всеми классами перевода, - это NullTranslations
; он предоставляет базовый интерфейс, который вы можете использовать для написания собственных специализированных классов перевода. Вот методы NullTranslations
:
- class gettext.NullTranslations(fp=None)¶
Принимает необязательный file object 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()
к резервному варианту. В противном случае верните единственное число, если n равно 1; в противном случае верните множественное число. Переопределяется в производных классах.
- 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
как в большом, так и в малом порядке следования.
GNUTranslations
анализирует необязательные метаданные из каталога переводов. Согласно соглашению с GNU gettext, метаданные включаются в качестве перевода пустой строки. Эти метаданные находятся в парах RFC 822 в стиле key: value
и должны содержать ключ Project-Id-Version
. Если ключ Content-Type
найден, то свойство charset
используется для инициализации «защищенной» переменной экземпляра _charset
, значение по умолчанию равно None
, если оно не найдено. Если указана кодировка charset, то все идентификаторы сообщений и строки сообщений, считываемые из каталога, преобразуются в Unicode с использованием этой кодировки, в противном случае предполагается использование ASCII.
Поскольку идентификаторы сообщений также считываются как строки в Юникоде, все методы *gettext()
будут принимать идентификаторы сообщений как строки в Юникоде, а не как байтовые строки.
Весь набор пар ключ/значение помещается в словарь и задается как «защищенная» переменная экземпляра _info
.
Если магический номер файла .mo
неверен, основной номер версии является неожиданным или если при чтении файла возникают другие проблемы, при создании экземпляра класса GNUTranslations
может возникнуть OSError
.
- class gettext.GNUTranslations¶
Следующие методы переопределены из реализации базового класса:
- gettext(message)¶
Найдите идентификатор сообщения в каталоге и верните соответствующую строку сообщения в формате Unicode. Если в каталоге нет записи для идентификатора message и был задан резервный вариант, поиск перенаправляется к методу
gettext()
. В противном случае возвращается идентификатор message.
- ngettext(singular, plural, n)¶
Выполните поиск идентификатора сообщения в форме множественного числа. единственное число используется в качестве идентификатора сообщения для поиска в каталоге, в то время как n используется для определения того, какую форму множественного числа использовать. Возвращаемая строка сообщения является строкой в Юникоде.
Если идентификатор сообщения не найден в каталоге и указан запасной вариант, запрос перенаправляется в резервный метод
ngettext()
. В противном случае, когда n равно 1, возвращается значение в единственном числе, а во всех остальных случаях - значение во множественном числе.Вот пример:
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 в каталоге и верните соответствующую строку сообщения в виде строки в Юникоде. Если в каталоге нет записи для идентификатора сообщения и контекста и был установлен резервный вариант, поиск перенаправляется в метод
pgettext()
. В противном случае возвращается идентификатор сообщения.Добавлено в версии 3.8.
- npgettext(context, singular, plural, n)¶
Выполните поиск по форме множественного числа для идентификатора сообщения. единственное число используется в качестве идентификатора сообщения для поиска в каталоге, в то время как n используется для определения того, какую форму множественного числа использовать.
Если идентификатор сообщения для context не найден в каталоге и указан резервный вариант, запрос перенаправляется методу
npgettext()
, который является резервным. В противном случае, когда n равно 1, возвращается единственное число, а во всех остальных случаях возвращается множественное число.Добавлено в версии 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, вам необходимо выполнить следующие действия:
подготовьте свою программу или модуль, специально выделив переводимые строки
запустите набор инструментов над вашими помеченными файлами, чтобы создать каталоги необработанных сообщений
создавайте переводы каталогов сообщений на разные языки
используйте модуль
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 также включает в себя версии этих программ на чистом 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, а API на основе классов.
Допустим, ваш модуль называется «spam» и различные файлы перевода на естественные языки .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()
.
Признание¶
Следующие люди внесли свой вклад в создание этого модуля: код, отзывы, предложения по дизайну, предыдущие реализации и ценный опыт.:
Питер Фанк
Джеймс Хенстридж
Хуан Давид Ибаньес Паломар
Марк-Андре Лембург
Мартин фон Левис
Франсуа Пинар
Барри Варшава
Густаво Нимейер
Сноски