Локализация Python с gettext
Этот практический гид показывает, как быстро сделать консольное Python‑приложение многоязычным с помощью встроенного модуля gettext. Мы пройдемся по пометке строк, созданию .po/.mo файлов, настройке каталогов локалей и добавим полезные методики, проверки и примеры для реального проекта.

Введение
Локализация (l10n) и интернационализация (i18n) часто пугают. В Python задача проще: модуль gettext разделяет логику приложения и тексты для пользователя. Это облегчает перевод и поддержку множества языков.
В этой статье вы найдете пошаговый пример на простом CLI‑приложении, а также практичные подсказки для расширения: работа с контекстом, множественным числом, тестированием переводов и стратегиями развертывания.
Короткая терминология
- gettext — модуль Python для загрузки переводов.
- .pot — шаблон (template) с исходными строками.
- .po — человекочитаемый файл перевода (Portable Object).
- .mo — скомпилированный двоичный файл для быстрого чтения (Machine Object).
Шаг 1: Одноязычное приложение (исходник)
Начнем с простого примера — приложение приветствует пользователя и спрашивает имя. Все строки сейчас захардкожены.
# app.py
def run_app():
print("Welcome to the multilingual demo!")
name = input("Please enter your name: ")
print(f"Hello, {name}! How are you today?")
if __name__ == "__main__":
run_app()Это работает, но только на одном языке. Наша цель — отделить тексты от кода, чтобы добавлять новые языки без изменения логики.
Шаг 2: Помечаем строки для перевода
Обертка _() — устоявшаяся конвенция для gettext. Она делает код читаемым и удобным для инструментов извлечения строк.
# app.py
import gettext
# Set up a placeholder for the gettext function.
# This makes it easy to replace with a real translation
# function later, as you'll see.
_ = gettext.gettext
def run_app():
print(_("Welcome to the multilingual demo!"))
name = input(_("Please enter your name: "))
print(_(f"Hello, {name}! How are you today?"))
if __name__ == "__main__":
run_app()Важно: не переводите внутри форматируемых строк до момента выполнения перевода. Лучше использовать f‑строки или .format с переменными, но помните о порядке аргументов и синтаксисе языков.
Шаг 3: Структура каталогов и создание шаблона
Стандартная структура локалей выглядит так:
└── my_app/
├── app.py
└── locales/
├── en_US/
│ └── LC_MESSAGES/
│ └── base.po
└── es_ES/
└── LC_MESSAGES/
└── base.poИспользуйте инструменты вроде xgettext или pybabel для извлечения строк в .pot файл.
xgettext --language=Python --keyword=_ --output=locales/base.pot app.pyЭтот шаг создаст locales/base.pot с набором строк, помеченных _().
Затем создайте .po для каждого языка:
msginit --locale=es_ES --input=locales/base.pot --output-file=locales/es_ES/LC_MESSAGES/base.po
msginit --locale=en_US --input=locales/base.pot --output-file=locales/en_US/LC_MESSAGES/base.poШаг 4: Заполнение .po файла переводом
Файл .po прост: msgid — оригинал, msgstr — перевод. Переводчики редактируют только msgstr.
# locales/es_ES/LC_MESSAGES/base.po
msgid ""
msgstr ""
"Project-Id-Version: Multilingual Demo\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: es_ES\n"
msgid "Welcome to the multilingual demo!"
msgstr "¡Bienvenido a la demostración multilingüe!"
msgid "Please enter your name: "
msgstr "Por favor, introduce tu nombre: "
msgid "Hello, {name}! How are you today?"
msgstr "¡Hola, {name}! ¿Cómo estás hoy?"Советы переводчику:
- Не изменяйте msgid.
- Сохраняйте placeholder’ы {name} в переводе.
- Учитывайте порядок слов и пунктуацию в целевом языке.
Шаг 5: Компиляция .po в .mo и загрузка в Python
Скомпилируйте .po в .mo:
msgfmt -o locales/es_ES/LC_MESSAGES/base.mo locales/es_ES/LC_MESSAGES/base.poЗатем настройте загрузку переводов в коде:
# app.py
import gettext
import os
# Define the root directory for our translation files
LOCALE_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'locales')
def setup_language(lang_code):
try:
# Load the translation from the locale directory
translation = gettext.translation('base', localedir=LOCALE_DIR, languages=[lang_code])
# Install the translation globally so gettext can find it
translation.install()
except FileNotFoundError:
# Fallback to a default language (e.g., English) if translation isn't found
print(f"Warning: Translation for '{lang_code}' not found. Using default language.")
gettext.install('base', LOCALE_DIR, names=['ngettext'])
if __name__ == "__main__":
# Simulate setting the user's language environment.
# On a real system, you might read this from a user setting or system locale.
# Try running the app in Spanish
setup_language('es_ES')
print(_("Welcome to the multilingual demo!"))
# Now, let's switch to English for the rest of the app's flow
setup_language('en_US')
name = input(_("Please enter your name: "))
print(_(f"Hello, {name}! How are you today?"))Запустите скрипт — вы увидите первую строку на испанском, остальное на английском, что демонстрирует динамическую смену переводов.
Расширенные возможности и лучшие практики
Контекст и множественное число
- Для контекста используйте pgettext или msgctxt в .po. Контекст важен, когда одно слово имеет разные значения.
- Для множественного числа используйте ngettext и заполните форму в .po для чисел.
Пример ngettext в коде:
from gettext import ngettext
count = 3
print(ngettext("{count} file deleted", "{count} files deleted", count).format(count=count)).po файл должен содержать соответствующие формы plural‑rules.
Безопасность форматирования
Всегда валидируйте и экранируйте пользовательские данные. Перед вставкой в переводимые строки используйте именованные плейсхолдеры ({name}) вместо позиционных, это уменьшает ошибки при переводе.
Идём дальше: CI, тесты и рабочий процесс переводов
- Автоматизируйте извлечение строк в CI (xgettext) при изменении кода.
- Проверяйте, что .po файлы не содержат незаполненных msgstr.
- Включите тесты на корректность placeholder’ов и на наличие перевода для основных языков.
Когда gettext не подходит или требует доработок
Важно понимать ограничения:
- gettext хорош для классических приложений, но не покрывает динамическую генерацию UI‑текстов без дополнительной архитектуры.
- Работа с богатым содержимым (HTML, Markdown внутри переводов) требует дополнительного контроля безопасности.
- Для большого числа переводчиков и сложного рабочего процесса может быть удобнее использовать TMS (Translation Management System) и интегрировать экспорт/импорт .po.
Альтернативные подходы:
- JSON/YAML‑файлы с ключами (популярно в веб‑приложениях).
- ICU MessageFormat для сложных правил склонения и множественного числа.
- Сторонние библиотеки (babel, polyglot, fluent). Выбор зависит от требований к форматированию и взаимодействию с переводчиками.
Контроль качества переводов — чеклист
Роль: разработчик
- Пометил все пользовательские строки _().
- Использует именованные placeholder’ы.
- Настроил CI для обновления .pot.
Роль: локализатор (переводчик)
- Проверил все msgstr.
- Сохранил плейсхолдеры и их синтаксис.
- Проверил пунктуацию и стиль.
Роль: тестировщик
- Запуск тестов на наличие всех переводов.
- Проверка UI на усечение или переполнение текста.
- Проверка множественного числа в разных языках.
Критерии приёмки
- Все пользовательские строки помечены и присутствуют в .pot.
- Для целевых языков есть .mo в релизном артефакте.
- Переводы не ломают форматирование и плейсхолдеры.
- Тесты на placeholder/плюральность проходят в CI.
Мини‑методология внедрения локализации в проект
- Инвенторизация: найдите все тексты, видимые пользователю.
- Пометка: замените строки на _().
- Экстракция: автоматизированно создайте .pot.
- Перевод: интегрируйте .po в рабочий процесс переводчиков.
- Сборка: собирайте .mo и включайте их в сборки.
- Тестирование: автоматические и ручные проверки.
- Поддержка: процесс обновления переводов при изменениях UI.
Примеры проблем и как их решать
Проблема: плейсхолдер поменял порядок слов в другом языке. Решение: используйте именованные плейсхолдеры и объясните переводчику контекст.
Проблема: строка слишком длинная и ломает интерфейс. Решение: договоритесь о лимитах длины, добавьте проверку в QA, в UI оставьте место для расширения.
Проблема: вариант слова зависит от рода. Решение: разделяйте строку на более мелкие элементы с контекстом или используйте дополнительные msgctxt.
Тестовые случаи и приёмка
- Запуск приложения с различными языковыми параметрами (es_ES, en_US, fr_FR).
- Изменение системного локаля и проверка fallback.
- Наличие .mo в каталоге локалей после сборки.
- Проверка работы ngettext для нескольких чисел (0,1,2,5,21).
Глоссарий (1‑строчный)
- Локализация: адаптация продукта к языку и региональным особенностям.
- Интернационализация: проектирование продукта, чтобы его можно было легко локализовать.
- .po/.mo/.pot: форматы gettext для переводов.
Резюме
Используя gettext, вы быстро отделите тексты от кода и упростите перевод приложения. Начните с пометки строк, настройте структуру локалей, автоматизируйте экспорт/импорт и добавьте проверки в CI. Для сложных случаев рассмотрите ICU или TMS.
Важно
- Сохранение плейсхолдеров и контекста критично.
- Автоматизация уменьшает количество ошибок при обновлении строк.
Дополнительно: краткая дорожная карта внедрения
- Пометить все строки в кодовой базе.
- Настроить xgettext в CI.
- Подготовить шаблон .pot и подключить переводчиков.
- Добавить проверку на корректность placeholder’ов в unit‑тесты.
- Включить .mo файлы в релиз и мониторить отзывы пользователей по локалям.
–
Авторская заметка: этот гид описывает практический, безопасный и расширяемый подход к локализации небольших и средних Python‑проектов с использованием стандартных инструментов. Он пригоден как для CLI, так и как основа для серверных приложений, где простота и совместимость важнее сложной логики форматирования.
Похожие материалы
RDP: полный гид по настройке и безопасности
Android как клавиатура и трекпад для Windows
Советы и приёмы для работы с PDF
Calibration в Lightroom Classic: как и когда использовать
Отключить Siri Suggestions на iPhone