From e97e6c1d7d47a8007cc54bb78ee6ffaae8f69891 Mon Sep 17 00:00:00 2001 From: Dmitry Buslov Date: Sun, 16 Mar 2025 23:57:19 +0200 Subject: [PATCH 1/3] Feat: added i18 example (with dynamic translation) --- examples/i18n.py | 82 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 examples/i18n.py diff --git a/examples/i18n.py b/examples/i18n.py new file mode 100644 index 00000000..6acafd51 --- /dev/null +++ b/examples/i18n.py @@ -0,0 +1,82 @@ +""" +Original docs link: https://docs.aiogram.dev/en/dev-3.x/utils/i18n.html +PROBLEM SOLUTION PROVIDED: +Aiogram handlers remember language on the start and there's no chance of changing it in handler's runtime. +So, here's a little hack in `change_language handler` + +# Cheatsheet to create/update locales: +## Generating new: +1. `pybabel extract -k __ --input-dirs=. -o locales/messages.pot` +2. `pybabel init -i locales/messages.pot -d locales -D messages -l ` +3. Open `locales/{language}/LC_MESSAGES/messages.po` and Translate msgs +4. `pybabel compile -d locales -D messages` + +## Updating: +1. `pybabel extract -k __ --input-dirs=. -o locales/messages.pot` +2. `pybabel update -d locales -D messages -i locales/messages.pot` +3. Open `locales/{language}/LC_MESSAGES/messages.po` and Translate msgs +4. `pybabel compile -d locales -D messages` + +""" +import asyncio +from typing import Any + +from aiogram import Bot, Dispatcher, Router +from aiogram import types +from aiogram.filters.command import Command, CommandStart +from aiogram.utils.i18n import gettext as _, I18n +from aiogram.utils.i18n.middleware import I18nMiddleware + + +class BotI18nMiddleware(I18nMiddleware): + async def get_locale(self, event: types.Update | Any, data: dict[str, Any]) -> str: + # here is your logic, ex. get language from redis DB + return 'en' + + +bot = Bot(token="") +router = Router() +dp = Dispatcher() +# NOTE: custom middleware is provided. You can use any middleware you want, ex: SimpleI18nMiddleware +i18n_middleware = BotI18nMiddleware( + I18n( + path="locales", + default_locale="en", + domain="messages" + ) +) +dp.update.outer_middleware(i18n_middleware) + + +@router.message(CommandStart()) +async def start_handler(message: types.Message): + await message.answer( + _("Hello!. Write /language . Ex: /language ru") + ) + + +@router.message(Command("language")) +async def change_language(message: types.Message): + """ + This function dynamically changes language inside of handler + Handler input: /language . Ex: /language ru + + Aiogram saves language context on start, so we should change language in runtime + """ + new_lang = message.text.split(maxsplit=1)[1] + # change in runtime + with i18n_middleware.i18n.context(), i18n_middleware.i18n.use_locale(new_lang): + # all messages in this context will be translated to new_lang + await message.answer(_('Language was changed to {lang}').format(lang=new_lang)) + + +dp.include_router(router) + + +async def main(): + print("Bot started") + await dp.start_polling(bot) + + +if __name__ == "__main__": + asyncio.run(main()) From c3fbe87ab417ed06dae4927a7cdc8972d94ddb15 Mon Sep 17 00:00:00 2001 From: Dmitry Buslov Date: Mon, 17 Mar 2025 00:02:30 +0200 Subject: [PATCH 2/3] Fix: rm custom i18n middleware so it doesn't confuse user --- examples/i18n.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/i18n.py b/examples/i18n.py index 6acafd51..89708d59 100644 --- a/examples/i18n.py +++ b/examples/i18n.py @@ -25,20 +25,20 @@ from aiogram import Bot, Dispatcher, Router from aiogram import types from aiogram.filters.command import Command, CommandStart from aiogram.utils.i18n import gettext as _, I18n -from aiogram.utils.i18n.middleware import I18nMiddleware +from aiogram.utils.i18n.middleware import I18nMiddleware, SimpleI18nMiddleware -class BotI18nMiddleware(I18nMiddleware): - async def get_locale(self, event: types.Update | Any, data: dict[str, Any]) -> str: - # here is your logic, ex. get language from redis DB - return 'en' +# class BotI18nMiddleware(I18nMiddleware): +# async def get_locale(self, event: types.Update | Any, data: dict[str, Any]) -> str: +# # here is your logic, ex. get language from redis DB. +# return 'en' bot = Bot(token="") router = Router() dp = Dispatcher() -# NOTE: custom middleware is provided. You can use any middleware you want, ex: SimpleI18nMiddleware -i18n_middleware = BotI18nMiddleware( +# NOTE: basic middleware is provided. You can customize your middleware, ex. BotI18nMiddleware +i18n_middleware = SimpleI18nMiddleware( I18n( path="locales", default_locale="en", From e3764961fb365d98778baad2db387dd972f5586e Mon Sep 17 00:00:00 2001 From: Dmitry Buslov Date: Mon, 17 Mar 2025 00:06:48 +0200 Subject: [PATCH 3/3] Feat: changelog --- CHANGES/1652.feature.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 CHANGES/1652.feature.rst diff --git a/CHANGES/1652.feature.rst b/CHANGES/1652.feature.rst new file mode 100644 index 00000000..81b15c9f --- /dev/null +++ b/CHANGES/1652.feature.rst @@ -0,0 +1 @@ +Feature: Example of dynamic language switching in aiogram bot using i18n utilities, allowing real-time translation.