Locale.gettext: примеры (PYTHON)

Работа с locale.gettext в Python: локализация строковых сообщений
Раздел: Интернационализация, Локализация
locale.gettext(msg: str): str

Обзор функции locale.gettext в Python

Функция locale.gettext() является частью модуля locale в стандартной библиотеке Python. Она предоставляет интерфейс для получения локализованных строковых сообщений на основе системных настроек локали или переопределенных параметров. Использование данной функции характерно для интернационализации (i18n) и локализации (L10n) приложений.

Функция применяется в случаях, когда программное обеспечение требует адаптации текстового контента под языковые и региональные предпочтения пользователя. Типичные сценарии включают локализацию интерфейса, форматирование дат, чисел и валют с учетом региональных стандартов.

Синтаксис и параметры

Базовый синтаксис функции: locale.gettext(message).

  • message (строка, обязательный): исходное строковое сообщение на языке по умолчанию (обычно английском), которое требуется перевести. Эта строка служит ключом для поиска в файлах перевода (например, .po или .mo).

Возвращаемое значение

Функция возвращает локализованную строку, соответствующую переданному сообщению message, если перевод для текущей локали существует. В противном случае возвращается оригинальная строка message. Тип возвращаемого значения - строка (str).

Для корректной работы функции locale.gettext требуется предварительная настройка модуля locale с помощью функций locale.setlocale() и locale.bindtextdomain(). Эти вызовы устанавливают текущую локаль и указывают путь к каталогам с переводами.

Примеры применения locale.gettext

Базовый пример с установкой локали

Перед использованием gettext необходимо настроить локаль и привязать домен перевода.

import locale
import os

# Установка локали в 'ru_RU.UTF-8'
locale.setlocale(locale.LC_ALL, 'ru_RU.UTF-8')
# Привязка домена 'myapp' к каталогу с переводами
locale.bindtextdomain('myapp', os.path.join(os.path.dirname(__file__), 'locale'))
locale.textdomain('myapp')

# Пример вызова gettext
print(locale.gettext("Hello, world!"))
Привет, мир!

Пример без найденного перевода

Если перевод для сообщения отсутствует, возвращается исходная строка.

import locale

locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
print(locale.gettext("Untranslated string"))
Untranslated string

Использование с псевдопереводом для тестирования

Иногда используют псевдоперевод для проверки работы системы локализации.

import locale

locale.setlocale(locale.LC_ALL, 'C')
# В локали 'C' переводы не загружаются, возвращается оригинал
print(locale.gettext("Test message"))
Test message

Альтернативные функции в Python

В Python для задач интернационализации доступны несколько функций и модулей со схожей функциональностью.

gettext.gettext()

Функция gettext.gettext() из одноименного модуля является более низкоуровневым аналогом. Она не зависит от системных настроек локали, требует явной установки переводов через объекты GNUTranslations. Предпочтительнее использовать при необходимости тонкого контроля над загрузкой переводов или в embedded-средах.

gettext.pgettext()

Функция gettext.pgettext(context, message) позволяет указать контекст для перевода, что полезно при наличии омонимов. Используется, когда одно исходное сообщение может иметь разные переводы в зависимости от контекста.

gettext.ngettext()

Функция gettext.ngettext(singular, plural, n) предназначена для обработки множественных чисел. Она выбирает правильную форму перевода на основе числа n. Альтернатива locale.gettext для перевода строк с учетом количества.

Babel

Сторонняя библиотека Babel предоставляет расширенные возможности интернационализации, включая извлечение строк, форматирование дат, чисел, единиц измерения. Babel предпочтительнее для сложных проектов с необходимостью форматирования множества локализуемых данных.

Аналоги функции в других языках программирования

JavaScript (i18next)

Библиотека i18next предоставляет функционал для перевода строк с поддержкой пространств имен, контекста и множественного числа.

// Установка и настройка i18next
i18next.init({
  lng: 'ru',
  resources: {
    ru: { translation: { "Hello": "Привет" } }
  }
});
console.log(i18next.t('Hello'));
Привет

Java (ResourceBundle)

В Java для локализации используют класс ResourceBundle, который загружает свойства из файлов .properties на основе локали.

import java.util.ResourceBundle;
import java.util.Locale;

Locale locale = new Locale("ru", "RU");
ResourceBundle bundle = ResourceBundle.getBundle("Messages", locale);
String message = bundle.getString("hello");
System.out.println(message);
Привет

PHP (gettext)

В PHP встроены функции gettext, которые работают аналогично Python, но интегрированы с веб-сервером.

setlocale(LC_ALL, 'ru_RU.UTF-8');
bindtextdomain('myapp', './locale');
textdomain('myapp');
echo gettext("Hello");
Привет

C# (Resources и CultureInfo)

В .NET используют файлы ресурсов (.resx) и класс CultureInfo для управления локализацией.

System.Globalization.CultureInfo ci = new System.Globalization.CultureInfo("ru-RU");
System.Threading.Thread.CurrentThread.CurrentCulture = ci;
string message = MyApp.Resources.Strings.Hello;
Привет

Golang (golang.org/x/text/message)

Пакет message из golang.org/x/text предоставляет функции для интернационализации, похожие на gettext.

import "golang.org/x/text/message"

p := message.NewPrinter(message.MatchLanguage("ru"))
p.Println("Hello")
Привет

Основное отличие аналогов в других языках заключается в интеграции с экосистемой и дополнительными возможностями, такими как встроенная поддержка plural forms в JavaScript-библиотеках или автоматическое извлечение строк в Java.

