Initial commit
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
import logging
|
||||
import json
|
||||
import os
|
||||
import aiosqlite
|
||||
from typing import Any ,Awaitable ,Callable ,Dict ,Optional
|
||||
|
||||
from aiogram import BaseMiddleware
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
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 :
|
||||
global _i18n_instance
|
||||
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
|
||||
|
||||
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}")
|
||||
|
||||
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 :
|
||||
|
||||
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 .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
|
||||
}
|
||||
logging .debug (f"I18nMiddleware: Final current_language for event: {current_language}")
|
||||
|
||||
return await handler (event ,data )
|
||||
Reference in New Issue
Block a user