Spec-Zone.ru › Python 2.7

gettext — Услуги многоязычной локализации

Исходный код: Lib/gettext.py

Модуль gettext предоставляет услуги интернационализации (I18N) и локализации (L10N) для ваших модулей и приложений Python. Он поддерживает как API каталога сообщений GNU gettext , так и API на основе классов, который может быть более подходящим для файлов Python. Описанный ниже интерфейс позволяет вам записывать сообщения своего модуля и приложения на одном естественном языке и предоставлять каталог переведённых сообщений для работы на разных естественных языках.

Также приведены некоторые советы по локализации ваших модулей и приложений Python.

1. API GNU gettext

Модуль gettext определяет следующий API, очень похожий на API GNU gettext. Если вы используете этот API, вы повлияете на перевод всего вашего приложения глобально. Часто это именно то, что вам нужно, если ваше приложение моноязычное, а выбор языка зависит от локали вашего пользователя. Если вы локализуете модуль Python или ваше приложение должно переключаться между языками в динамике, вам, вероятно, лучше использовать API на основе классов.

gettext.bindtextdomain(domain[, localedir])

Связывает домен с каталогом локали 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])

Связывает домен с 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.dgettext(domain, message)

Как gettext(), но ищет сообщение в указанном домене.

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(), но ищет сообщение в указанном домене.

Новое в версии 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.')

2. API на основе классов

API на основе классов модуля gettext предоставляет вам большую гибкость и удобство по сравнению с API GNU gettext. Это рекомендуемый способ локализации ваших приложений и модулей Python. gettext определяет класс «переводы», который реализует обработку файлов формата GNU .mo, и имеет методы для возврата стандартных строк с 8-битными данными или строк Unicode. Экземпляры этого класса «переводы» также могут устанавливать себя в встроенное пространство имён в качестве функции _().

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 на основе домена, каталога локалей и языков, которые сначала передаются в find() для получения списка связанных путей к файлам .mo. Экземпляры с идентичными именами файлов .mo кэшируются. Фактически, создаваемый класс — это class_, если он предоставлен, иначе GNUTranslations. Конструктор класса должен принимать один аргумент — объект файла. Если предоставлен codeset, он изменит кодировку, используемую для кодирования переведённых строк.

Если найдено несколько файлов, более поздние файлы используются как резервные варианты для более ранних. Для возможности настройки резервного варианта используется copy.copy() для клонирования каждого объекта перевода из кэша; фактические данные экземпляра всё ещё совместно используются с кэшем.

Если файл .mo не найден, эта функция вызывает IOError, если fallback ложно (что является значением по умолчанию), и возвращает экземпляр NullTranslations, если fallback истинно.

Изменено в версии 2.4: Добавлен параметр codeset.

gettext.install(domain[, localedir[, unicode[, codeset[, names]]]])

Устанавливает функцию _() в пространство имён встроенных функций Python, на основе домена, каталога локалей и кодировки, которые передаются функции translation(). Флаг unicode передаётся методу install() результирующего объекта перевода.

Для параметра names см. описание метода объекта перевода install().

Как показано ниже, вы обычно отмечаете строки в своём приложении, которые могут быть переведены, заключив их в вызов функции _(), например так:

print _('This string will be translated.')

Для удобства вы хотите, чтобы функция _() была установлена в пространстве имён встроенных функций Python, чтобы к ней легко можно было получить доступ во всех модулях вашего приложения.

Изменено в версии 2.4: Добавлен параметр codeset.

Изменено в версии 2.5: Добавлен параметр names.

2.1. Класс NullTranslations

Классы перевода фактически реализуют перевод строк сообщений исходного файла на переведённые строки сообщений. Базовый класс, используемый всеми классами перевода, — это 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 ложный, этот метод устанавливает self.gettext() в пространство имён встроенных функций, связывая его с _. Если unicode истинный, он связывает self.ugettext() вместо этого. По умолчанию unicode ложный.

Если параметр 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.

2.2. Класс GNUTranslations

Модуль gettext предоставляет один дополнительный класс, производный от NullTranslations: GNUTranslations. Этот класс переопределяет _parse() для поддержки чтения файлов формата GNU gettext .mo в формате как big-endian, так и little-endian. Он также преобразует идентификаторы сообщений и строки сообщений в Unicode.

GNUTranslations извлекает необязательные метаданные из каталога переводов. По соглашению с GNU gettext, метаданные включены в качестве перевода для пустой строки. Эти метаданные находятся в парах в формате RFC 822 key: value и должны содержать ключ Project-Id-Version. Если найден ключ Content-Type, то свойство charset используется для инициализации переменной экземпляра «защищенного» _charset, по умолчанию None, если не найдено. Если указана кодировка символов, все идентификаторы сообщений и строки сообщений, считанные из каталога, преобразуются в Unicode с использованием этой кодировки. Метод ugettext() всегда возвращает Unicode, тогда как gettext() возвращает закодированную строку в 8 бит. В качестве аргументов идентификаторов сообщений для обоих методов допустимы либо строки Unicode, либо строки в 8 бит, содержащие только символы US-ASCII. Обратите внимание, что версия методов Unicode (т.е. ugettext() и ungettext()) — рекомендуемый интерфейс для использования в международных программах Python.

Весь набор пар ключ/значение помещается в словарь и устанавливается в качестве переменной экземпляра «защищенного» _info.

Если магическое число файла .mo недействительно или возникают другие проблемы при чтении файла, создание экземпляра класса GNUTranslations может вызвать IOError.

Следующие методы переопределены по сравнению с реализацией базового класса:

