Code Refactoring
This commit is contained in:
+128
-85
@@ -2,112 +2,155 @@ import logging
|
||||
import json
|
||||
import os
|
||||
import aiosqlite
|
||||
from typing import Any ,Awaitable ,Callable ,Dict ,Optional
|
||||
from typing import Any, Awaitable, Callable, Dict, Optional
|
||||
|
||||
from aiogram import BaseMiddleware
|
||||
from aiogram .types import TelegramObject ,User ,Update
|
||||
from aiogram.types import TelegramObject, User, Update
|
||||
|
||||
from db.database import get_user, get_db_connection_manager, _setup_db_connection
|
||||
from config.settings import Settings
|
||||
|
||||
|
||||
from db .database import get_user ,get_db_connection_manager ,_setup_db_connection
|
||||
from config .settings import Settings
|
||||
class JsonI18n:
|
||||
|
||||
def __init__(self, path: str, default: str = "en", domain: str = "bot"):
|
||||
self.domain = domain
|
||||
self.path = path
|
||||
self.default_lang = default
|
||||
self.locales_data: Dict[str, Dict[str, str]] = {}
|
||||
self._load_locales()
|
||||
logging.info(
|
||||
f"JsonI18n initialized. Loaded languages: {list(self.locales_data.keys())}. Default: {self.default_lang}"
|
||||
)
|
||||
|
||||
def _load_locales(self):
|
||||
if not os.path.isdir(self.path):
|
||||
logging.error(f"Locales path not found: {self.path}")
|
||||
return
|
||||
for item in os.listdir(self.path):
|
||||
if item.endswith(".json"):
|
||||
lang_code = item.split(".")[0]
|
||||
file_path = os.path.join(self.path, item)
|
||||
try:
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
self.locales_data[lang_code] = json.load(f)
|
||||
except Exception as e:
|
||||
logging.error(
|
||||
f"Error loading locale {lang_code} from {file_path}: {e}"
|
||||
)
|
||||
|
||||
def gettext(self, lang_code: Optional[str], key: str, **kwargs) -> str:
|
||||
effective_lang_code = lang_code
|
||||
if not effective_lang_code or effective_lang_code not in self.locales_data:
|
||||
effective_lang_code = self.default_lang
|
||||
lang_data = self.locales_data.get(effective_lang_code)
|
||||
if lang_data is None:
|
||||
logging.warning(
|
||||
f"No language data for '{effective_lang_code}' (default: '{self.default_lang}'). Key '{key}'."
|
||||
)
|
||||
return key.format(**kwargs) if kwargs else key
|
||||
text = lang_data.get(key)
|
||||
if text is None:
|
||||
if effective_lang_code != self.default_lang:
|
||||
default_lang_data = self.locales_data.get(
|
||||
self.default_lang, {})
|
||||
text = default_lang_data.get(key)
|
||||
if text is None: return key.format(**kwargs) if kwargs else key
|
||||
try:
|
||||
return text.format(**kwargs) if kwargs else text
|
||||
except KeyError as e:
|
||||
logging.warning(
|
||||
f"Missing format key {e} for key '{key}' (lang: {effective_lang_code}). Text: '{text}'"
|
||||
)
|
||||
return text
|
||||
except Exception as e:
|
||||
logging.error(
|
||||
f"Error formatting key '{key}' (lang: {effective_lang_code}): {e}. Text: '{text}'"
|
||||
)
|
||||
return text
|
||||
|
||||
|
||||
_i18n_instance: Optional[JsonI18n] = None
|
||||
|
||||
|
||||
class JsonI18n :
|
||||
def __init__ (self ,path :str ,default :str ="en",domain :str ="bot"):
|
||||
self .domain =domain
|
||||
self .path =path
|
||||
self .default_lang =default
|
||||
self .locales_data :Dict [str ,Dict [str ,str ]]={}
|
||||
self ._load_locales ()
|
||||
logging .info (f"JsonI18n initialized. Loaded languages: {list(self.locales_data.keys())}. Default: {self.default_lang}")
|
||||
|
||||
def _load_locales (self ):
|
||||
if not os .path .isdir (self .path ):logging .error (f"Locales path not found: {self.path}");return
|
||||
for item in os .listdir (self .path ):
|
||||
if item .endswith (".json"):
|
||||
lang_code =item .split (".")[0 ];file_path =os .path .join (self .path ,item )
|
||||
try :
|
||||
with open (file_path ,"r",encoding ="utf-8")as f :self .locales_data [lang_code ]=json .load (f )
|
||||
except Exception as e :logging .error (f"Error loading locale {lang_code} from {file_path}: {e}")
|
||||
|
||||
def gettext (self ,lang_code :Optional [str ],key :str ,**kwargs )->str :
|
||||
effective_lang_code =lang_code
|
||||
if not effective_lang_code or effective_lang_code not in self .locales_data :
|
||||
effective_lang_code =self .default_lang
|
||||
lang_data =self .locales_data .get (effective_lang_code )
|
||||
if lang_data is None :
|
||||
logging .warning (f"No language data for '{effective_lang_code}' (default: '{self.default_lang}'). Key '{key}'.");return key .format (**kwargs )if kwargs else key
|
||||
text =lang_data .get (key )
|
||||
if text is None :
|
||||
if effective_lang_code !=self .default_lang :
|
||||
default_lang_data =self .locales_data .get (self .default_lang ,{});text =default_lang_data .get (key )
|
||||
if text is None :return key .format (**kwargs )if kwargs else key
|
||||
try :return text .format (**kwargs )if kwargs else text
|
||||
except KeyError as e :logging .warning (f"Missing format key {e} for key '{key}' (lang: {effective_lang_code}). Text: '{text}'");return text
|
||||
except Exception as e :logging .error (f"Error formatting key '{key}' (lang: {effective_lang_code}): {e}. Text: '{text}'");return text
|
||||
|
||||
_i18n_instance :Optional [JsonI18n ]=None
|
||||
def get_i18n_instance (path :str ="locales",default :str ="en",domain :str ="bot")->JsonI18n :
|
||||
def get_i18n_instance(path: str = "locales",
|
||||
default: str = "en",
|
||||
domain: str = "bot") -> JsonI18n:
|
||||
global _i18n_instance
|
||||
if _i18n_instance is None :_i18n_instance =JsonI18n (path =path ,default =default ,domain =domain )
|
||||
if _i18n_instance is None:
|
||||
_i18n_instance = JsonI18n(path=path, default=default, domain=domain)
|
||||
return _i18n_instance
|
||||
|
||||
|
||||
class I18nMiddleware (BaseMiddleware ):
|
||||
def __init__ (self ,i18n :JsonI18n ,settings :Settings ):
|
||||
super ().__init__ ()
|
||||
self .i18n =i18n
|
||||
self .settings =settings
|
||||
class I18nMiddleware(BaseMiddleware):
|
||||
|
||||
async def __call__ (
|
||||
self ,
|
||||
handler :Callable [[Update ,Dict [str ,Any ]],Awaitable [Any ]],
|
||||
event :Update ,
|
||||
data :Dict [str ,Any ]
|
||||
)->Any :
|
||||
event_user :Optional [User ]=data .get ("event_from_user")
|
||||
def __init__(self, i18n: JsonI18n, settings: Settings):
|
||||
super().__init__()
|
||||
self.i18n = i18n
|
||||
self.settings = settings
|
||||
|
||||
current_language =self .i18n .default_lang
|
||||
async def __call__(self, handler: Callable[[Update, Dict[str, Any]],
|
||||
Awaitable[Any]], event: Update,
|
||||
data: Dict[str, Any]) -> Any:
|
||||
event_user: Optional[User] = data.get("event_from_user")
|
||||
|
||||
current_language = self.i18n.default_lang
|
||||
|
||||
if event_user :
|
||||
logging .debug (f"I18nMiddleware: Processing for user {event_user.id}")
|
||||
if event_user:
|
||||
logging.debug(
|
||||
f"I18nMiddleware: Processing for user {event_user.id}")
|
||||
|
||||
try :
|
||||
try:
|
||||
|
||||
user_db_data =await get_user (event_user .id )
|
||||
if user_db_data and user_db_data ['language_code']and user_db_data ['language_code']in self .i18n .locales_data :
|
||||
current_language =user_db_data ['language_code']
|
||||
logging .debug (f"I18nMiddleware: User {event_user.id} language loaded from DB: {current_language}")
|
||||
else :
|
||||
user_db_data = await get_user(event_user.id)
|
||||
if user_db_data and user_db_data[
|
||||
'language_code'] and user_db_data[
|
||||
'language_code'] in self.i18n.locales_data:
|
||||
current_language = user_db_data['language_code']
|
||||
logging.debug(
|
||||
f"I18nMiddleware: User {event_user.id} language loaded from DB: {current_language}"
|
||||
)
|
||||
else:
|
||||
|
||||
if event_user .language_code :
|
||||
lang_prefix =event_user .language_code .split ('-')[0 ]
|
||||
if lang_prefix in self .i18n .locales_data :
|
||||
current_language =lang_prefix
|
||||
logging .debug (f"I18nMiddleware: User {event_user.id} language set from Telegram client (prefix): {current_language}")
|
||||
elif event_user .language_code in self .i18n .locales_data :
|
||||
current_language =event_user .language_code
|
||||
logging .debug (f"I18nMiddleware: User {event_user.id} language set from Telegram client (full): {current_language}")
|
||||
else :
|
||||
if event_user.language_code:
|
||||
lang_prefix = event_user.language_code.split('-')[0]
|
||||
if lang_prefix in self.i18n.locales_data:
|
||||
current_language = lang_prefix
|
||||
logging.debug(
|
||||
f"I18nMiddleware: User {event_user.id} language set from Telegram client (prefix): {current_language}"
|
||||
)
|
||||
elif event_user.language_code in self.i18n.locales_data:
|
||||
current_language = event_user.language_code
|
||||
logging.debug(
|
||||
f"I18nMiddleware: User {event_user.id} language set from Telegram client (full): {current_language}"
|
||||
)
|
||||
else:
|
||||
|
||||
logging .debug (f"I18nMiddleware: User {event_user.id} Telegram client language '{event_user.language_code}' not supported. Using default: {current_language}")
|
||||
else :
|
||||
logging .debug (f"I18nMiddleware: User {event_user.id} has no language_code from Telegram. Using default: {current_language}")
|
||||
except Exception as e_db_lang :
|
||||
logging.debug(
|
||||
f"I18nMiddleware: User {event_user.id} Telegram client language '{event_user.language_code}' not supported. Using default: {current_language}"
|
||||
)
|
||||
else:
|
||||
logging.debug(
|
||||
f"I18nMiddleware: User {event_user.id} has no language_code from Telegram. Using default: {current_language}"
|
||||
)
|
||||
except Exception as e_db_lang:
|
||||
|
||||
logging .error (f"I18nMiddleware: Error fetching user language from DB for user {event_user.id}: {e_db_lang}. Falling back.")
|
||||
if event_user .language_code :
|
||||
lang_prefix =event_user .language_code .split ('-')[0 ]
|
||||
if lang_prefix in self .i18n .locales_data :current_language =lang_prefix
|
||||
elif event_user .language_code in self .i18n .locales_data :current_language =event_user .language_code
|
||||
logging.error(
|
||||
f"I18nMiddleware: Error fetching user language from DB for user {event_user.id}: {e_db_lang}. Falling back."
|
||||
)
|
||||
if event_user.language_code:
|
||||
lang_prefix = event_user.language_code.split('-')[0]
|
||||
if lang_prefix in self.i18n.locales_data:
|
||||
current_language = lang_prefix
|
||||
elif event_user.language_code in self.i18n.locales_data:
|
||||
current_language = event_user.language_code
|
||||
|
||||
data ["i18n_data"]={
|
||||
"i18n_instance":self .i18n ,
|
||||
"current_language":current_language
|
||||
data["i18n_data"] = {
|
||||
"i18n_instance": self.i18n,
|
||||
"current_language": current_language
|
||||
}
|
||||
logging .debug (f"I18nMiddleware: Final current_language for event: {current_language}")
|
||||
logging.debug(
|
||||
f"I18nMiddleware: Final current_language for event: {current_language}"
|
||||
)
|
||||
|
||||
return await handler (event ,data )
|
||||
return await handler(event, data)
|
||||
|
||||
Reference in New Issue
Block a user