CCCPaste

i18n fluent

# 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")