How to add translations for your telegram bot

Clock
14.01.2025
An eye
50
Hearts
0
Connected dots
0
Connected dots
0
Connected dots
0

Introduction

Our bot example will consist of 2 *.py files, main.py and config.py. The first will contain the entry point and the corresponding handlers, and the second will contain the settings for translations. The code for the example was taken from the Bot-example for connecting the Inline mode.

Necessary code and setup

As I promised, we do translations. Firstly, create a locales directory in the project folder. Then add the following imports to the main.py file:
import asyncio
import logging
import sys
import uuid
from aiogram import F
from aiogram.types import Message, InlineQuery, InlineQueryResultArticle, InputTextMessageContent, InlineQueryResultPhoto
from aiogram.utils.i18n import gettext as _
from aiogram.filters import Command
from config import bot_dispatcher, bot




@bot_dispatcher.inline_query(F.query == "greeting")
async def send_greetings(inline_query: InlineQuery):
results = []
results.append(InlineQueryResultArticle(
id=str(uuid.uuid4()),
title="Обычное приветствие",
input_message_content=InputTextMessageContent(
disable_web_page_preview=True,
message_text="Приветствую я вас сегодня."
)
))
results.append(InlineQueryResultArticle(
id=str(uuid.uuid4()),
title="Жёсткое приветствие",
input_message_content=InputTextMessageContent(
disable_web_page_preview=True,
message_text="Ну чё, как дела"
)
))
results.append(InlineQueryResultArticle(
id=str(uuid.uuid4()),
title="Спокойное приветствие",
input_message_content=InputTextMessageContent(
disable_web_page_preview=True,
message_text="Привет"
)
))
await inline_query.answer(results)


@bot_dispatcher.inline_query(F.query == "memes")
async def send_user_images(inline_query: InlineQuery):
results = []
results.append(InlineQueryResultPhoto(
id=str(uuid.uuid4()),
photo_url="https://wisconsinskydivingcenter.com/wp-content/uploads/2024/05/you-dont-need-a-parachute-to-go-skydiving-meme.jpeg",
thumbnail_url="https://wisconsinskydivingcenter.com/wp-content/uploads/2024/05/you-dont-need-a-parachute-to-go-skydiving-meme.jpeg"
))
results.append(InlineQueryResultPhoto(
id=str(uuid.uuid4()),
photo_url="https://jungleroots.com/wp-content/uploads/2022/11/1_OkVxoXBTygSKB8K-zbB7uQ-300x176.jpeg",
thumbnail_url="https://jungleroots.com/wp-content/uploads/2022/11/1_OkVxoXBTygSKB8K-zbB7uQ-300x176.jpeg"
))
results.append(InlineQueryResultPhoto(
id=str(uuid.uuid4()),
photo_url="https://livestorm.imgix.net/1127/1651607695-obi-wan-alarm-work-meme.jpeg?h=auto&w=730&fm=jpeg&auto=format&q=90&dpr=1",
thumbnail_url="https://livestorm.imgix.net/1127/1651607695-obi-wan-alarm-work-meme.jpeg?h=auto&w=730&fm=jpeg&auto=format&q=90&dpr=1"
))
await inline_query.answer(results)


@bot_dispatcher.message(Command('help'))
async def help(message: Message):
await message.answer("Этот бот является примером того, как можно реализовать inline функционал для него.")
await message.answer("/startapp - запустить приложение")
await message.answer("@joker_gut_bot memes - запросить мемы для показа")
await message.answer("@joker_gut_bot greeting - запросить ответы по умолчанию")








@bot_dispatcher.message(Command("startapp"))
async def start_app(message: Message):
await message.answer("Этот бот является примером того, как можно реализовать inline функционал для него.")








async def main() -> None:
await bot_dispatcher.start_polling(bot)




if __name__ == "__main__":
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
asyncio.run(main())



In the config.py file, we will set up a directory for storing translations and middleware, respectively.
from aiogram import Bot, Dispatcher
from aiogram.utils.i18n import I18n, SimpleI18nMiddleware

with open(".env", "r") as file:
buffer = file.read()
line_pos = buffer.find("BOT_TOKEN")
TOKEN = buffer[buffer.find("=", line_pos) + 1:]

bot = Bot(TOKEN)
bot_dispatcher = Dispatcher(bot=bot)

# Set up translation
i18n = I18n(path="locales", domain="messages")
i18n_handler = SimpleI18nMiddleware(i18n)
i18n_handler.setup(bot_dispatcher)

Marking strings for translation

After we have connected the relevant modules and set up the environment, we need to mark which strings we are going to translate. In main.py I created an alias.
...
from aiogram.utils.i18n import gettext as _
...
Each line will need to be wrapped around the alias of gettext. The end result will look something like this:
import asyncio
import logging
import sys
import uuid
from aiogram import F
from aiogram.types import Message, InlineQuery, InlineQueryResultArticle, InputTextMessageContent, InlineQueryResultPhoto
from aiogram.filters import Command
from aiogram.utils.i18n import gettext as _
from config import bot_dispatcher, bot


