diff --git a/examples/i18n_class_example/i18n_class.py b/examples/i18n_class_example/i18n_class.py deleted file mode 100644 index aa02612..0000000 --- a/examples/i18n_class_example/i18n_class.py +++ /dev/null @@ -1,66 +0,0 @@ -import gettext -import os - - -class I18N: - """ - This class provides high-level tool for internationalization - It is based on gettext util. - """ - - def __init__(self, translations_path, domain_name: str): - self.path = translations_path - self.domain = domain_name - self.translations = self.find_translations() - - @property - def available_translations(self): - return list(self.translations) - - def gettext(self, text: str, lang: str = None): - """ - Singular translations - """ - if not lang or lang not in self.translations: - return text - - translator = self.translations[lang] - return translator.gettext(text) - - def ngettext(self, singular: str, plural: str, lang: str = None, n=1): - """ - Plural translations - """ - if not lang or lang not in self.translations: - if n == 1: - return singular - return plural - - translator = self.translations[lang] - return translator.ngettext(singular, plural, n) - - def find_translations(self): - """ - Looks for translations with passed 'domain' in passed 'path' - """ - if not os.path.exists(self.path): - raise RuntimeError(f"Translations directory by path: {self.path!r} was not found") - - result = {} - - for name in os.listdir(self.path): - translations_path = os.path.join(self.path, name, 'LC_MESSAGES') - - if not os.path.isdir(translations_path): - continue - - po_file = os.path.join(translations_path, self.domain + '.po') - mo_file = po_file[:-2] + 'mo' - - if os.path.isfile(po_file) and not os.path.isfile(mo_file): - raise FileNotFoundError(f"Translations for: {name!r} were not compiled!") - - with open(mo_file, 'rb') as file: - result[name] = gettext.GNUTranslations(file) - - return result diff --git a/examples/i18n_class_example/keyboards.py b/examples/i18n_class_example/keyboards.py deleted file mode 100644 index 4a94268..0000000 --- a/examples/i18n_class_example/keyboards.py +++ /dev/null @@ -1,23 +0,0 @@ -from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton - - -def languages_keyboard(): - return InlineKeyboardMarkup( - keyboard=[ - [ - InlineKeyboardButton(text="English", callback_data='en'), - InlineKeyboardButton(text="Русский", callback_data='ru'), - InlineKeyboardButton(text="O'zbekcha", callback_data='uz_Latn') - ] - ] - ) - - -def clicker_keyboard(_, lang): - return InlineKeyboardMarkup( - keyboard=[ - [ - InlineKeyboardButton(text=_("click", lang=lang), callback_data='click'), - ] - ] - ) diff --git a/examples/i18n_class_example/locales/en/LC_MESSAGES/messages.po b/examples/i18n_class_example/locales/en/LC_MESSAGES/messages.po deleted file mode 100644 index fc6565a..0000000 --- a/examples/i18n_class_example/locales/en/LC_MESSAGES/messages.po +++ /dev/null @@ -1,51 +0,0 @@ -# English translations for PROJECT. -# Copyright (C) 2022 ORGANIZATION -# This file is distributed under the same license as the PROJECT project. -# FIRST AUTHOR , 2022. -# -msgid "" -msgstr "" -"Project-Id-Version: PROJECT VERSION\n" -"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2022-02-18 17:54+0500\n" -"PO-Revision-Date: 2022-02-18 16:22+0500\n" -"Last-Translator: FULL NAME \n" -"Language: en\n" -"Language-Team: en \n" -"Plural-Forms: nplurals=2; plural=(n != 1)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.9.1\n" - -#: keyboards.py:20 -msgid "click" -msgstr "" - -#: main.py:78 -msgid "" -"Hello, {user_fist_name}!\n" -"This is the example of multilanguage bot.\n" -"Available commands:\n" -"\n" -"/lang - change your language\n" -"/plural - pluralization example" -msgstr "" - -#: main.py:102 -msgid "Language has been changed" -msgstr "" - -#: main.py:114 -#, fuzzy -msgid "You have {number} click" -msgid_plural "You have {number} clicks" -msgstr[0] "" -msgstr[1] "" - -#: main.py:120 -msgid "" -"This is clicker.\n" -"\n" -msgstr "" - diff --git a/examples/i18n_class_example/locales/ru/LC_MESSAGES/messages.po b/examples/i18n_class_example/locales/ru/LC_MESSAGES/messages.po deleted file mode 100644 index 1733ef1..0000000 --- a/examples/i18n_class_example/locales/ru/LC_MESSAGES/messages.po +++ /dev/null @@ -1,60 +0,0 @@ -# Russian translations for PROJECT. -# Copyright (C) 2022 ORGANIZATION -# This file is distributed under the same license as the PROJECT project. -# FIRST AUTHOR , 2022. -# -msgid "" -msgstr "" -"Project-Id-Version: PROJECT VERSION\n" -"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2022-02-18 17:54+0500\n" -"PO-Revision-Date: 2022-02-18 16:22+0500\n" -"Last-Translator: FULL NAME \n" -"Language: ru\n" -"Language-Team: ru \n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " -"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.9.1\n" - -#: keyboards.py:20 -msgid "click" -msgstr "Клик" - -#: main.py:78 -msgid "" -"Hello, {user_fist_name}!\n" -"This is the example of multilanguage bot.\n" -"Available commands:\n" -"\n" -"/lang - change your language\n" -"/plural - pluralization example" -msgstr "" -"Привет, {user_fist_name}!\n" -"Это пример мультиязычного бота.\n" -"Доступные команды:\n" -"\n" -"/lang - изменить язык\n" -"/plural - пример плюрализации" - -#: main.py:102 -msgid "Language has been changed" -msgstr "Язык был сменён" - -#: main.py:114 -msgid "You have {number} click" -msgid_plural "You have {number} clicks" -msgstr[0] "У вас {number} клик" -msgstr[1] "У вас {number} клика" -msgstr[2] "У вас {number} кликов" - -#: main.py:120 -msgid "" -"This is clicker.\n" -"\n" -msgstr "" -"Это кликер.\n" -"\n" - diff --git a/examples/i18n_class_example/locales/uz_Latn/LC_MESSAGES/messages.po b/examples/i18n_class_example/locales/uz_Latn/LC_MESSAGES/messages.po deleted file mode 100644 index e694b78..0000000 --- a/examples/i18n_class_example/locales/uz_Latn/LC_MESSAGES/messages.po +++ /dev/null @@ -1,58 +0,0 @@ -# Uzbek (Latin) translations for PROJECT. -# Copyright (C) 2022 ORGANIZATION -# This file is distributed under the same license as the PROJECT project. -# FIRST AUTHOR , 2022. -# -msgid "" -msgstr "" -"Project-Id-Version: PROJECT VERSION\n" -"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2022-02-18 17:54+0500\n" -"PO-Revision-Date: 2022-02-18 16:22+0500\n" -"Last-Translator: FULL NAME \n" -"Language: uz_Latn\n" -"Language-Team: uz_Latn \n" -"Plural-Forms: nplurals=2; plural=(n != 1)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.9.1\n" - -#: keyboards.py:20 -msgid "click" -msgstr "clik" - -#: main.py:78 -msgid "" -"Hello, {user_fist_name}!\n" -"This is the example of multilanguage bot.\n" -"Available commands:\n" -"\n" -"/lang - change your language\n" -"/plural - pluralization example" -msgstr "" -"Salom, {user_fist_name}!\n" -"Bu multilanguage bot misoli.\n" -"Mavjud buyruqlar:\n" -"\n" -"/lang - tilni ozgartirish\n" -"/plural - pluralizatsiya misoli" - -#: main.py:102 -msgid "Language has been changed" -msgstr "Til ozgartirildi" - -#: main.py:114 -msgid "You have {number} click" -msgid_plural "You have {number} clicks" -msgstr[0] "Sizda {number}ta clik" -msgstr[1] "Sizda {number}ta clik" - -#: main.py:120 -msgid "" -"This is clicker.\n" -"\n" -msgstr "" -"Bu clicker.\n" -"\n" - diff --git a/examples/i18n_class_example/main.py b/examples/i18n_class_example/main.py deleted file mode 100644 index 23794f2..0000000 --- a/examples/i18n_class_example/main.py +++ /dev/null @@ -1,153 +0,0 @@ -""" -In this example you will learn how to adapt your bot to different languages -Using built-in class I18N. - -You need to install babel package 'https://pypi.org/project/Babel/' -Babel provides a command-line interface for working with message catalogs -After installing babel package you have a script called 'pybabel' -Too see all the commands open terminal and type 'pybabel --help' -Full description for pybabel commands can be found here: 'https://babel.pocoo.org/en/latest/cmdline.html' - -Create a directory 'locales' where our translations will be stored - -First we need to extract texts: - pybabel extract -o locales/{domain_name}.pot --input-dirs . -{domain_name}.pot - is the file where all translations are saved -The name of this file should be the same as domain which you pass to I18N class -In this example domain_name will be 'messages' - -For gettext (singular texts) we use '_' alias and it works perfect -You may also you some alias for ngettext (plural texts) but you can face with a problem that -your plural texts are not being extracted -That is because by default 'pybabel extract' recognizes the following keywords: - _, gettext, ngettext, ugettext, ungettext, dgettext, dngettext, N_ -To add your own keyword you can use '-k' flag -In this example for 'ngettext' i will assign double underscore alias '__' - -Full command with pluralization support will look so: - pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs . - -Then create directories with translations (get list of all locales: 'pybabel --list-locales'): - pybabel init -i locales/{domain_name}.pot -d locales -l en - pybabel init -i locales/{domain_name}.pot -d locales -l ru - pybabel init -i locales/{domain_name}.pot -d locales -l uz_Latn - -Now you can translate the texts located in locales/{language}/LC_MESSAGES/{domain_name}.po -After you translated all the texts you need to compile .po files: - pybabel compile -d locales - -When you delete/update your texts you also need to update them in .po files: - pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs . - pybabel update -i locales/{domain_name}.pot -d locales - - translate - pybabel compile -d locales - -If you have any exceptions check: - - you have installed babel - - translations are ready, so you just compiled it - - in the commands above you replaced {domain_name} to messages - - you are writing commands from correct path in terminal -""" - -from functools import wraps -import keyboards -from telebot import TeleBot, types, custom_filters -from telebot.storage.memory_storage import StateMemoryStorage -from i18n_class import I18N - -storage = StateMemoryStorage() -bot = TeleBot("", state_storage=storage) - -i18n = I18N(translations_path='locales', domain_name='messages') -_ = i18n.gettext # for singular translations -__ = i18n.ngettext # for plural translations - -# These are example storages, do not use it in a production development -users_lang = {} -users_clicks = {} - - -def get_user_language(func): - """ - This decorator will pass to your handler current user's language - """ - - @wraps(func) - def inner(*args, **kwargs): - obj = args[0] - kwargs.update(lang=users_lang.get(obj.from_user.id, 'en')) - return func(*args, **kwargs) - - return inner - - -@bot.message_handler(commands='start') -@get_user_language -def start_handler(message: types.Message, lang): - text = _("Hello, {user_fist_name}!\n" - "This is the example of multilanguage bot.\n" - "Available commands:\n\n" - "/lang - change your language\n" - "/plural - pluralization example", lang=lang) - - # remember don't use f string for interpolation, use .format method instead - text = text.format(user_fist_name=message.from_user.first_name) - bot.send_message(message.from_user.id, text) - - -@bot.message_handler(commands='lang') -def change_language_handler(message: types.Message): - bot.send_message(message.chat.id, "Choose language\nВыберите язык\nTilni tanlang", - reply_markup=keyboards.languages_keyboard()) - - -@bot.callback_query_handler(func=None, text=custom_filters.TextFilter(contains=['en', 'ru', 'uz_Latn'])) -def language_handler(call: types.CallbackQuery): - lang = call.data - users_lang[call.from_user.id] = lang - - bot.edit_message_text(_("Language has been changed", lang=lang), call.from_user.id, call.message.id) - bot.delete_state(call.from_user.id) - - -@bot.message_handler(commands='plural') -@get_user_language -def pluralization_handler(message: types.Message, lang): - if not users_clicks.get(message.from_user.id): - users_clicks[message.from_user.id] = 0 - clicks = users_clicks[message.from_user.id] - - text = __( - singular="You have {number} click", - plural="You have {number} clicks", - n=clicks, - lang=lang - ) - text = _("This is clicker.\n\n", lang=lang) + text.format(number=clicks) - bot.send_message(message.chat.id, text, reply_markup=keyboards.clicker_keyboard(_, lang)) - - -@bot.callback_query_handler(func=None, text=custom_filters.TextFilter(equals='click')) -@get_user_language -def click_handler(call: types.CallbackQuery, lang): - if not users_clicks.get(call.from_user.id): - users_clicks[call.from_user.id] = 1 - else: - users_clicks[call.from_user.id] += 1 - - clicks = users_clicks[call.from_user.id] - - text = __( - singular="You have {number} click", - plural="You have {number} clicks", - n=clicks, - lang=lang - ) - text = _("This is clicker.\n\n", lang=lang) + text.format(number=clicks) - bot.edit_message_text(text, call.from_user.id, call.message.message_id, - reply_markup=keyboards.clicker_keyboard(_, lang)) - - -if __name__ == '__main__': - bot.add_custom_filter(custom_filters.TextMatchFilter()) - bot.infinity_polling()