GNUTranslations.gettext(message)

Ищет идентификатор сообщения в каталоге и возвращает соответствующую строку сообщения в виде строки в 8 бит, закодированной с использованием кодировки символов каталога, если она известна. Если в каталоге нет записи для идентификатора сообщения, а резервный вариант установлен, поиск перенаправляется на метод резервного варианта gettext(). В противном случае возвращается идентификатор сообщения.

GNUTranslations.lgettext(message)

Эквивалентно gettext(), но перевод возвращается в предпочтительной кодировке системы, если никакая другая кодировка явно не была установлена с помощью set_output_charset().

Добавлена в версии 2.4.

GNUTranslations.ugettext(message)

Ищет идентификатор сообщения в каталоге и возвращает соответствующую строку сообщения в виде строки Unicode. Если в каталоге нет записи для идентификатора сообщения, а резервный вариант установлен, поиск перенаправляется на метод резервного варианта ugettext() метода. В противном случае возвращается идентификатор сообщения.

GNUTranslations.ngettext(singular, plural, n)

Выполняет поиск по множественным формам сообщения. singular используется как идентификатор сообщения для поиска в каталоге, в то время как n используется для определения того, какую форму множественного числа использовать. Возвращаемая строка сообщения представляет собой строку в 8 бит, закодированную с использованием кодировки символов каталога, если она известна.

Если идентификатор сообщения не найден в каталоге и указан резервный вариант, запрос перенаправляется на метод резервного варианта 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.

2.3. Поддержка каталогов сообщений Solaris

Операционная система Solaris определяет собственный двоичный формат файла .mo, но поскольку по этому формату нет документации, он в настоящее время не поддерживается.

2.4. Конструктор каталога

GNOME использует версию модуля gettext Джеймса Хенстриджа, но у этой версии немного другой API. Документированное использование было:

import gettext
cat = gettext.Catalog(domain, localedir)
_ = cat.gettext
print _('hello world')

Для совместимости со старым модулем функция Catalog() является псевдонимом для функции translation(), описанной выше.

Различие между этим модулем и модулем Хенстриджа: объекты его каталогов поддерживали доступ через API отображения, но, похоже, это не используется, поэтому в настоящее время не поддерживается.

3. Интернационализация ваших программ и модулей

Интернационализация (I18N) относится к операции, в результате которой программа становится знакомой с несколькими языками. Локализация (L10N) относится к адаптации вашей программы, после интернационализации, к местному языку и культурным обычаям. Чтобы обеспечить многоязычные сообщения для ваших программ Python, необходимо выполнить следующие шаги:

  1. подготовьте свою программу или модуль, специально пометив переводимые строки
  2. запустите набор инструментов над помеченными файлами для создания исходных каталогов сообщений
  3. создайте переводы каталогов сообщений на языки
  4. используйте модуль gettext, чтобы строки сообщений были надлежащим образом переведены

Для подготовки кода к I18N необходимо просмотреть все строки в ваших файлах. Любая строка, которая должна быть переведена, должна быть помечена путем ее обертывания в _('...') — то есть, вызовом функции _(). Например:

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 в вашем коде зависит от того, интернационализируете ли вы один модуль или все ваше приложение. В следующих двух разделах будет обсуждаться каждый случай.

3.1. Локализация вашего модуля

Если вы локализуете свой модуль, вы должны позаботиться о том, чтобы не вносить глобальные изменения, например, в встроенное пространство имен. Вы не должны использовать API GNU gettext, а вместо этого — API на основе класса.

Допустим, ваш модуль называется «spam», а различные файлы перевода на естественные языки модуля находятся в /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

3.2. Локализация вашего приложения

Если вы локализуете своё приложение, вы можете установить функцию _() глобально в встроенное пространство имён, обычно в основном файле драйвера вашего приложения. Это позволит всем файлам вашего приложения использовать _('...') без явного его установки в каждом файле.

В простом случае вам нужно добавить следующий фрагмент кода в основной файл драйвера вашего приложения:

import gettext
gettext.install('myapplication')

Если вам нужно установить каталог локалей или флаг unicode, вы можете передать их в функцию install():

import gettext
gettext.install('myapplication', '/usr/share/locale', unicode=1)

3.3. Изменение языков в режиме реального времени

Если вашей программе нужно поддерживать несколько языков одновременно, вы можете создать несколько экземпляров перевода и затем явно переключаться между ними, например так:

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()

3.4. Отложенные переводы

В большинстве случаев программирования строки переводятся там, где они кодируются. Однако иногда нужно пометить строки для перевода, но отложить фактический перевод на более поздний срок. Классический пример:

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» как подлежащую переводу для программы 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 обе поддерживают это с помощью переключателей командной строки.

3.5. gettext() против lgettext()

В Python 2.4 была введена семейство функций lgettext(). Целью этих функций является предоставление альтернативы, которая лучше соответствует текущей реализации GNU gettext. В отличие от gettext(), которая возвращает строки, закодированные тем же кодировкой, что и в файле перевода, lgettext() вернёт строки, закодированные в предпочтительной системной кодировке, как возвращается функцией locale.getpreferredencoding(). Также обратите внимание, что Python 2.4 вводит новые функции для явного выбора кодировки, используемой в переведённых строках. Если кодировка установлена явно, даже lgettext() вернёт переведённые строки в запрошенной кодировке, как ожидалось в реализации GNU gettext.

4. Благодарности

Следующие люди внесли вклад в создание этого модуля: код, отзывы, предложения по дизайну, предыдущие реализации и ценный опыт:

  • 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().

© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/2.7/library/gettext.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API