Глава 5. Интернационализация в Aiogram с использованием Fluent.
Подготовка кода
Для хорошей поддержки интернационализации и локализации разработчики
Aiogram создали отдельный пакет aiogram_i18n
https://github.com/aiogram/i18n, в который добавили
поддержку движка локализации Fluent для Python
https://github.com/projectfluent/python-fluent.
Установим его с помощью команды:
pip install aiogram_i18n
Также нам потребуются FluentCompileCore
и FluentRuntimeCore
pip install fluent_compiler
pip install fluent.runtime
В предыдущем разделе мы рассказывали о том, что подход к созданию файлов перевода у Fluent в корне отличается от привычного. По сути теперь интернационализация полностью в ваших руках, а локализацию выполняет ядро Python Fluent. У нас теперь нет шаблонов перевода и мы не извлекаем строки из исходного кода нашего проекта. Хотя есть различные плагины для IDE, которые могут нам помочь с извлечением строк. Отказ от использования строк английского текста как ключей, накладывает некоторые ограничения в угоду гибкости самих переводов. Теперь нам нужно самим проектировать ПО так, чтобы перевод был возможен. И по-началу будет много ручной работы. Проект Fluent - 2017 год, и по меркам gettext 1995 года, он еще молодой. Инструменты автоматизации, которые я нашел, созданы в основном для JavaScript (в силу специфики разработки под локализацию браузера Firefox). Поэтому пока будем создавать файлы перевода вручную.
Создадим код нашего нового проекта:
1import asyncio
2from logging import basicConfig, INFO
3from typing import Any
4
5from aiogram import Router, Dispatcher, F, Bot
6from aiogram.enums import ParseMode
7from aiogram.filters import CommandStart
8from aiogram.types import Message
9
10from aiogram_i18n import I18nContext, LazyProxy, I18nMiddleware
11from aiogram_i18n.cores.fluent_runtime_core import FluentRuntimeCore
12from aiogram_i18n.types import (
13 ReplyKeyboardMarkup, KeyboardButton
14 # you should import mutable objects from here if you want to use LazyProxy in them
15)
16
17router = Router(name=__name__)
18rkb = ReplyKeyboardMarkup(
19 keyboard=[
20 [KeyboardButton(text=LazyProxy("help"))] # or L.help()
21 ], resize_keyboard=True)
22
23@router.message(CommandStart())
24async def cmd_start(message: Message, i18n: I18nContext) -> Any:
25 name = message.from_user.mention_html()
26 return message.reply(
27 text=i18n.get("hello", user=name), # or i18n.hello(user=name)
28 reply_markup=rkb)
29
30
31@router.message(F.text == LazyProxy("help"))
32async def cmd_help(message: Message) -> Any:
33 return message.reply(text="-- " + message.text + " --")
34
35async def main() -> None:
36 basicConfig(level=INFO)
37 bot = Bot("TOKEN", parse_mode=ParseMode.HTML)
38 i18n_middleware = I18nMiddleware(
39 core=FluentRuntimeCore(
40 path="locales/{locale}/LC_MESSAGES",
41 ),
42 default_locale="ru")
43 dp = Dispatcher()
44 dp.include_router(router)
45 i18n_middleware.setup(dispatcher=dp)
46 await dp.start_polling(bot)
47
48
49if __name__ == "__main__":
50 asyncio.run(main())
Мы импортировали следующие объекты:
10from aiogram_i18n import I18nContext, LazyProxy, I18nMiddleware
11from aiogram_i18n.cores.fluent_runtime_core import FluentRuntimeCore
12from aiogram_i18n.types import (
13 ReplyKeyboardMarkup, KeyboardButton
14 # you should import mutable objects from here if you want to use LazyProxy in them
15)
Нам понадобится сам движок FluentRuntimeCore
, также контекст
I18nContext
и один из вариантов middleware (для примера я взял
I18nMiddleware
). Также нужны нам LazyProxy
- ленивые строки,
какие мы видели в lazy gettext
. И изменяемые объекты Aiogram, такие,
как клавиатура, которые нужно импортировать именно из
aiogram_i18n.types
. Эти объекты нам нужны, когда работа с объектом
происходит, но язык еще не известен, так как код выполняется еще за
пределами роутеров. И перевод будет добавлен лениво, то есть в самом
конце перед передачей пользователю.
Создадим объект нашего middleware:
38i18n_middleware = I18nMiddleware(
39 core=FluentRuntimeCore(
40 path="locales/{locale}/LC_MESSAGES", # путь к папке локалей
41 ),
42 default_locale="ru") # язык интерфейса. Переключать научимся позже.
И зарегистрируем его через встроенный метод setup (в этом методе реализована регистрация компонентов в нужном порядке)
45i18n_middleware.setup(dispatcher=dp)
Создадим файл переводов в формате FTL
(Fluent Translation List).
Файл следующего содержания с английским переводом my-super-bot.ftl
положим в папку locales/en/LC_MESSAGES
:
hello = Hello, <b>{ $user }</b>!
cur-lang = Your current language: <i>{ $language }</i>
help = Help
Файл с русским переводом my-super-bot.ftl
положим в папку locales/ru/LC_MESSAGES
:
hello = Привет, <b>{ $user }</b>!
cur-lang = Текущий язык : <i>{ $language }</i>
help = Помощь
Запустим и проверим работу на русском языке. Затем изменим язык в middleware и проверим на английском:
38i18n_middleware = I18nMiddleware(
39 core=FluentRuntimeCore(
40 path="locales/{locale}/LC_MESSAGES", # путь к папке локалей
41 ),
42 default_locale="en")