Распространенные ошибки при работе с locale.gettext

Ошибка установки локали

Неправильное указание кода локали приводит к возврату исходных строк.

import locale

# Локаль 'ru_RU' может быть недоступна в системе
locale.setlocale(locale.LC_ALL, 'ru_RU')
print(locale.gettext("Hello"))
Hello

Отсутствие привязки домена перевода

Без вызова locale.bindtextdomain и locale.textdomain функция не сможет найти файлы переводов.

import locale

locale.setlocale(locale.LC_ALL, 'ru_RU.UTF-8')
# Домен не привязан
print(locale.gettext("Test"))
Test

Использование до настройки локали

Вызов gettext до установки локали с помощью setlocale может не дать ожидаемого результата.

import locale

print(locale.gettext("Hello"))  # Локаль еще не установлена
locale.setlocale(locale.LC_ALL, 'ru_RU.UTF-8')
Hello

Проблемы с кодировкой файлов перевода

Файлы .po должны быть в корректной кодировке (обычно UTF-8). Иначе возможны ошибки декодирования.

# Файл .po в кодировке Windows-1251 без указания charset
# При загрузке может возникнуть ошибка UnicodeDecodeError

Забытый вызов install() для gettext

При использовании модуля gettext напрямую, для замены стандартной функции _() требуется вызвать gettext.install().

import gettext
gettext.bindtextdomain('myapp', 'locale')
gettext.textdomain('myapp')
_ = gettext.gettext  # Без install() функция _ не будет глобально доступна
print(_("Hello"))

Изменения в функции в последних версиях Python

Функция locale.gettext остается стабильной в течение многих версий Python. Однако в модуле locale произошли изменения, влияющие на ее работу.

Python 3.11

В Python 3.11 улучшена обработка ошибок при установке локали. Функция setlocale теперь может возвращать более информативные сообщения об ошибках, что облегчает отладку проблем с gettext.

Python 3.8

В Python 3.8 добавлена поддержка локали "C.UTF-8" на некоторых платформах, что улучшает поведение locale.gettext в контейнеризованных средах, где локаль POSIX может быть недоступна.

Python 3.7

В Python 3.7 улучшена совместимость с платформами Windows, где ранее могли возникать проблемы с установкой локалей, отличных от системной.

Стоит отметить, что сам модуль gettext, на котором базируется locale.gettext, также развивается. В последних версиях улучшена поддержка формата .mo файлов и добавлены возможности для кастомизации.

Расширенные примеры использования locale.gettext

Локализация с использованием доменов

Можно использовать несколько доменов перевода в одном приложении.

Пример python
import locale
import os

locale.setlocale(locale.LC_ALL, 'ru_RU.UTF-8')
# Привязка двух доменов
locale.bindtextdomain('ui', '/path/to/ui/locale')
locale.bindtextdomain('errors', '/path/to/errors/locale')

# Установка активного домена
locale.textdomain('ui')
print(locale.gettext("Save"))  # Ищет в домене 'ui'

# Временное переключение домена для получения перевода ошибки
old_domain = locale.textdomain('errors')
print(locale.gettext("File not found"))
locale.textdomain(old_domain)  # Возврат к предыдущему домену
Сохранить
Файл не найден

Динамическая смена локали в рантайме

Локаль можно менять во время выполнения программы.

Пример python
import locale

def switch_locale(locale_name):
    locale.setlocale(locale.LC_ALL, locale_name)
    locale.bindtextdomain('myapp', 'locale')
    locale.textdomain('myapp')
    return locale.gettext

_ = switch_locale('ru_RU.UTF-8')
print(_("Hello"))

_ = switch_locale('de_DE.UTF-8')
print(_("Hello"))
Привет
Hallo

Интеграция с классами и объектами

Локализованные строки можно интегрировать в классы с помощью свойств.

Пример python
import locale

locale.setlocale(locale.LC_ALL, 'ru_RU.UTF-8')
locale.bindtextdomain('myapp', 'locale')
locale.textdomain('myapp')

class LocalizedApp:
    @property
    def welcome(self):
        return locale.gettext("Welcome to the application")
    
    @property
    def exit_message(self):
        return locale.gettext("Goodbye!")

app = LocalizedApp()
print(app.welcome)
print(app.exit_message)
Добро пожаловать в приложение
До свидания!

Кэширование переводов для производительности

При частом вызове gettext с одинаковыми аргументами можно использовать кэширование.

Пример python
import locale
from functools import lru_cache

locale.setlocale(locale.LC_ALL, 'ru_RU.UTF-8')
locale.bindtextdomain('myapp', 'locale')
locale.textdomain('myapp')

@lru_cache(maxsize=100)
def cached_gettext(msg):
    return locale.gettext(msg)

# Многократный вызов с одним аргументом
for _ in range(5):
    print(cached_gettext("Hello"))
Привет
Привет
Привет
Привет
Привет

Использование с шаблонизаторами

Функцию можно интегрировать в шаблонизаторы, например, Jinja2.

Пример python
import locale
from jinja2 import Environment

locale.setlocale(locale.LC_ALL, 'ru_RU.UTF-8')
locale.bindtextdomain('myapp', 'locale')
locale.textdomain('myapp')

env = Environment()
env.globals['_'] = locale.gettext
template = env.from_string("{{ _('Hello') }}, {{ name }}!")
print(template.render(name="Иван"))
Привет, Иван!

питон locale.gettext function comments

En
Locale.gettext Get the translation for a single message