@bot_dispatcher.inline_query(F.query == "greeting")
async def send_greetings(inline_query: InlineQuery):
results = []
results.append(InlineQueryResultArticle(
id=str(uuid.uuid4()),
title=_("Обычное приветствие"),
input_message_content=InputTextMessageContent(
disable_web_page_preview=True,
message_text=_("Приветствую я вас сегодня.")
)
))
results.append(InlineQueryResultArticle(
id=str(uuid.uuid4()),
title=_("Жёсткое приветствие"),
input_message_content=InputTextMessageContent(
disable_web_page_preview=True,
message_text=_("Ну чё, как дела")
)
))
results.append(InlineQueryResultArticle(
id=str(uuid.uuid4()),
title=_("Спокойное приветствие"),
input_message_content=InputTextMessageContent(
disable_web_page_preview=True,
message_text=_("Привет")
)
))
await inline_query.answer(results)

@bot_dispatcher.inline_query(F.query == "memes")
async def send_user_images(inline_query: InlineQuery):
results = []
results.append(InlineQueryResultPhoto(
id=str(uuid.uuid4()),
photo_url="https://wisconsinskydivingcenter.com/wp-content/uploads/2024/05/you-dont-need-a-parachute-to-go-skydiving-meme.jpeg",
thumbnail_url="https://wisconsinskydivingcenter.com/wp-content/uploads/2024/05/you-dont-need-a-parachute-to-go-skydiving-meme.jpeg"
))
results.append(InlineQueryResultPhoto(
id=str(uuid.uuid4()),
photo_url="https://jungleroots.com/wp-content/uploads/2022/11/1_OkVxoXBTygSKB8K-zbB7uQ-300x176.jpeg",
thumbnail_url="https://jungleroots.com/wp-content/uploads/2022/11/1_OkVxoXBTygSKB8K-zbB7uQ-300x176.jpeg"
))
results.append(InlineQueryResultPhoto(
id=str(uuid.uuid4()),
photo_url="https://livestorm.imgix.net/1127/1651607695-obi-wan-alarm-work-meme.jpeg?h=auto&w=730&fm=jpeg&auto=format&q=90&dpr=1",
thumbnail_url="https://livestorm.imgix.net/1127/1651607695-obi-wan-alarm-work-meme.jpeg?h=auto&w=730&fm=jpeg&auto=format&q=90&dpr=1"
))
await inline_query.answer(results)

@bot_dispatcher.message(Command('help'))
async def help(message: Message):
await message.answer(_("Этот бот является примером того, как можно реализовать inline функционал для него."))
await message.answer(_("/startapp - запустить приложение"))
await message.answer(_("@joker_gut_bot memes - запросить мемы для показа"))
await message.answer(_("@joker_gut_bot greeting - запросить ответы по умолчанию"))


@bot_dispatcher.message(Command("startapp"))
async def start_app(message: Message):
await message.answer(_("Этот бот является примером того, как можно реализовать inline функционал для него."))


async def main() -> None:
await bot_dispatcher.start_polling(bot)

if __name__ == "__main__":
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
asyncio.run(main())

Extracting strings

Finally, you can start extracting strings to the locales folder. All strings and their data will be saved in the messages.pot file.
pybabel extract --input-dirs=. -o locales/messages.pot

Initializing locales and translated languages

You will need to make exactly as many locales as languages ​​​​you are going to use. In my case, there are 2, and they are made like this:
pybabel init -i locales/messages.pot -d locales -D messages -l en;
pybabel init -i locales/messages.pot -d locales -D messages -l ru;

Translate

The first is English, the second is Russian. In the first case, after generation, you will need to write a translation. You will need to find the messages.po file in locales\en\LC_MESSAGES and write translations. In the second case, this is the original language. No need to do translations.

Compiling translations

Now let's compile the translations
pybabel compile -d locales -D messages

Updating translations

The translations are ready. But what if you wanted to make changes to the translations or added another text? In this case, you need to update the translations. And to do this, you will have to perform slightly different steps than when initializing in the first steps.
It is important to remember that we initialize only once. Then, unless you want to lose all the translations, you will need to update them in a special way. First, extract the changes from all files with translation strings.
pybabel extract --input-dirs=. -o locales/messages.pot
Afterward, we update all locales
pybabel update -d locales -D messages -i locales/messages.pot
And finally compile the translations.
pybabel compile -d locales -D messages
That's it, the translations have been updated and will be shown depending on the user's Telegram client settings.

Conclusion

Translations for bots in Telegram are not so easy and can cause quite a headache for those who do it for the first time. The most difficult step, in my opinion, is finding and wrapping the lines you need for translation. Because not all lines can be wrapped, but if you follow the basic rules, everything should work out. ( ̄︶ ̄)↗ 


Comments

(0)
captcha
Send
It's empty now. Be the first (o゚v゚)ノ