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.
bind_textdomain_codeset
(domain, codeset=None)¶ Привязка domain к codeset, изменяющая кодировку байтовых строк, возвращаемых функциями
lgettext()
,ldgettext()
,lngettext()
иldngettext()
. Если codeset опущен, то возвращается текущая привязка.Deprecated since version 3.8, removed in version 3.10.
-
gettext.
textdomain
(domain=None)¶ Изменение или запрос текущего глобального домена. Если domain равно
None
, то возвращается текущий глобальный домен, иначе глобальный домен устанавливается в domain, который и возвращается.
-
gettext.
gettext
(message)¶ Возвращает локализованный перевод message, основанный на текущем глобальном домене, языке и каталоге локали. В локальном пространстве имен эта функция обычно имеет псевдоним
_()
(см. примеры ниже).
-
gettext.
ngettext
(singular, plural, n)¶ Аналогично
gettext()
, но учитывает формы множественного числа. Если перевод найден, примените формулу множественного числа к n и верните полученное сообщение (некоторые языки имеют более двух форм множественного числа). Если перевод не найден, верните singular, если n равно 1; верните plural в противном случае.Формула множественного числа берется из заголовка каталога. Она представляет собой выражение на языке 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.
-
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, если возникнут проблемы с кодировкой переведенных строк.
Deprecated since version 3.8, removed in version 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 на основе классов¶
API на основе классов модуля gettext
дает вам большую гибкость и большее удобство, чем API GNU gettext. Это рекомендуемый способ локализации приложений и модулей Python. gettext
определяет класс GNUTranslations
, который реализует разбор файлов формата GNU .mo
и имеет методы для возврата строк. Экземпляры этого класса могут также устанавливаться во встроенное пространство имен как функция _()
.
-
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
на основе domain, localedir и languages, которые сначала передаются вfind()
для получения списка соответствующих.mo
путей к файлам. Экземпляры с одинаковыми именами файлов.mo
кэшируются. Фактический инстанцируемый класс - это class_, если он указан, иначеGNUTranslations
. Конструктор класса должен принимать один аргумент file object. Если предоставлено, codeset изменит кодовую сетку, используемую для кодирования переведенных строк в методахlgettext()
иlngettext()
.Если найдено несколько файлов, более поздние файлы используются в качестве запасных для более ранних. Для того чтобы можно было установить запасной вариант,
copy.copy()
используется для клонирования каждого объекта перевода из кэша; фактические данные экземпляра по-прежнему используются совместно с кэшем.Если файл
.mo
не найден, эта функция поднимаетOSError
, если fallback равен false (что является значением по умолчанию), и возвращает экземплярNullTranslations
, если fallback равен true.Deprecated since version 3.8, removed in version 3.10: Параметр codeset.
-
gettext.
install
(domain, localedir=None, codeset=None, names=None)¶ Это устанавливает функцию
_()
в пространство имен Python builtins, основанное на domain, localedir и codeset, которые передаются в функциюtranslation()
.Для параметра names см. описание метода
install()
объекта перевода.Как показано ниже, вы обычно помечаете строки в вашем приложении, которые являются кандидатами на перевод, обернув их в вызов функции
_()
, как показано ниже:print(_('This string will be translated.'))
Для удобства вы хотите, чтобы функция
_()
была установлена в пространстве имен Python builtins, чтобы она была легко доступна во всех модулях вашего приложения.Deprecated since version 3.8, removed in version 3.10: Параметр codeset.
Класс NullTranslations
¶
Классы трансляции - это то, что фактически реализует трансляцию строк сообщений исходного файла в переведенные строки сообщений. Базовым классом, используемым всеми классами трансляции, является NullTranslations
; он предоставляет базовый интерфейс, который можно использовать для написания собственных специализированных классов трансляции. Вот методы класса NullTranslations
:
-
class
gettext.
NullTranslations
(fp=None)¶ Принимает необязательный file object fp, который игнорируется базовым классом. Инициализирует «защищенные» переменные экземпляра _info и _charset, которые устанавливаются производными классами, а также _fallback, которая устанавливается через
add_fallback()
. Затем вызываетself._parse(fp)
, если fp не являетсяNone
.-
_parse
(fp)¶ No-op в базовом классе, этот метод принимает объект файла fp и считывает данные из файла, инициализируя свой каталог сообщений. Если у вас неподдерживаемый формат файла каталога сообщений, вам следует переопределить этот метод для разбора вашего формата.
-
add_fallback
(fallback)¶ Добавьте fallback в качестве объекта fallback для текущего объекта перевода. Если объект перевода не может предоставить перевод для данного сообщения, он должен обратиться к резервному объекту.
-
gettext
(message)¶ Если был установлен fallback, перешлите
gettext()
в fallback. В противном случае возвращается сообщение. Переопределяется в производных классах.
-
ngettext
(singular, plural, n)¶ Если задан обратный ход, перешлите
ngettext()
в обратный ход. В противном случае возвращает сингулярный, если n равно 1; возвращает плюральный в противном случае. Переопределено в производных классах.
-
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()
.Deprecated since version 3.8, removed in version 3.10.
-
info
()¶ Возвращает переменную «protected»
_info
, словарь, содержащий метаданные, найденные в файле каталога сообщений.
-
charset
()¶ Возвращает кодировку файла каталога сообщений.
-
output_charset
()¶ Возвращает кодировку, используемую для возврата переведенных сообщений в
lgettext()
иlngettext()
.Deprecated since version 3.8, removed in version 3.10.
-
set_output_charset
(charset)¶ Изменение кодировки, используемой для возврата переведенных сообщений.
Deprecated since version 3.8, removed in version 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, так и в формате little-endian.
GNUTranslations
разбирает необязательные метаданные из каталога переводов. В GNU gettext принято включать метаданные в качестве перевода для пустой строки. Эти метаданные находятся в парах RFC 822-стиля key: value
и должны содержать ключ Project-Id-Version
. Если ключ Content-Type
найден, то свойство charset
используется для инициализации переменной экземпляра «protected» _charset
, по умолчанию None
, если не найден. Если указана кодировка charset, то все идентификаторы сообщений и строки сообщений, считанные из каталога, преобразуются в Unicode с использованием этой кодировки, в противном случае принимается ASCII.
Поскольку идентификаторы сообщений также читаются как строки Unicode, все методы *gettext()
будут считать идентификаторы сообщений строками Unicode, а не байтовыми строками.
Весь набор пар ключ/значение помещается в словарь и устанавливается как переменная экземпляра «protected» _info
.
Если магический номер файла .mo
недействителен, основной номер версии неожиданный, или если при чтении файла возникают другие проблемы, инстанцирование класса GNUTranslations
может вызвать ошибку OSError
.
-
class
gettext.
GNUTranslations
¶ Следующие методы переопределяются из реализации базового класса:
-
gettext
(message)¶ Поиск идентификатора message в каталоге и возврат соответствующей строки сообщения в виде строки Unicode. Если в каталоге нет записи для идентификатора message, и установлен fallback, поиск передается методу fallback’а
gettext()
. В противном случае возвращается идентификатор сообщения.
-
ngettext
(singular, plural, n)¶ Выполните поиск множественной формы для идентификатора сообщения. singular используется в качестве идентификатора сообщения для поиска в каталоге, а n используется для определения формы множественного числа. Возвращаемая строка сообщения является строкой Unicode.
Если идентификатор сообщения не найден в каталоге, и указан fallback, запрос пересылается методу fallback’а
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 и вернуть соответствующую строку сообщения в виде строки Unicode. Если в каталоге нет записей для идентификатора message и context, и установлен fallback, поиск передается методу fallback’а
pgettext()
. В противном случае возвращается идентификатор сообщения.Добавлено в версии 3.8.
-
npgettext
(context, singular, plural, n)¶ Выполните поиск множественной формы для идентификатора сообщения. singular используется в качестве идентификатора сообщения для поиска в каталоге, а n используется для определения формы множественного числа.
Если идентификатор сообщения для context не найден в каталоге, и указан fallback, запрос передается методу fallback’а
npgettext()
. В противном случае, если n равно 1, возвращается сингулярное, а во всех остальных случаях возвращается плюральное.Добавлено в версии 3.8.
-
lgettext
(message)¶
-
lngettext
(singular, plural, n)¶ Эквивалентно
gettext()
иngettext()
, но перевод возвращается в виде строки байтов, закодированной в предпочтительной системной кодировке, если кодировка не была явно задана с помощьюset_output_charset()
.Предупреждение
Этих методов следует избегать в Python 3. См. предупреждение для функции
lgettext()
.Deprecated since version 3.8, removed in version 3.10.
-
Поддержка каталога сообщений 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 также включает чисто питоновские версии этих программ, называемые 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», а различные файлы перевода на естественный язык .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()
.
Благодарности¶
Следующие люди предоставили код, отзывы, предложения по дизайну, предыдущие реализации и ценный опыт для создания этого модуля:
Питер Функ
Джеймс Хенстридж
Хуан Давид Ибаньес Паломар
Марк-Андре Лембург
Мартин фон Лёвис
Франсуа Пинар
Барри Варшава
Густаво Нимейер
Сноски
- 1
Каталог локали по умолчанию зависит от системы; например, в RedHat Linux это
/usr/share/locale
, а в Solaris это/usr/lib/locale
. Модульgettext
не пытается поддерживать эти системно-зависимые значения по умолчанию; вместо этого по умолчанию используетсяsys.base_prefix/share/locale
(см.sys.base_prefix
). По этой причине всегда лучше вызыватьbindtextdomain()
с явным абсолютным путем в начале вашего приложения.- 2
См. сноску для
bindtextdomain()
выше.