Added html/md_text properties to Message object and refactor I18n context

This commit is contained in:
Alex Root Junior 2021-09-23 23:45:22 +03:00
parent 481aec2144
commit c19cbc6a5f
10 changed files with 99 additions and 28 deletions

View file

@ -37,5 +37,5 @@ __all__ = (
"md",
)
__version__ = "3.0.0a16"
__version__ = "3.0.0a17"
__api_version__ = "5.3"

View file

@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, List, Optional, Union
from pydantic import Field
from aiogram.utils import helper
from aiogram.utils.text_decorations import TextDecoration, html_decoration, markdown_decoration
from .base import UNSET, TelegramObject
@ -259,6 +260,22 @@ class Message(TelegramObject):
return ContentType.UNKNOWN
def _unparse_entities(self, text_decoration: TextDecoration) -> str:
text = self.text or self.caption
if text is None:
raise TypeError("This message doesn't have any text.")
entities = self.entities or self.caption_entities
return text_decoration.unparse(text=text, entities=entities)
@property
def html_text(self) -> str:
return self._unparse_entities(html_decoration)
@property
def md_text(self) -> str:
return self._unparse_entities(markdown_decoration)
def reply_animation(
self,
animation: Union[InputFile, str],

View file

@ -1,14 +1,11 @@
from contextvars import ContextVar
from typing import Any, Optional
from typing import Any
from aiogram.utils.i18n.core import I18n
from aiogram.utils.i18n.lazy_proxy import LazyProxy
ctx_i18n: ContextVar[Optional[I18n]] = ContextVar("aiogram_ctx_i18n", default=None)
def get_i18n() -> I18n:
i18n = ctx_i18n.get()
i18n = I18n.get_current(no_error=True)
if i18n is None:
raise LookupError("I18n context is not set")
return i18n

View file

@ -1,24 +1,26 @@
import gettext
import os
from contextlib import contextmanager
from contextvars import ContextVar
from pathlib import Path
from typing import Dict, Optional, Tuple, Union
from typing import Dict, Generator, Optional, Tuple, Union
from aiogram.utils.i18n.lazy_proxy import LazyProxy
from aiogram.utils.mixins import ContextInstanceMixin
class I18n:
class I18n(ContextInstanceMixin["I18n"]):
def __init__(
self,
*,
path: Union[str, Path],
locale: str = "en",
default_locale: str = "en",
domain: str = "messages",
) -> None:
self.path = path
self.locale = locale
self.default_locale = default_locale
self.domain = domain
self.ctx_locale = ContextVar("aiogram_ctx_locale", default=locale)
self.ctx_locale = ContextVar("aiogram_ctx_locale", default=default_locale)
self.locales = self.find_locales()
@property
@ -29,6 +31,28 @@ class I18n:
def current_locale(self, value: str) -> None:
self.ctx_locale.set(value)
@contextmanager
def use_locale(self, locale: str) -> Generator[None, None, None]:
"""
Create context with specified locale
"""
ctx_token = self.ctx_locale.set(locale)
try:
yield
finally:
self.ctx_locale.reset(ctx_token)
@contextmanager
def context(self) -> Generator["I18n", None, None]:
"""
Use I18n context
"""
token = self.set_current(self)
try:
yield self
finally:
self.reset_current(token)
def find_locales(self) -> Dict[str, gettext.GNUTranslations]:
"""
Load all compiled locales from path

View file

@ -1,3 +1,4 @@
import logging
from abc import ABC, abstractmethod
from typing import Any, Awaitable, Callable, Dict, Optional, Set, cast
@ -9,9 +10,10 @@ except ImportError: # pragma: no cover
from aiogram import BaseMiddleware, Router
from aiogram.dispatcher.fsm.context import FSMContext
from aiogram.types import TelegramObject, User
from aiogram.utils.i18n.context import ctx_i18n
from aiogram.utils.i18n.core import I18n
logger = logging.getLogger(__name__)
class I18nMiddleware(BaseMiddleware, ABC):
"""
@ -60,17 +62,16 @@ class I18nMiddleware(BaseMiddleware, ABC):
event: TelegramObject,
data: Dict[str, Any],
) -> Any:
self.i18n.current_locale = await self.get_locale(event=event, data=data)
current_locale = await self.get_locale(event=event, data=data) or self.i18n.default_locale
logger.debug("Detected locale %r", current_locale)
if self.i18n_key:
data[self.i18n_key] = self.i18n
if self.middleware_key:
data[self.middleware_key] = self
token = ctx_i18n.set(self.i18n)
try:
with self.i18n.context(), self.i18n.use_locale(current_locale):
return await handler(event, data)
finally:
ctx_i18n.reset(token)
@abstractmethod
async def get_locale(self, event: TelegramObject, data: Dict[str, Any]) -> str:
@ -118,10 +119,10 @@ class SimpleI18nMiddleware(I18nMiddleware):
event_from_user: Optional[User] = data.get("event_from_user", None)
if event_from_user is None:
return self.i18n.locale
return self.i18n.default_locale
locale = Locale.parse(event_from_user.language_code, sep="-")
if locale.language not in self.i18n.available_locales:
return self.i18n.locale
return self.i18n.default_locale
return cast(str, locale.language)