# presentation/bot/i18n.py from pathlib import Path from fluent.runtime import FluentBundle, FluentResource class I18n: def __init__(self, locale: str = "ru"): self.locale = locale self.bundle = self._load_bundle(locale) self._register_methods() # создаём методы для автодополнения def _load_bundle(self, locale: str) -> FluentBundle: """Загружает все .ftl файлы для указанной локали.""" locale_dir = Path(__file__).parent / "locales" / locale if not locale_dir.exists(): raise FileNotFoundError(f"Папка локализации не найдена: {locale_dir}") bundle = FluentBundle([locale]) for ftl_path in sorted(locale_dir.rglob("*.ftl")): with open(ftl_path, "r", encoding="utf-8") as f: resource = FluentResource(f.read()) bundle.add_resource(resource) return bundle def t(self, key: str, **kwargs) -> str: """ Возвращает текст по ключу с подстановкой kwargs. Если kwargs пусты, placeholders остаются в тексте в виде {name}. """ message = self.bundle.get_message(key) if not message: raise KeyError(f"Ключ '{key}' не найден в локали '{self.locale}'") # Даже если kwargs пусты, передаем {} — fluent вернет строку value, errors = self.bundle.format_pattern(message.value, kwargs or {}) # errors можно логировать, но не ломать рендер return value def _register_methods(self): """Создаёт методы для каждого ключа .ftl с автокомплитом.""" for key in self.bundle._messages.keys(): method_name = key.replace("-", "_") setattr(self, method_name, self._make_method(key)) def _make_method(self, key): """Метод для каждого ключа .ftl.""" def method(**kwargs): return self.t(key, **kwargs) return method # Экземпляр для бота i18n = I18n(locale="ru")