Содержание страницы
22.1. gettext – Многоязычные службы интернационализации¶gettext – Multilingual internationalization services
Исходный код: Lib/gettext.py
Модуль gettext предоставляет службы интернационализации (I18N) и локализации (L10N) для модулей и приложений Python. Он поддерживает как API каталогов сообщений GNU gettext, так и API более высокого уровня, основанный на классах, который может лучше подходить для файлов Python. Описанный ниже интерфейс позволяет писать сообщения модуля и приложения на одном естественном языке и предоставлять каталог переведённых сообщений для работы на разных естественных языках.
Также даются некоторые советы по локализации модулей и приложений Python.
22.1.1. GNU gettext API¶
Модуль gettext определяет следующий API, очень похожий на API GNU gettext. Использование этого API повлияет на перевод всего приложения глобально. Часто это желательно, если приложение одноязычное и выбор языка зависит от локали пользователя. Если требуется локализовать модуль Python или приложению нужно переключать языки на лету, вероятно, следует использовать API на основе классов.
-
gettext.bindtextdomain(domain[, localedir])¶ Связывает domain с каталогом локали localedir. Если говорить точнее,
gettextбудет искать двоичные файлы.moдля заданного домена по пути (в Unix):localedir/language/LC_MESSAGES/domain.mo, где languages ищется в переменных окруженияLANGUAGE,LC_ALL,LC_MESSAGESиLANGсоответственно.Если localedir опущен или равен
None, то возвращается текущая привязка для domain. 1
-
gettext.bind_textdomain_codeset(domain[, codeset])¶ Привязывает domain к codeset, изменяя кодировку строк, возвращаемых семейством функций
gettext(). Если codeset опущен, возвращается текущая привязка.Новое в версии 2.4.
-
gettext.textdomain([domain])¶ Изменяет или запрашивает текущий глобальный домен. Если domain равен
None, возвращается текущий глобальный домен; в противном случае глобальный домен устанавливается в domain, который и возвращается.
-
gettext.gettext(message)¶ Возвращает локализованный перевод message на основе текущего глобального домена, языка и каталога локали. Эта функция обычно имеет псевдоним
_()в локальном пространстве имён (см. примеры ниже).
-
gettext.lgettext(message)¶ Эквивалентно
gettext(), но перевод возвращается в предпочтительной системной кодировке, если никакая другая кодировка не была явно задана с помощьюbind_textdomain_codeset().Новое в версии 2.4.
-
gettext.ldgettext(domain, message)¶ Эквивалентно
dgettext(), но перевод возвращается в предпочтительной системной кодировке, если никакая другая кодировка не была явно задана с помощьюbind_textdomain_codeset().Новое в версии 2.4.
-
gettext.ngettext(singular, plural, n)¶ Как
gettext(), но учитывает формы множественного числа. Если перевод найден, применяет формулу множественного числа к n и возвращает результирующее сообщение (в некоторых языках более двух форм множественного числа). Если перевод не найден, возвращает singular, если n равно 1; в противном случае возвращает plural.Формула множественного числа берётся из заголовка каталога. Это выражение на C или Python, использующее свободную переменную n; выражение вычисляет индекс формы множественного числа в каталоге. Обратитесь к документации GNU gettext для получения точного синтаксиса, используемого в файлах
.po, и формул для множества языков.Новое в версии 2.3.
-
gettext.lngettext(singular, plural, n)¶ Эквивалентно
ngettext(), но перевод возвращается в предпочтительной системной кодировке, если никакая другая кодировка не была явно задана с помощьюbind_textdomain_codeset().Новое в версии 2.4.
-
gettext.dngettext(domain, singular, plural, n)¶ Как
ngettext(), но ищет сообщение в указанном domain.Новое в версии 2.3.
-
gettext.ldngettext(domain, singular, plural, n)¶ Эквивалентно
dngettext(), но перевод возвращается в предпочтительной системной кодировке, если никакая другая кодировка не была явно задана с помощьюbind_textdomain_codeset().Новое в версии 2.4.
Обратите внимание, что 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.')
22.1.2. API на основе классов¶Class-based API
API на основе классов модуля gettext обеспечивает большую гибкость и удобство по сравнению с GNU gettext API. Это рекомендуемый способ локализации приложений и модулей Python. gettext определяет класс «translations», который реализует разбор файлов в формате GNU .mo и имеет методы для возврата как стандартных 8-битных строк, так и строк Unicode. Экземпляры этого класса «translations» также могут устанавливать себя во встроенное пространство имён как функцию _().
-
gettext.find(domain[, localedir[, languages[, all]]])¶ Эта функция реализует стандартный алгоритм поиска файлов
.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[, languages[, class_[, fallback[, codeset]]]]])¶ Возвращает экземпляр
Translationsна основе domain, localedir и languages, которые сначала передаются вfind()для получения списка соответствующих путей к файлам.mo. Экземпляры с одинаковыми именами файлов.moкэшируются. Фактически создаваемый класс – это либо class_, если он указан, либоGNUTranslations. Конструктор класса должен принимать один аргумент – объект файла. Если указан codeset, он изменит кодировку, используемую для кодирования переведённых строк.Если найдено несколько файлов, последующие файлы используются как запасные для предыдущих. Чтобы иметь возможность задать запасной вариант,
copy.copy()используется для клонирования каждого объекта перевода из кэша; фактические данные экземпляра по-прежнему являются общими с кэшем.Если файл
.moне найден, эта функция вызываетIOError, если fallback имеет значение false (по умолчанию), и возвращает экземплярNullTranslations, если fallback имеет значение true.Изменено в версии 2.4: Добавлен параметр codeset.
-
gettext.install(domain[, localedir[, unicode[, codeset[, names]]]])¶ Эта функция устанавливает функцию
_()во встроенное пространство имён Python на основе domain, localedir и codeset, которые передаются функцииtranslation(). Флаг unicode передаётся методуinstall()конечного объекта перевода.Описание параметра names см. в описании метода
install()объекта перевода.Как показано ниже, строки в приложении, предназначенные для перевода, обычно помечаются обёртыванием их в вызов функции
_(), например:print _('This string will be translated.')
Для удобства функцию
_()устанавливают в пространство имён встроенных функций Python, чтобы она была легко доступна во всех модулях приложения.Изменено в версии 2.4: Добавлен параметр codeset.
Изменено в версии 2.5: Добавлен параметр names.
22.1.2.1. Класс NullTranslations¶The NullTranslations class
Классы перевода – это то, что фактически реализует перевод строк сообщений исходного файла в переведённые строки сообщений. Базовый класс, используемый всеми классами перевода, – NullTranslations; он предоставляет базовый интерфейс, который можно использовать для написания собственных специализированных классов перевода. Ниже приведены методы NullTranslations:
-
class
gettext.NullTranslations([fp])¶ Принимает необязательный объект файла fp, который игнорируется базовым классом. Инициализирует «защищённые» переменные экземпляра _info и _charset, которые устанавливаются производными классами, а также _fallback, который устанавливается через
add_fallback(). Затем вызываетself._parse(fp), если fp не равенNone.-
_parse(fp)¶ В базовом классе этот метод не выполняет никаких действий; он принимает файловый объект fp, читает из файла данные и инициализирует свой каталог сообщений. Если формат файла каталога сообщений не поддерживается, следует переопределить этот метод для разбора такого формата.
-
add_fallback(fallback)¶ Добавляет fallback в качестве резервного объекта для текущего объекта перевода. Объект перевода должен обращаться к резервному, если не может предоставить перевод для заданного сообщения.
-
gettext(message)¶ Если резервный объект установлен, перенаправляет
gettext()резервному объекту. В противном случае возвращает переведённое сообщение. Переопределяется в производных классах.
-
lgettext(message)¶ Если резервный объект установлен, перенаправляет
lgettext()резервному объекту. В противном случае возвращает переведённое сообщение. Переопределяется в производных классах.Новое в версии 2.4.
-
ugettext(message)¶ Если резервный объект установлен, перенаправляет
ugettext()резервному объекту. В противном случае возвращает переведённое сообщение в виде строки Unicode. Переопределяется в производных классах.
-
ngettext(singular, plural, n)¶ Если резервный объект установлен, перенаправляет
ngettext()резервному объекту. В противном случае возвращает переведённое сообщение. Переопределяется в производных классах.Новое в версии 2.3.
-
lngettext(singular, plural, n)¶ Если резервный объект установлен, перенаправляет
lngettext()резервному объекту. В противном случае возвращает переведённое сообщение. Переопределяется в производных классах.Новое в версии 2.4.
-
ungettext(singular, plural, n)¶ Если задан резервный вариант, перенаправить
ungettext()в него. В противном случае возвращать переведённое сообщение как строку Unicode. Переопределяется в производных классах.Новое в версии 2.3.
-
info()¶ Возвращает «защищённую» переменную
_info.
-
charset()¶ Возвращает «защищённую» переменную
_charset.
-
output_charset()¶ Возвращает «защищённую» переменную
_output_charset, определяющую кодировку, используемую для возврата переведённых сообщений.Новое в версии 2.4.
-
set_output_charset(charset)¶ Изменяет «защищённую» переменную
_output_charset, определяющую кодировку, используемую для возврата переведённых сообщений.Новое в версии 2.4.
-
install([unicode[, names]])¶ Если флаг unicode равен false, этот метод устанавливает
self.gettext()во встроенное пространство имён, связывая его с_. Если unicode равен true, он связываетself.ugettext(). По умолчанию unicode равен false.Если задан параметр names, он должен быть последовательностью, содержащей имена функций, которые требуется установить во встроенное пространство имён в дополнение к
_(). Поддерживаемые имена:'gettext'(привязывается кself.gettext()илиself.ugettext()в зависимости от флага unicode),'ngettext'(привязывается кself.ngettext()илиself.ungettext()в зависимости от флага unicode),'lgettext'и'lngettext'.Обратите внимание, что это лишь один из способов, хоть и самый удобный, сделать функцию
_()доступной в приложении. Поскольку это влияет на всё приложение глобально, и в особенности на встроенное пространство имён, локализованные модули никогда не должны устанавливать_(). Вместо этого им следует использовать следующий код, чтобы сделать_()доступной в своём модуле:import gettext t = gettext.translation('mymodule', ...) _ = t.gettext
Это помещает
_()только в глобальное пространство имён модуля и поэтому влияет только на вызовы внутри этого модуля.Изменено в версии 2.5: Добавлен параметр names.
-
22.1.2.2. Класс GNUTranslations¶The GNUTranslations class
Модуль gettext предоставляет один дополнительный класс, производный от NullTranslations: GNUTranslations. Этот класс переопределяет _parse() для чтения .mo-файлов формата GNU gettext как в формате big-endian, так и little-endian. Он также преобразует как идентификаторы сообщений, так и строки сообщений в Unicode.
GNUTranslations извлекает необязательные метаданные из каталога переводов. В GNU gettext принято включать метаданные как перевод пустой строки. Эти метаданные представлены в стиле RFC 822 пар key: value и должны содержать ключ Project-Id-Version. Если найден ключ Content-Type, то свойство charset используется для инициализации «защищённой» переменной экземпляра _charset, по умолчанию None, если не найдено. Если указана кодировка charset, то все идентификаторы сообщений и строки сообщений, читаемые из каталога, преобразуются в Unicode с использованием этой кодировки. Метод ugettext() всегда возвращает строку Unicode, а метод gettext() возвращает закодированную 8-битную строку. Для аргументов идентификаторов сообщений обоих методов допустимы как строки Unicode, так и 8-битные строки, содержащие только символы US-ASCII. Обратите внимание, что версии методов, работающие с Unicode (т.е. ugettext() и ungettext()), являются рекомендуемым интерфейсом для интернационализированных программ на Python.
Весь набор пар ключ/значение помещается в словарь и устанавливается как
«защищённая» переменная экземпляра _info.
Если магическое число файла .mo недействительно или при чтении файла возникают другие проблемы, создание экземпляра класса GNUTranslations может вызвать IOError.
Следующие методы переопределены из реализации базового класса:
-
GNUTranslations.gettext(message)¶ Ищет идентификатор сообщения в каталоге и возвращает соответствующую строку сообщения в виде 8-битной строки, закодированной с использованием кодировки charset каталога, если она известна. Если в каталоге нет записи для идентификатора сообщения и задан резервный вариант, поиск перенаправляется в метод
gettext()резервного варианта. В противном случае возвращается идентификатор сообщения.
-
GNUTranslations.lgettext(message)¶ Эквивалентно
gettext(), но перевод возвращается в предпочтительной системной кодировке, если другая кодировка не была явно задана с помощьюset_output_charset().Новое в версии 2.4.
-
GNUTranslations.ugettext(message)¶ Ищет идентификатор сообщения в каталоге и возвращает соответствующую строку сообщения в виде строки Unicode. Если в каталоге нет записи для идентификатора сообщения и задан резервный вариант, поиск перенаправляется в метод
ugettext()резервного варианта. В противном случае возвращается идентификатор сообщения.
-
GNUTranslations.ngettext(singular, plural, n)¶ Выполняет поиск по множественным формам для идентификатора сообщения. singular используется как идентификатор сообщения для поиска в каталоге, а n используется для определения, какую множественную форму использовать. Возвращаемая строка сообщения представляет собой 8-битную строку, закодированную с использованием кодировки charset каталога, если она известна.
Если идентификатор сообщения не найден в каталоге и указан резервный вариант, запрос перенаправляется в метод
ngettext()резервного варианта. В противном случае, если n равно 1, возвращается singular, а во всех остальных случаях возвращается plural.Новое в версии 2.3.
-
GNUTranslations.lngettext(singular, plural, n)¶ Эквивалентно
gettext(), но перевод возвращается в предпочтительной системной кодировке, если другая кодировка не была явно задана с помощьюset_output_charset().Новое в версии 2.4.
-
GNUTranslations.ungettext(singular, plural, n)¶ Выполняет поиск формы множественного числа по идентификатору сообщения. singular используется как идентификатор сообщения для поиска в каталоге, а n используется для определения того, какую форму множественного числа использовать. Возвращаемая строка сообщения является строкой Unicode.
Если идентификатор сообщения не найден в каталоге и указан резервный вариант, запрос перенаправляется в метод
ungettext()резервного варианта. В противном случае, если n равно 1, возвращается singular, а во всех остальных случаях возвращается plural.Вот пример:
n = len(os.listdir('.')) cat = GNUTranslations(somefile) message = cat.ungettext( 'There is %(num)d file in this directory', 'There are %(num)d files in this directory', n) % {'num': n}
Новое в версии 2.3.
22.1.2.3. Поддержка каталогов сообщений Solaris¶Solaris message catalog support
Операционная система Solaris определяет собственный двоичный формат файлов .mo, но,
поскольку документации по этому формату найти не удалось, он в настоящее время не поддерживается.
22.1.2.4. Конструктор Catalog¶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), но, по-видимому, эта возможность не используется и поэтому в настоящее время не поддерживается.
22.1.3. Интернационализация программ и модулей¶Internationalizing your programs and modules
Интернационализация (I18N) – это процесс, благодаря которому программа начинает работать с несколькими языками. Локализация (L10N) – это адаптация уже интернационализированной программы к местному языку и культурным особенностям. Чтобы добавить в программу на Python поддержку нескольких языков, нужно выполнить следующие шаги:
подготовить программу или модуль, специально помечая переводимые строки
запустить набор инструментов над размеченными файлами для создания исходных каталогов сообщений
создание языковых переводов каталогов сообщений
использовать модуль
gettextдля корректного перевода строк сообщений
Чтобы подготовить код к интернационализации, нужно просмотреть все строки в
своих файлах. Любую строку, которую требуется перевести, следует пометить,
обернув её в вызов _('...') – то есть в функцию _(). Например:
filename = 'mylog.txt'
message = _('writing a log message')
fp = open(filename, 'w')
fp.write(message)
fp.close()
В этом примере строка 'writing a log message' помечена как кандидат на
перевод, а строки 'mylog.txt' и 'w' – нет.
Дистрибутив Python включает два инструмента, которые помогут создать каталоги сообщений после подготовки исходного кода. Они могут быть доступны или не доступны в бинарном дистрибутиве, но их можно найти в дистрибутиве исходных кодов, в каталоге Tools/i18n.
Программа pygettext 3 сканирует весь исходный код Python в поиске строк, помеченных ранее как переводимые. Она похожа на программу GNU gettext, за исключением того, что понимает все тонкости исходного кода Python, но ничего не знает об исходном коде C или C++. GNU gettext не нужна, если вы также не собираетесь переводить код C (например, модули расширения C).
pygettext создаёт текстовые человекочитаемые файлы каталогов сообщений в стиле Uniforum – .pot, по сути структурированные человекочитаемые файлы, содержащие каждую помеченную строку в исходном коде вместе с заполнителем для строк перевода. pygettext – это скрипт командной строки, поддерживающий интерфейс командной строки, похожий на xgettext; для подробностей об его использовании выполните:
pygettext.py --help
Копии этих файлов .pot затем передаются переводчикам, которые создают версии для каждого поддерживаемого естественного языка. Они возвращают заполненные языковые версии в виде файла .po. С помощью программы msgfmt.py 4 (в каталоге Tools/i18n) вы берёте файлы .po от переводчиков и создаёте машиночитаемые двоичные файлы каталогов .mo. Файлы .mo используются модулем gettext для фактической обработки перевода во время выполнения.
То, как используется модуль gettext в коде, зависит от того, интернационализируется
ли один модуль или всё приложение. В следующих двух разделах будет рассмотрен каждый случай.
22.1.3.1. Локализация вашего модуля¶Localizing your module
Если вы локализуете свой модуль, нужно быть осторожным, чтобы не вносить глобальные
изменения, например, во встроенное пространство имён. Не следует использовать GNU gettext
API, а вместо этого – API на основе классов.
Допустим, модуль называется «spam», а его файлы перевода .mo для разных
естественных языков лежат в каталоге /usr/share/locale в формате GNU
gettext. Вот что нужно поместить в начало модуля:
import gettext
t = gettext.translation('spam', '/usr/share/locale')
_ = t.lgettext
Если переводчики предоставляют строки Unicode в своих файлах .po, вместо этого следует сделать:
import gettext
t = gettext.translation('spam', '/usr/share/locale')
_ = t.ugettext
22.1.3.2. Локализация вашего приложения¶Localizing your application
Если вы локализуете приложение, можно установить функцию _() глобально
во встроенное пространство имён, обычно в главном файле приложения.
Тогда во всех файлах приложения можно будет использовать _('...')
без явной установки в каждом из них.
В простейшем случае нужно добавить в главный файл приложения следующий код:
import gettext
gettext.install('myapplication')
Если требуется задать каталог локали или флаг unicode, можно передать их в функцию install():
import gettext
gettext.install('myapplication', '/usr/share/locale', unicode=1)
22.1.3.3. Смена языка на лету¶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()
22.1.3.4. Отложенные переводы¶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). Однако будьте осторожны, если во вложенном пространстве имён
уже есть определение _().
Обратите внимание, что второе использование _() не будет распознано программой pygettext как переводимое, поскольку это не строка.
Другой способ – следующий пример:
def N_(message): return message
animals = [N_('mollusk'),
N_('albatross'),
N_('rat'),
N_('penguin'),
N_('python'), ]
# ...
for a in animals:
print _(a)
В этом случае переводимые строки помечаются функцией N_(), 5, которая не конфликтует ни с каким определением _(). Однако потребуется обучить программу извлечения сообщений искать переводимые строки, помеченные N_(). pygettext и xpot обе поддерживают это с помощью параметров командной строки.
22.1.3.5. gettext() и lgettext()¶gettext() vs. lgettext()
В Python 2.4 было введено семейство функций lgettext(). Их цель – предоставить альтернативу, более совместимую с текущей реализацией GNU gettext. В отличие от gettext(), которая возвращает строки, закодированные с использованием той же кодировки, что и в файле перевода, lgettext() будет возвращать строки, закодированные в предпочтительной системной кодировке, возвращаемой locale.getpreferredencoding(). Также обратите внимание, что Python 2.4 вводит новые функции для явного выбора кодировки, используемой в переведённых строках. Если кодировка задана явно, даже lgettext() будет возвращать переведённые строки в запрошенной кодировке, как и следовало ожидать в реализации GNU gettext.
22.1.4. Благодарности¶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.prefix/share/locale. По этой причине всегда лучше вызыватьbindtextdomain()с явным абсолютным путём в начале приложения.- 2
См. сноску для
bindtextdomain()выше.- 3
Франсуа Пинар написал программу под названием xpot, которая выполняет аналогичную задачу. Она доступна как часть его пакета po-utils.
- 4
msgfmt.py двоично совместим с GNU msgfmt, за исключением того, что предоставляет более простую реализацию на чистом Python. С помощью этого и pygettext.py обычно не требуется устанавливать пакет GNU gettext для интернационализации приложений Python.
- 5
Выбор
N_()здесь абсолютно произволен; с тем же успехом это мог бытьMarkThisStringForTranslation().