gettext — Услуги многоязычной локализации
Исходный код: Lib/gettext.py
Модуль gettext предоставляет услуги интернационализации (I18N) и локализации (L10N) для ваших Python-модулей и приложений. Он поддерживает как API каталога сообщений GNU gettext, так и более высокоуровневый, основанный на классах API, который может быть более подходящим для Python-файлов. Описанный ниже интерфейс позволяет вам записывать сообщения модуля и приложения на одном естественном языке и предоставлять каталог переведённых сообщений для выполнения под различными естественными языками.
Также приведены некоторые советы по локализации ваших Python-модулей и приложений.
API GNU gettext
Модуль gettext определяет следующий API, который очень похож на API GNU gettext. Если вы используете этот 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.bind_textdomain_codeset(domain, codeset=None) -
Связывает область с codeset, изменяя кодировку строковых байтов, возвращаемых функциями
lgettext(),ldgettext(),lngettext()иldngettext(). Если codeset опущен, возвращается текущая привязка.Устарело начиная с версии 3.8, удалено в версии 3.10.
-
gettext.textdomain(domain=None) -
Изменить или запросить текущую глобальную область. Если domain —
None, то возвращается текущая глобальная область, иначе глобальная область устанавливается в domain, которая и возвращается.
-
gettext.gettext(message) -
Возвращает переведённый текст message, основанный на текущей глобальной области, языке и каталоге локали. Эта функция обычно алиасируется как
_()в локальном пространстве имен (см. примеры ниже).
-
gettext.dgettext(domain, message) -
Как
gettext(), но ищет сообщение в указанной domain.
-
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 на основе классов
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, основанный на домене, localedir и languages, которые сначала передаются вfind()для получения списка путей к файлам.mo. Экземпляры с одинаковыми именами файлов.moкэшируются. Фактически, экземпляр класса class_, если он предоставлен, в противном случае —GNUTranslations. Конструктор класса должен принимать единственный аргумент — объект файла объект файла. Если предоставлен, параметр 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, основываясь на домене, localedir и codeset, которые передаются функцииtranslation().Для параметра names см. описание метода объекта перевода
install().Как показано ниже, вы обычно помечаете строки в своём приложении, которые подлежат переводу, обернув их в вызов функции
_(), как в этом примере:print(_('This string will be translated.'))Для удобства вы хотите установить функцию
_()в пространстве имен встроенных функций Python, чтобы она была легко доступна во всех модулях вашего приложения.Устаревшее с версии 3.8, удалено в версии 3.10: Параметр codeset.
Класс NullTranslations
Классы переводов — это те, что фактически реализуют перевод строк сообщений исходного файла на строки сообщений, переведённые на другой язык. Базовый класс, используемый всеми классами переводов, — NullTranslations; он предоставляет базовый интерфейс, который можно использовать для написания собственных специализированных классов переводов. Вот методы NullTranslations:
-
class gettext.NullTranslations(fp=None) -
Принимает необязательный объект файла fp, который игнорируется базовым классом. Инициализирует «защищённые» переменные экземпляра _info и _charset, которые устанавливаются производными классами, а также _fallback, который устанавливается с помощью
add_fallback(). Затем он вызываетself._parse(fp), если fp неNone.-
_parse(fp) -
Пустая операция в базовом классе, этот метод принимает объект файла fp и читает данные из файла, инициализируя его каталог сообщений. Если у вас формат файла каталога сообщений не поддерживается, вы должны переопределить этот метод для парсинга вашего формата.
-
add_fallback(fallback) -
Добавляет fallback в качестве объекта fallback для текущего объекта перевода. Объект перевода должен обращаться к fallback, если он не может предоставить перевод для данного сообщения.
-
gettext(message) -
Если fallback установлен, передаёт
gettext()в fallback. В противном случае возвращает message. Переопределяется в производных классах.
-
ngettext(singular, plural, n) -
Если fallback установлен, передаёт
ngettext()в fallback. В противном случае возвращает singular, если n равно 1; в противном случае возвращает plural. Переопределяется в производных классах.
-
pgettext(context, message) -
Если fallback установлен, передаёт
pgettext()в fallback. В противном случае возвращает переведённое сообщение. Переопределяется в производных классах.Новое в версии 3.8.
-
npgettext(context, singular, plural, n) -
Если fallback установлен, передаёт
npgettext()в fallback. В противном случае возвращает переведённое сообщение. Переопределяется в производных классах.Новое в версии 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'.Обратите внимание, что это лишь один из способов, хотя и наиболее удобный, сделать функцию
_()доступной для вашего приложения. Поскольку это влияет на всё приложение в целом и, конкретно, на пространство имён built-in, локальные модули никогда не должны устанавливать_(). Вместо этого они должны использовать этот код, чтобы сделать_()доступным для своего модуля:import gettext t = gettext.translation('mymodule', ...) _ = t.gettextЭто помещает
_()только в глобальное пространство имён модуля, и поэтому это влияет только на вызовы в этом модуле.Изменено в версии 3.8: Добавлены
'pgettext'и'npgettext'.
-
Класс GNUTranslations
Модуль gettext предоставляет один дополнительный класс, производный от NullTranslations: GNUTranslations. Этот класс переопределяет _parse() для поддержки чтения файлов в формате GNU gettext .mo в формате как big-endian, так и little-endian.
Класс GNUTranslations анализирует необязательные метаданные из каталога переводов. В соответствии с соглашениями GNU gettext метаданные включены как перевод для пустой строки. Эти метаданные представлены в формате пар key: value в стиле RFC 822 и должны содержать ключ Project-Id-Version. Если найден ключ Content-Type, то свойство charset используется для инициализации переменной экземпляра «защищённого» _charset, по умолчанию None, если не найдено. Если указано кодирование символов, то все идентификаторы сообщений и строки сообщений, считываемые из каталога, преобразуются в Unicode с использованием этого кодирования, в противном случае предполагается ASCII.
Поскольку идентификаторы сообщений также считываются как строки Unicode, все методы *gettext() будут предполагать идентификаторы сообщений как строки Unicode, а не байтовые строки.
Весь набор пар ключ/значение помещается в словарь и устанавливается в качестве переменной экземпляра «защищённого» _info.
Если магическое число файла .mo недействительно, номер основной версии неожиданный или при чтении файла возникают другие проблемы, создание экземпляра класса GNUTranslations может вызвать OSError.
-
class gettext.GNUTranslations -
Следующие методы переопределены из реализации базового класса:
-
gettext(message) -
Ищет идентификатор сообщения в каталоге и возвращает соответствующую строку сообщения в виде строки Unicode. Если в каталоге нет записи для идентификатора сообщения, а резервный вариант установлен, поиск перенаправляется на метод
gettext()резервного варианта. В противном случае возвращается идентификатор сообщения.
-
ngettext(singular, plural, n) -
Выполняет поиск сообщения по множественным формам. singular используется как идентификатор сообщения для поиска в каталоге, а n используется для определения используемой формы множественного числа. Возвращаемая строка сообщения — строка Unicode.
Если идентификатор сообщения не найден в каталоге, а резервный вариант задан, запрос перенаправляется на метод
ngettext()резервного варианта. В противном случае, когда n равно 1, возвращается singular, а в остальных случаях — plural.Вот пример:
n = len(os.listdir('.')) cat = GNUTranslations(somefile) message = cat.ngettext( 'There is %(num)d file in this directory', 'There are %(num)d files in this directory', n) % {'num': n}
-
pgettext(context, message) -
Ищет идентификатор контекста и сообщения в каталоге и возвращает соответствующую строку сообщения в виде строки Unicode. Если в каталоге нет записи для идентификатора сообщения и контекста, а резервный вариант установлен, поиск перенаправляется на метод
pgettext()резервного варианта. В противном случае возвращается идентификатор сообщения.Добавлена в версии 3.8.
-
npgettext(context, singular, plural, n) -
Выполняет поиск сообщения по множественным формам. singular используется как идентификатор сообщения для поиска в каталоге, а n используется для определения используемой формы множественного числа.
Если идентификатор сообщения для контекста не найден в каталоге, а резервный вариант задан, запрос перенаправляется на метод
npgettext()резервного варианта. В противном случае, когда n равно 1, возвращается singular, а в остальных случаях — plural.Добавлена в версии 3.8.
-
lgettext(message)
-
lngettext(singular, plural, n) -
Эквивалентно
gettext()иngettext(), но перевод возвращается как строка байтов, закодированная в предпочтительной системной кодировке, если кодирование не было явно задано с помощьюset_output_charset().Предупреждение
От этих методов следует отказаться в Python 3. См. предупреждение для функции
lgettext().Устарело с версии 3.8, удалено в версии 3.10.
-
Поддержка каталогов сообщений Solaris
Операционная система Solaris определяет свой собственный двоичный формат файла .mo, но поскольку документация по этому формату отсутствует, он в настоящее время не поддерживается.
Конструктор каталога
GNOME использует версию модуля gettext от James Henstridge, но у этой версии немного отличается API. Документированное использование было:
import gettext
cat = gettext.Catalog(domain, localedir)
_ = cat.gettext
print(_('hello world'))
Для совместимости со старым модулем функция Catalog() является псевдонимом функции translation(), описанной выше.
Различие между этим модулем и модулем Henstridge: объекты его каталога поддерживали доступ через API отображения, но этот API, похоже, не используется, поэтому он в настоящее время не поддерживается.
Локализация ваших программ и модулей
Локализация (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 также включает чистые версии этих программ на Python, называемые pygettext.py и msgfmt.py; некоторые дистрибутивы Python установят их для вас. pygettext.py похож на xgettext, но понимает только исходный код Python и не может обрабатывать другие языки программирования, такие как C или C++. pygettext.py поддерживает командную строку, аналогичную xgettext; для получения подробной информации о его использовании, выполните pygettext.py
--help. msgfmt.py двоично совместим с GNU msgfmt. С этими двумя программами вам, возможно, не понадобится пакет GNU gettext для локализации ваших приложений Python.)
xgettext, pygettext и аналогичные инструменты генерируют файлы .po — это каталоги сообщений. Это структурированные читаемые человеком файлы, содержащие каждую помеченную строку в исходном коде вместе с заполнителем для переведённых версий этих строк.
Копии этих .po файлов затем передаются индивидуальным переводчикам, которые пишут переводы для каждого поддерживаемого естественного языка. Они отправляют обратно завершённые языковые версии в виде файла <language-name>.po, который компилируется в машиночитаемый .mo двоичный каталог с помощью программы msgfmt. Файлы .mo используются модулем gettext для фактической обработки перевода во время выполнения.
Способ использования модуля gettext в вашем коде зависит от того, локализуете ли вы отдельный модуль или всё ваше приложение. В следующих двух разделах будет рассмотрено каждый случай.
Локализация вашего модуля
Если вы локализуете свой модуль, вы должны позаботиться о том, чтобы не вносить глобальные изменения, например, в встроенное пространство имён. Вы не должны использовать API GNU gettext, а вместо этого используйте API на основе классов.
Предположим, ваш модуль называется «spam», а различные файлы перевода на естественные языки модуля находятся в /usr/share/locale в формате GNU gettext. Вот что вы бы поместили вверху своего модуля:
import gettext
t = gettext.translation('spam', '/usr/share/locale')
_ = t.gettext
Локализация вашего приложения
Если вы локализуете своё приложение, вы можете установить функцию _() глобально во встроенное пространство имён, обычно в главном файле драйвера вашего приложения. Это позволит всем файлам вашего приложения просто использовать _('...') без явного его установки в каждом файле.
В простейшем случае вам нужно добавить только следующий фрагмент кода в главный файл драйвера вашего приложения:
import gettext
gettext.install('myapplication')
Если вам нужно установить каталог локалей, вы можете передать его в функцию install():
import gettext
gettext.install('myapplication', '/usr/share/locale')
Изменение языков в режиме реального времени
Если вашей программе нужно поддерживать множество языков одновременно, вы можете создать несколько экземпляров переводов и затем явно переключаться между ними, например, так:
import gettext
lang1 = gettext.translation('myapplication', languages=['en'])
lang2 = gettext.translation('myapplication', languages=['fr'])
lang3 = gettext.translation('myapplication', languages=['de'])
# start by using language1
lang1.install()
# ... time goes by, user selects language 2
lang2.install()
# ... more time goes by, user selects language 3
lang3.install()
Отложенные переводы
В большинстве ситуаций программирования строки переводятся там, где они кодируются. Однако иногда вам нужно пометить строки для перевода, но отложить фактический перевод на более поздний срок. Классический пример:
animals = ['mollusk',
'albatross',
'rat',
'penguin',
'python', ]
# ...
for a in animals:
print(a)
Здесь вы хотите пометить строки в списке animals как переводимые, но вы не хотите переводить их до тех пор, пока они не будут напечатаны.
Вот один способ решения этой ситуации:
def _(message): return message
animals = [_('mollusk'),
_('albatross'),
_('rat'),
_('penguin'),
_('python'), ]
del _
# ...
for a in animals:
print(_(a))
Это работает, потому что псевдоним _() просто возвращает строку без изменений. И этот псевдоним временно переопределит любое определение _() во встроенном пространстве имён (до команды del). Однако будьте осторожны, если у вас есть предыдущее определение _() в локальном пространстве имён.
Обратите внимание, что второй вызов _() не идентифицирует «а» как переводимый для программы 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()выше.
© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.10/library/gettext.html