Содержание страницы
gettext – Многоязычные сервисы интернационализации¶gettext – Multilingual internationalization services
Исходный код: 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)¶ Привязывает domain к каталогу локали localedir. Конкретнее,
gettextбудет искать двоичные файлы.moдля указанного домена, используя путь (в Unix):localedir/language/LC_MESSAGES/domain.mo, где language ищется в переменных окруженияLANGUAGE,LC_ALL,LC_MESSAGESиLANGсоответственно.Если localedir опущен или равен
None, то возвращается текущая привязка для domain. 1
-
gettext.bind_textdomain_codeset(domain, codeset=None)¶ Привязывает domain к codeset, изменяя кодировку байтовых строк, возвращаемых функциями
lgettext(),ldgettext(),lngettext()иldngettext(). Если codeset опущен, возвращается текущая привязка.Устарело с версии 3.8, будет удалено в версии 3.10.
-
gettext.textdomain(domain=None)¶ Изменяет или запрашивает текущий глобальный домен. Если domain равен
None, возвращается текущий глобальный домен; в противном случае глобальный домен устанавливается в domain, который и возвращается.
-
gettext.gettext(message)¶ Возвращает локализованный перевод message на основе текущего глобального домена, языка и каталога локали. Эта функция обычно имеет псевдоним
_()в локальном пространстве имён (см. примеры ниже).
-
gettext.ngettext(singular, plural, n)¶ Как
gettext(), но учитывает формы множественного числа. Если перевод найден, применяет формулу множественного числа к n и возвращает результирующее сообщение (в некоторых языках более двух форм множественного числа). Если перевод не найден, возвращает singular, если n равно 1; в противном случае возвращает plural.Формула множественного числа берётся из заголовка каталога. Это выражение на C или Python со свободной переменной n; выражение вычисляет индекс формы множественного числа в каталоге. См. документацию GNU gettext для точного синтаксиса, используемого в файлах
.po, и формулы для различных языков.
-
gettext.dngettext(domain, singular, plural, n)¶ Как
ngettext(), но ищет сообщение в указанном domain.
-
gettext.pgettext(context, message)¶
-
gettext.dpgettext(domain, context, message)¶
-
gettext.npgettext(context, singular, plural, n)¶
-
gettext.dnpgettext(domain, context, singular, plural, n)¶ Аналогично соответствующим функциям без
pв префиксе (то естьgettext(),dgettext(),ngettext(),dngettext()), но перевод ограничен заданным context сообщения.Новое в версии 3.8.
-
gettext.lgettext(message)¶
-
gettext.ldgettext(domain, message)¶
-
gettext.lngettext(singular, plural, n)¶
-
gettext.ldngettext(domain, singular, plural, n)¶ Эквивалентно соответствующим функциям без префикса
l(gettext(),dgettext(),ngettext()иdngettext()), но перевод возвращается в виде байтовой строки, закодированной в предпочтительной системной кодировке, если другая кодировка не была явно задана с помощьюbind_textdomain_codeset().Предупреждение
Этих функций следует избегать в Python 3, так как они возвращают закодированные байты. Гораздо лучше использовать альтернативы, возвращающие вместо них строки Unicode, поскольку большинство приложений Python работают с читаемым текстом в виде строк, а не байтов. Кроме того, возможно возникновение неожиданных исключений, связанных с Unicode, при проблемах кодировки переведённых строк.
Устарело с версии 3.8, будет удалено в версии 3.10.
Обратите внимание, что GNU gettext также определяет метод dcgettext(), но он был признан бесполезным и на данный момент не реализован.
Вот пример типичного использования этого API:
import gettext
gettext.bindtextdomain('myapplication', '/path/to/my/language/directory')
gettext.textdomain('myapplication')
_ = gettext.gettext
# ...
print(_('This is a translatable string.'))
API на основе классов¶Class-based API
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, 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.Устарело с версии 3.8, будет удалено в версии 3.10: Параметр codeset.
-
gettext.install(domain, localedir=None, codeset=None, names=None)¶ Эта функция устанавливает функцию
_()в пространство имён встроенных функций Python на основе domain, localedir и codeset, которые передаются в функциюtranslation().Описание параметра names см. в описании метода
install()объекта перевода.Как показано ниже, строки в приложении, предназначенные для перевода, обычно помечаются обёртыванием их в вызов функции
_(), например:print(_('This string will be translated.'))
Для удобства функцию
_()устанавливают в пространство имён встроенных функций Python, чтобы она была легко доступна во всех модулях приложения.Устарело с версии 3.8, будет удалено в версии 3.10: Параметр codeset.
Класс NullTranslations¶The NullTranslations class
Классы перевода – это то, что фактически реализует перевод строк сообщений исходного файла в переведённые строки сообщений. Базовый класс, используемый всеми классами перевода, – 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.
-
lgettext(message)¶
-
lngettext(singular, plural, n)¶ Эквивалентно
gettext()иngettext(), но перевод возвращается в виде байтовой строки, закодированной в предпочтительной системной кодировке, если кодировка не была явно задана с помощьюset_output_charset(). Переопределяется в производных классах.Предупреждение
Этих методов следует избегать в Python 3. См. предупреждение для функции
lgettext().Устарело с версии 3.8, будет удалено в версии 3.10.
-
info()¶ Возвращает «защищённую» переменную
_info– словарь, содержащий метаданные из файла каталога сообщений.
-
charset()¶ Возвращает кодировку файла каталога сообщений.
-
output_charset()¶ Возвращает кодировку, используемую для возврата переведённых сообщений в
lgettext()иlngettext().Устарело с версии 3.8, будет удалено в версии 3.10.
-
set_output_charset(charset)¶ Изменяет кодировку, используемую для возврата переведённых сообщений.
Устарело с версии 3.8, будет удалено в версии 3.10.
-
install(names=None)¶ Этот метод устанавливает
gettext()во встроенное пространство имён, связывая его с_.Если задан параметр names, он должен быть последовательностью, содержащей имена функций, которые нужно установить в пространство имён builtins в дополнение к
_(). Поддерживаемые имена:'gettext','ngettext','pgettext','npgettext','lgettext'и'lngettext'.Обратите внимание, что это лишь один из способов, хоть и самый удобный, сделать функцию
_()доступной в приложении. Поскольку это влияет на всё приложение глобально, и в особенности на встроенное пространство имён, локализованные модули никогда не должны устанавливать_(). Вместо этого им следует использовать следующий код, чтобы сделать_()доступной в своём модуле:import gettext t = gettext.translation('mymodule', ...) _ = t.gettext
Это помещает
_()только в глобальное пространство имён модуля и поэтому влияет только на вызовы внутри этого модуля.Изменено в версии 3.8: Добавлены
'pgettext'и'npgettext'.
-
Класс GNUTranslations¶The GNUTranslations class
Модуль gettext предоставляет один дополнительный класс, производный от
NullTranslations: GNUTranslations. Этот класс переопределяет
_parse(), чтобы обеспечить чтение файлов .mo в формате GNU gettext
как в формате big-endian, так и little-endian.
GNUTranslations извлекает необязательные метаданные из каталога
переводов. По соглашению в GNU gettext метаданные включаются как
перевод пустой строки. Эти метаданные представлены в стиле RFC 822
парами key: value и должны содержать ключ Project-Id-Version. Если найден
ключ Content-Type, то свойство charset используется для
инициализации «защищённой» переменной экземпляра _charset, по умолчанию
равной None, если не найдено. Если указана кодировка charset, то все
идентификаторы сообщений и строки сообщений, прочитанные из каталога, преобразуются в Unicode с использованием
этой кодировки; в противном случае предполагается ASCII.
Поскольку идентификаторы сообщений также читаются как строки Unicode, все методы *gettext()
будут считать идентификаторы сообщений строками Unicode, а не байтовыми строками.
Весь набор пар ключ/значение помещается в словарь и устанавливается как
«защищённая» переменная экземпляра _info.
Если магическое число файла .mo недействительно, основной номер версии
неожиданный, или возникают другие проблемы при чтении файла, создание экземпляра класса
GNUTranslations может вызвать исключение OSError.
-
class
gettext.GNUTranslations¶ Следующие методы переопределены из реализации базового класса:
-
gettext(message)¶ Ищет идентификатор message в каталоге и возвращает соответствующую строку сообщения в виде строки Unicode. Если в каталоге нет записи для идентификатора message и задан резервный источник (fallback), поиск перенаправляется в метод
gettext()резервного источника. В противном случае возвращается идентификатор message.
-
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)¶ Ищет идентификаторы context и message в каталоге и возвращает соответствующую строку сообщения в виде строки Unicode. Если в каталоге нет записи для идентификатора message и context, и задан резервный источник, поиск перенаправляется в метод
pgettext()резервного источника. В противном случае возвращается идентификатор message.Новое в версии 3.8.
-
npgettext(context, singular, plural, n)¶ Выполняет поиск формы множественного числа по идентификатору сообщения. singular используется как идентификатор сообщения для поиска в каталоге, а n используется для определения того, какую форму множественного числа использовать.
Если идентификатор сообщения для context не найден в каталоге и указан резервный источник, запрос перенаправляется в метод
npgettext()резервного источника. В противном случае, когда n равно 1, возвращается singular, а во всех остальных случаях возвращается plural.Новое в версии 3.8.
-
lgettext(message)¶
-
lngettext(singular, plural, n)¶ Эквивалентно
gettext()иngettext(), но перевод возвращается в виде байтовой строки, закодированной в предпочтительной системной кодировке, если кодировка не была явно задана с помощьюset_output_charset().Предупреждение
Этих методов следует избегать в Python 3. См. предупреждение для функции
lgettext().Устарело с версии 3.8, будет удалено в версии 3.10.
-
Поддержка каталогов сообщений Solaris¶Solaris message catalog support
Операционная система Solaris определяет собственный двоичный формат файлов .mo, но,
поскольку документации по этому формату найти не удалось, он в настоящее время не поддерживается.
Конструктор каталога¶The Catalog constructor
GNOME использует версию модуля gettext от James Henstridge, но эта
версия имеет несколько другой API. Её задокументированное использование было таким:
import gettext
cat = gettext.Catalog(domain, localedir)
_ = cat.gettext
print(_('hello world'))
Для совместимости с этим старым модулем функция Catalog() является
псевдонимом функции translation(), описанной выше.
Одно различие между этим модулем и модулем Хенстриджа: его объекты каталога поддерживали доступ через API отображения (mapping API), но, по-видимому, эта возможность не используется и поэтому в настоящее время не поддерживается.
Интернационализация программ и модулей¶Internationalizing your programs and modules
Интернационализация (I18N) – это процесс, благодаря которому программа начинает работать с несколькими языками. Локализация (L10N) – это адаптация уже интернационализированной программы к местному языку и культурным особенностям. Чтобы добавить в программу на Python поддержку нескольких языков, нужно выполнить следующие шаги:
подготовить программу или модуль, специально помечая переводимые строки
запустить набор инструментов над размеченными файлами для создания исходных каталогов сообщений
создать языковые переводы каталогов сообщений
использовать модуль
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 также есть чисто питоновские версии этих программ: pygettext.py
и msgfmt.py; некоторые дистрибутивы Python устанавливают их автоматически.
pygettext.py похожа на xgettext, но понимает только
исходный код Python и не умеет работать с другими языками вроде C или C++.
pygettext.py поддерживает интерфейс командной строки, аналогичный
xgettext; подробнее о его использовании – в выводе pygettext.py
--help.
msgfmt.py двоично совместима с GNU msgfmt.
С этими двумя программами для интернационализации приложений на Python
пакет GNU gettext может и не понадобиться.)
xgettext, pygettext и похожие инструменты создают
файлы .po – каталоги сообщений. Это структурированные,
человекочитаемые файлы, содержащие каждую помеченную строку из исходного кода
вместе с местом для переведённых версий этих строк.
Копии этих файлов .po затем передаются переводчикам-людям,
которые пишут переводы для каждого поддерживаемого естественного языка.
Они возвращают готовые языковые версии в виде файла <language-name>.po, который
компилируется в машинно-читаемый двоичный каталог .mo с помощью
программы msgfmt. Файлы .mo используются модулем
gettext для непосредственного перевода во время выполнения.
То, как используется модуль gettext в коде, зависит от того, интернационализируется
ли один модуль или всё приложение. В следующих двух разделах будет рассмотрен каждый случай.
Локализация модуля¶Localizing your module
Если вы локализуете свой модуль, нужно соблюдать осторожность, чтобы не вносить глобальные изменения, например во встроенное пространство имён. Следует использовать не API GNU gettext, а API на основе классов.
Допустим, модуль называется «spam», а его файлы перевода .mo для разных
естественных языков лежат в каталоге /usr/share/locale в формате GNU
gettext. Вот что нужно поместить в начало модуля:
import gettext
t = gettext.translation('spam', '/usr/share/locale')
_ = t.gettext
Локализация приложения¶Localizing your application
Если вы локализуете приложение, можно установить функцию _() глобально
во встроенное пространство имён, обычно в главном файле приложения.
Тогда во всех файлах приложения можно будет использовать _('...')
без явной установки в каждом из них.
В простейшем случае нужно добавить в главный файл приложения следующий код:
import gettext
gettext.install('myapplication')
Если требуется указать каталог с языковыми файлами, можно передать его
в функцию install():
import gettext
gettext.install('myapplication', '/usr/share/locale')
Переключение языков на лету¶Changing languages on the fly
Если программа должна одновременно поддерживать много языков, можно создать несколько экземпляров перевода и переключаться между ними явно:
import gettext
lang1 = gettext.translation('myapplication', languages=['en'])
lang2 = gettext.translation('myapplication', languages=['fr'])
lang3 = gettext.translation('myapplication', languages=['de'])
# начните с использования language1
lang1.install()
# ... проходит время, пользователь выбирает language 2
lang2.install()
# ... проходит ещё больше времени, пользователь выбирает language 3
lang3.install()
Отложенные переводы¶Deferred translations
В большинстве случаев строки переводятся там же, где и пишутся. Однако иногда нужно пометить строки для перевода, но отложить собственно перевод на потом. Классический пример:
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). Однако будьте осторожны, если во вложенном пространстве имён
уже есть определение _().
Обратите внимание, что второе использование _() не будет определено
программой 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().
Благодарности¶Acknowledgements
Следующие люди внесли код, отзывы, предложения по оформлению, предыдущие реализации и ценный опыт в создание этого модуля:
Peter Funk
James Henstridge
Juan David Ibáñez Palomar
Marc-André Lemburg
Martin von Löwis
François Pinard
Barry Warsaw
Gustavo Niemeyer
Сноски
- 1
Директория локали по умолчанию зависит от системы; например, в RedHat Linux это
/usr/share/locale, а в Solaris –/usr/lib/locale. Модульgettextне пытается поддерживать эти системно-зависимые значения по умолчанию; вместо этого его значение по умолчанию –sys.base_prefix/share/locale(см.sys.base_prefix). По этой причине всегда лучше вызыватьbindtextdomain()с явным абсолютным путём в начале приложения.- 2
См. сноску для
bindtextdomain()выше.