Files
tg_bot_language/utils/i18n.py
mamonov.ep 3e5c1be464 feat: add translation language setting & onboarding flow
- Add separate translation_language setting (independent from interface language)
- Implement 3-step onboarding for new users:
  1. Choose interface language
  2. Choose learning language
  3. Choose translation language
- Fix localization issues when using callback.message (user_id from state)
- Add UserService.get_user_by_id() method
- Add get_user_translation_lang() helper in i18n
- Update all handlers to use correct translation language
- Add localization keys for onboarding (ru/en/ja)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-07 16:35:08 +03:00

65 lines
2.0 KiB
Python

import json
from pathlib import Path
from functools import lru_cache
from typing import Any, Dict
FALLBACK_LANG = "ru"
@lru_cache(maxsize=16)
def _load_lang(lang: str) -> Dict[str, Any]:
base_dir = Path(__file__).resolve().parents[1] / "locales"
file_path = base_dir / f"{lang}.json"
if not file_path.exists():
# fallback to default
if lang != FALLBACK_LANG:
return _load_lang(FALLBACK_LANG)
return {}
try:
return json.loads(file_path.read_text(encoding="utf-8"))
except Exception:
return {}
def _resolve_key(data: Dict[str, Any], dotted_key: str) -> Any:
cur: Any = data
for part in dotted_key.split("."):
if not isinstance(cur, dict) or part not in cur:
return None
cur = cur[part]
return cur
def get_user_lang(user) -> str:
"""Унифицированное получение языка интерфейса пользователя."""
return (getattr(user, 'language_interface', None) if user else None) or 'ru'
def get_user_translation_lang(user) -> str:
"""Получить язык перевода (translation_language или language_interface как fallback)."""
translation_lang = getattr(user, 'translation_language', None) if user else None
if translation_lang:
return translation_lang
return get_user_lang(user)
def t(lang: str, key: str, **kwargs) -> str:
"""Translate key for given lang; fallback to ru and to key itself.
Supports dotted keys and str.format(**kwargs) placeholders.
"""
data = _load_lang(lang or FALLBACK_LANG)
value = _resolve_key(data, key)
if value is None and lang != FALLBACK_LANG:
value = _resolve_key(_load_lang(FALLBACK_LANG), key)
if value is None:
value = key # last resort: return the key
try:
if isinstance(value, str) and kwargs:
return value.format(**kwargs)
except Exception:
pass
return value if isinstance(value, str) else str(value)