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, то возвращается текущая привязка для домена. 1
-
gettext.textdomain(domain=None) -
Изменить или запросить текущий глобальный домен. Если домен равен
None, возвращается текущий глобальный домен, в противном случае глобальный домен устанавливается в домен, который и возвращается.
-
gettext.gettext(message) -
Возвращает переведенный вариант сообщения, основанный на текущем глобальном домене, языке и каталоге локали. Эта функция обычно алиасится как
_()в локальном пространстве имен (см. примеры ниже).
-
gettext.dgettext(domain, message) -
Подобно
gettext(), но ищет сообщение в указанном домене.
-
gettext.ngettext(singular, plural, n) -
Подобно
gettext(), но учитывает формы множественного числа. Если перевод найден, применяется формула множественного числа к n, и возвращается полученное сообщение (некоторые языки имеют более двух форм множественного числа). Если перевод не найден, возвращает единственное число, если n равно 1; в противном случае возвращает множественное число.Формула множественного числа взята из заголовка каталога. Это выражение C или Python, имеющее свободную переменную n; выражение вычисляется в индекс множественного числа в каталоге. См. документацию GNU gettext для точного синтаксиса, используемого в файлах
.poи формул для различных языков.
-
gettext.dngettext(domain, singular, plural, n) -
Подобно
ngettext(), но ищет сообщение в указанном домене.
-
gettext.pgettext(context, message)
-
gettext.dpgettext(domain, context, message)
-
gettext.npgettext(context, singular, plural, n)
-
gettext.dnpgettext(domain, context, singular, plural, n) -
Аналогично соответствующим функциям без
pв префиксе (то есть,gettext(),dgettext(),ngettext(),dngettext()), но перевод ограничен заданным контекстом сообщения.Доступно начиная с версии 3.8.
Обратите внимание, что GNU gettext также определяет метод dcgettext(), но он считается бесполезным и поэтому в настоящее время не реализован.
Вот пример типичного использования этого API:
import gettext
gettext.bindtextdomain('myapplication', '/path/to/my/language/directory')
gettext.textdomain('myapplication')
_ = gettext.gettext
# ...
print(_('This is a translatable string.'))
Модуль API на основе классов
Модуль API на основе классов для модуля gettext предоставляет большую гибкость и удобство по сравнению с API GNU gettext. Это рекомендуемый способ локализации ваших приложений и модулей Python. gettext определяет класс GNUTranslations, который реализует разбор файлов формата GNU .mo и предоставляет методы для возвращения строк. Экземпляры этого класса также могут устанавливать себя в встроенное пространство имён как функцию _().
-
gettext.find(domain, localedir=None, languages=None, all=False) -
Эта функция реализует стандартный алгоритм поиска файлов
.mo. Она принимает параметр 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, функция возвращает список всех имён файлов в порядке их появления в списке languages или переменных окружения.
-
gettext.translation(domain, localedir=None, languages=None, class_=None, fallback=False) -
Возвращает экземпляр
*Translations, основанный на domain, localedir и languages, которые сначала передаются вfind()для получения списка путей к связанным файлам.mo. Экземпляры с одинаковыми именами файлов.moкешируются. Фактически, создаётся экземпляр класса class_, если он предоставлен, в противном случае —GNUTranslations. Конструктор класса должен принимать один аргумент — объект файла.Если найдено несколько файлов, более поздние файлы используются как резервные для предыдущих. Для возможности установки резервного копирования используется
copy.copy()для клонирования каждого объекта перевода из кеша; фактические данные экземпляра по-прежнему разделяются с кешем.Если файл
.moне найден, функция вызываетOSError, если fallback ложно (по умолчанию), и возвращает экземплярNullTranslations, если fallback истинно.Изменено в версии 3.11: Параметр codeset удалён.
-
gettext.install(domain, localedir=None, *, names=None) -
Устанавливает функцию
_()в пространстве имён встроенных функций Python на основе domain и localedir, которые передаются в функциюtranslation().Для параметра names см. описание метода объекта перевода
install().Как показано ниже, вы обычно помечаете строки в своём приложении, которые подлежат переводу, заключая их в вызов функции
_(), например так:print(_('This string will be translated.'))Для удобства вы хотите установить функцию
_()в пространстве имен встроенных функций Python, чтобы она была легко доступна во всех модулях вашего приложения.Изменено в версии 3.11: names теперь является параметром только для ключевого слова.
Класс 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 как резервный объект для текущего объекта перевода. Объект перевода должен обратиться к резервному копированию, если он не может предоставить перевод для данного сообщения.
-
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.
-
info() -
Возвращает словарь, содержащий метаданные, найденные в файле каталога сообщений.
-
charset() -
Возвращает кодировку файла каталога сообщений.
-
install(names=None) -
Этот метод устанавливает
gettext()в пространство имён встроенных функций, привязывая его к_.Если параметр names задан, он должен быть последовательностью, содержащей имена функций, которые вы хотите установить в пространстве имён встроенных функций помимо
_(). Поддерживаемые имена:'gettext','ngettext','pgettext', и'npgettext'.Обратите внимание, что это всего лишь один из способов, хотя и наиболее удобный, сделать функцию
_()доступной вашему приложению. Поскольку она влияет на всё приложение в целом, а именно на пространство имён встроенных функций, локализованные модули никогда не должны устанавливать_(). Вместо этого они должны использовать этот код, чтобы сделать_()доступным для своего модуля: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.
-
Поддержка каталогов сообщений 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.
(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», а различные языковые файлы перевода модуля .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().
Благодарности
Следующие люди внесли вклад в разработку этого модуля, предоставив код, отзывы, предложения по дизайну, предыдущие реализации и ценный опыт:
- Peter Funk
- James Henstridge
- Juan David Ibáñez Palomar
- Marc-André Lemburg
- Martin von Löwis
- François Pinard
- Barry Warsaw
- Gustavo Niemeyer
Примечания
-
1 -
Стандартный каталог локализации зависит от системы; например, в Red Hat 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.11/library/gettext.html