mirror of
https://github.com/aiogram/aiogram.git
synced 2026-04-08 16:37:47 +00:00
Added docs
This commit is contained in:
parent
3627f36a54
commit
26d531a304
5 changed files with 268 additions and 16 deletions
|
|
@ -14,12 +14,23 @@ from aiogram.utils.i18n.core import I18n
|
|||
|
||||
|
||||
class I18nMiddleware(BaseMiddleware, ABC):
|
||||
"""
|
||||
Abstract I18n middleware.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
i18n: I18n,
|
||||
i18n_key: Optional[str] = "i18n",
|
||||
middleware_key: str = "i18n_middleware",
|
||||
) -> None:
|
||||
"""
|
||||
Create an instance of middleware
|
||||
|
||||
:param i18n: instance of I18n
|
||||
:param i18n_key: context key for I18n instance
|
||||
:param middleware_key: context key for this middleware
|
||||
"""
|
||||
self.i18n = i18n
|
||||
self.i18n_key = i18n_key
|
||||
self.middleware_key = middleware_key
|
||||
|
|
@ -27,6 +38,13 @@ class I18nMiddleware(BaseMiddleware, ABC):
|
|||
def setup(
|
||||
self: BaseMiddleware, router: Router, exclude: Optional[Set[str]] = None
|
||||
) -> BaseMiddleware:
|
||||
"""
|
||||
Register middleware for all events in the Router
|
||||
|
||||
:param router:
|
||||
:param exclude:
|
||||
:return:
|
||||
"""
|
||||
if exclude is None:
|
||||
exclude = set()
|
||||
exclude_events = {"update", "error", *exclude}
|
||||
|
|
@ -56,12 +74,32 @@ class I18nMiddleware(BaseMiddleware, ABC):
|
|||
|
||||
@abstractmethod
|
||||
async def get_locale(self, event: TelegramObject, data: Dict[str, Any]) -> str:
|
||||
"""
|
||||
Detect current user locale based on event and context.
|
||||
|
||||
**This method must be defined in child classes**
|
||||
|
||||
:param event:
|
||||
:param data:
|
||||
:return:
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class SimpleI18nMiddleware(I18nMiddleware):
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
super().__init__(*args, **kwargs)
|
||||
"""
|
||||
Simple I18n middleware.
|
||||
|
||||
Chooses language code from the User object received in event
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
i18n: I18n,
|
||||
i18n_key: Optional[str] = "i18n",
|
||||
middleware_key: str = "i18n_middleware",
|
||||
) -> None:
|
||||
super().__init__(i18n=i18n, i18n_key=i18n_key, middleware_key=middleware_key)
|
||||
|
||||
if Locale is None: # pragma: no cover
|
||||
raise RuntimeError(
|
||||
|
|
@ -88,8 +126,18 @@ class SimpleI18nMiddleware(I18nMiddleware):
|
|||
|
||||
|
||||
class ConstI18nMiddleware(I18nMiddleware):
|
||||
def __init__(self, locale: str, *args: Any, **kwargs: Any) -> None:
|
||||
super().__init__(*args, **kwargs)
|
||||
"""
|
||||
Const middleware chooses statically defined locale
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
locale: str,
|
||||
i18n: I18n,
|
||||
i18n_key: Optional[str] = "i18n",
|
||||
middleware_key: str = "i18n_middleware",
|
||||
) -> None:
|
||||
super().__init__(i18n=i18n, i18n_key=i18n_key, middleware_key=middleware_key)
|
||||
self.locale = locale
|
||||
|
||||
async def get_locale(self, event: TelegramObject, data: Dict[str, Any]) -> str:
|
||||
|
|
@ -97,8 +145,18 @@ class ConstI18nMiddleware(I18nMiddleware):
|
|||
|
||||
|
||||
class FSMI18nMiddleware(SimpleI18nMiddleware):
|
||||
def __init__(self, *args: Any, key: str = "locale", **kwargs: Any) -> None:
|
||||
super().__init__(*args, **kwargs)
|
||||
"""
|
||||
This middleware stores locale in the FSM storage
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
i18n: I18n,
|
||||
key: str = "locale",
|
||||
i18n_key: Optional[str] = "i18n",
|
||||
middleware_key: str = "i18n_middleware",
|
||||
) -> None:
|
||||
super().__init__(i18n=i18n, i18n_key=i18n_key, middleware_key=middleware_key)
|
||||
self.key = key
|
||||
|
||||
async def get_locale(self, event: TelegramObject, data: Dict[str, Any]) -> str:
|
||||
|
|
@ -114,5 +172,11 @@ class FSMI18nMiddleware(SimpleI18nMiddleware):
|
|||
return locale
|
||||
|
||||
async def set_locale(self, state: FSMContext, locale: str) -> None:
|
||||
"""
|
||||
Write new locale to the storage
|
||||
|
||||
:param state: instance of FSMContext
|
||||
:param locale: new locale
|
||||
"""
|
||||
await state.update_data(data={self.key: locale})
|
||||
self.i18n.current_locale = locale
|
||||
|
|
|
|||
10
docs/_static/stylesheets/extra.css
vendored
10
docs/_static/stylesheets/extra.css
vendored
|
|
@ -10,13 +10,3 @@
|
|||
code, kbd, pre {
|
||||
font-family: "JetBrainsMono", "Roboto Mono", "Courier New", Courier, monospace;
|
||||
}
|
||||
|
||||
.highlight * {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.highlight * {
|
||||
background: #424242;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,4 +93,5 @@ Contents
|
|||
install
|
||||
api/index
|
||||
dispatcher/index
|
||||
utils/index
|
||||
changelog
|
||||
|
|
|
|||
190
docs/utils/i18n.rst
Normal file
190
docs/utils/i18n.rst
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
===========
|
||||
Translation
|
||||
===========
|
||||
|
||||
In order to make you bot translatable you have to add a minimal number of hooks to your Python code.
|
||||
|
||||
These hooks are called translation strings.
|
||||
|
||||
The aiogram translation utils is build on top of `GNU gettext Python module <https://docs.python.org/3/library/gettext.html>`_
|
||||
and `Babel library <http://babel.pocoo.org/en/latest/>`_.
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
Babel is required to make simple way to extract translation strings from your code
|
||||
|
||||
Can be installed from pip directly:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install Babel
|
||||
|
||||
|
||||
or as `aiogram` extra dependency:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install aiogram[i18n]
|
||||
|
||||
|
||||
Make messages translatable
|
||||
==========================
|
||||
|
||||
In order to gettext need to know what the strings should be translated you will need to write translation strings.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 6-8
|
||||
|
||||
from aiogram import html
|
||||
from aiogram.utils.i18n import gettext as _
|
||||
|
||||
async def my_handler(message: Message) -> None:
|
||||
await message.answer(
|
||||
_("Hello, {name}!").format(
|
||||
name=html.quote(message.from_user.full_name)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
.. danger::
|
||||
|
||||
f-strings can't be used as translations string because any dynamic variables should be added to message after getting translated message
|
||||
|
||||
|
||||
Also if you want to use translated string in keyword- or magic- filters you will need to use lazy gettext calls:
|
||||
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 4
|
||||
|
||||
from aiogram import F
|
||||
from aiogram.utils.i18n import lazy_gettext as __
|
||||
|
||||
@router.message(F.text.lower() == __("My menu entry"))
|
||||
...
|
||||
|
||||
|
||||
.. danger::
|
||||
|
||||
Lazy gettext calls should always be used when the current language is not know at the moment
|
||||
|
||||
|
||||
.. danger::
|
||||
|
||||
Lazy gettext can't be used as value for API methods or any Telegram Object (like :class:`aiogram.types.inline_keyboard_button.InlineKeyboardButton` or etc.)
|
||||
|
||||
Configuring engine
|
||||
==================
|
||||
|
||||
After you messages is already done to use gettext your bot should know how to detect user language
|
||||
|
||||
On top of your application the instance of :class:`aiogram.utils.i18n.code.I18n` should be created
|
||||
|
||||
|
||||
.. code-block::
|
||||
|
||||
i18n = I18n(path="locales", language="en", domain="messages")
|
||||
|
||||
|
||||
After that you will need to choose one of builtin I18n middleware or write your own.
|
||||
|
||||
Builtin middlewares:
|
||||
|
||||
|
||||
SimpleI18nMiddleware
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: aiogram.utils.i18n.middleware.SimpleI18nMiddleware
|
||||
:members: __init__
|
||||
|
||||
ConstI18nMiddleware
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: aiogram.utils.i18n.middleware.ConstI18nMiddleware
|
||||
:members: __init__
|
||||
|
||||
FSMI18nMiddleware
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: aiogram.utils.i18n.middleware.FSMI18nMiddleware
|
||||
:members: __init__,set_locale
|
||||
|
||||
|
||||
I18nMiddleware
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
or define you own based on abstract I18nMiddleware middleware:
|
||||
|
||||
.. autoclass:: aiogram.utils.i18n.middleware.FSMI18nMiddleware
|
||||
:members: __init__,setup,get_locale
|
||||
|
||||
|
||||
Deal with Babel
|
||||
===============
|
||||
|
||||
Step 1: Extract messages
|
||||
-------------------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pybabel extract --input-dirs=. -o locales/messages.pot
|
||||
|
||||
|
||||
Here is :code:`--input-dirs=.` - path to code and the :code:`locales/messages.pot`
|
||||
is template where messages will be extracted and `messages` is translation domain.
|
||||
|
||||
.. note::
|
||||
|
||||
Some useful options:
|
||||
|
||||
- Extract texts with pluralization support :code:`-k __:1,2`
|
||||
- Add comments for translators, you can use another tag if you want (TR) :code:`--add-comments=NOTE`
|
||||
- Disable comments with string location in code :code:`--no-location`
|
||||
- Set project name :code:`--project=MySuperBot`
|
||||
- Set version :code:`--version=2.2`
|
||||
|
||||
|
||||
Step 2: Init language
|
||||
----------------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pybabel init -i locales/messages.pot -d locales -D messages -l en
|
||||
|
||||
- :code:`-i locales/messages.pot` - pre-generated template
|
||||
- :code:`-d locales` - translations directory
|
||||
- :code:`-D messages` - translations domain
|
||||
- :code:`-l en` - language. Can be changed to any other valid language code (For example :code:`-l uk` for ukrainian language)
|
||||
|
||||
|
||||
Step 3: Translate texts
|
||||
-----------------------
|
||||
|
||||
To open .po file you can use basic text editor or any PO editor, e.g. `Poedit <https://poedit.net/>`_
|
||||
|
||||
Just open the file named :code:`locales/{language}/LC_MESSAGES/messages.po` and write translations
|
||||
|
||||
Step 4: Compile translations
|
||||
----------------------------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pybabel compile -d locales -D messages
|
||||
|
||||
|
||||
Step 5: Updating messages
|
||||
-------------------------
|
||||
|
||||
When you change the code of your bot you need to update po & mo files
|
||||
|
||||
- Step 5.1: regenerate pot file: command from step 1
|
||||
- Step 5.2: update po files
|
||||
.. code-block::
|
||||
|
||||
pybabel update -d locales -D messages -i locales/messages.pot
|
||||
|
||||
- Step 5.3: update your translations: location and tools you know from step 3
|
||||
- Step 5.4: compile mo files: command from step 4
|
||||
7
docs/utils/index.rst
Normal file
7
docs/utils/index.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
=====
|
||||
Utils
|
||||
=====
|
||||
|
||||
.. toctree::
|
||||
|
||||
i18n
|
||||
Loading…
Add table
Add a link
Reference in a new issue