Глава 2. Интернационализация в Aiogram утилитами GNU gettext.
Подготовка кода.
Во фреймворке Aiogram предусмотрена возможность использования интернационализации (https://docs.aiogram.dev/en/latest/utils/i18n.html) с помощью библиотеки GNU gettext (https://docs.python.org/3/library/gettext.html) и инструментов Babel (https://babel.pocoo.org/en/latest/).
Установка пакета для переводов происходит через дополнительную зависимость командой
pip install aiogram[i18n]
Либо можно непосредственно установить сам Babel
pip install Babel
Первый и важный шаг для работы по интернационализации — нам нужно
подготовить наш код таким образом, чтобы он смог использовать файлы
перевода и загружать необходимые фразы из нужной локали. Для этого все
переводимые строки необходимо обернуть функцией gettext. Функцию
подстановки перевода из gettext принято обозначать _
- одинарное
нижнее подчеркивание, а вызов этой функции _()
.
from aiogram import html
from aiogram.utils.i18n import gettext as _
# импортируем модуль gettext из aiogram utils как _
Обертываем все строки, которые нуждаются в переводе функцией gettext.
Было:
async def my_handler(message: Message) -> None:
await message.answer(f"Hello, {html.quote(message.from_user.full_name)}!")
Стало:
async def my_handler(message: Message) -> None:
await message.answer(_("Hello, {name}!").format(name=html.quote(message.from_user.full_name)))
Внимание
Обратите внимание, что Gettext не может использовать f-строки. Поскольку при
использовании f-строк нельзя сначала создать шаблон, а затем его
использовать. Это происходит из-за того, что f-строка сразу выполняется
и в нее подставляются значения переменных, которые должны быть
определены ранее. А у нас сначала должен произойти перевод с
подстановкой в шаблон строки. Поэтому нужно использовать метод строк
format()
.
Более того, когда нам необходимо использовать перевод в фильтрах
ключевых слов или магических фильтрах, то нужно будет использовать
ленивые вызовы gettext - lazy_gettext
, которые будут обозначены
__
- двойное подчеркивание, а вызов этой функции __()
.
from aiogram import F
from aiogram.utils.i18n import lazy_gettext as __
# Выше мы импортируем функцию ленивого вызова gettext как _ _
В документации особо обращено внимание на то, что ленивые вызовы
lazy gettext
всегда следует использовать, если текущий язык в данный
момент неизвестен.
Также важно, что lazy gettext
нельзя использовать в качестве
значения для методов API или любого объекта Telegram (например, для
aiogram.types.inline_keyboard_button.InlineKeyboardButton и т. д.).
Конфигурация движка перевода
Сначала в коде проекта мы создаем объект класса I18n
, чтобы было
понятно, какой язык будет использоваться:
i18n = I18n(path="locales", default_locale="en", domain="my-super-bot")
Здесь:
path=
путь к папкам с локалями. В данном случае путь будет
сформирован будет так:
locales/{language}/LC_MESSAGES/файл_перевода.mo
, а мы указываем
верхний уровень locales, исходя из нашей структуры:
...
locales
├── messages.pot
├── en
│ └── LC_MESSAGES
│ └── my-super-bot.mo
├── ru
│ └── LC_MESSAGES
│ └── my-super-bot.mo
...
default_locale=
локаль по умолчанию, английская «en».
domain=
домен - это название домена переводов в gettext, по сути это
название приложения, для которого будет создана локаль (используется
чаще название того приложения, что мы переводим). Мы назвали
«my-super-bot».
Движок перевода - это middleware для I18n. И теперь нам необходимо
выбрать движок перевода, основанный на 4-х встроенных в aiogram классах
middleware из aiogram.utils.i18n.middleware
:
SimpleI18nMiddleware - выбирает код языка из объекта User, полученного в событии. Однако не все клиенты Telegram отдают это значение. Очень часто объект language_code не заполнен и является пустой строкой.
ConstI18nMiddleware - выбирает статически определенную локаль.
FSMI18nMiddleware - хранит локаль в хранилище FSM.
I18nMiddleware - это базовый абстрактный класс для наследования и создания собственного обработчика.
Наш код будет выглядеть примерно так:
1from aiogram import Bot, Dispatcher, F
2from aiogram.types import Message
3
4from aiogram.utils.i18n import gettext as _
5from aiogram.utils.i18n import lazy_gettext as __
6from aiogram.utils.i18n import I18n, ConstI18nMiddleware
7
8TOKEN = "token"
9dp = Dispatcher()
10
11@dp.message(F.text == __('Test'))
12async def test1(message: Message) -> None:
13 await message.answer(_("Hello, {name}!").format(name=html.quote(message.from_user.full_name)))
14
15def main() -> None:
16 bot = Bot(TOKEN, parse_mode="HTML")
17 i18n = I18n(path="locales", default_locale="en", domain="my-super-bot")
18 dp.message.outer_middleware(ConstI18nMiddleware(locale='en', i18n=i18n))
19
20 dp.run_polling(bot)
21
22if __name__ == "__main__":
23 main()