"""Internationalisierung (i18n) fuer die TV-App. Laedt Uebersetzungen aus JSON-Dateien und stellt Jinja2-Filter bereit.""" import json import logging import os from typing import Optional # Verfuegbare Sprachen SUPPORTED_LANGS = ("de", "en") DEFAULT_LANG = "de" # Cache fuer geladene Uebersetzungen _translations: dict[str, dict] = {} def load_translations(static_dir: str) -> None: """Laedt alle Uebersetzungsdateien aus static/tv/i18n/""" i18n_dir = os.path.join(static_dir, "tv", "i18n") for lang in SUPPORTED_LANGS: filepath = os.path.join(i18n_dir, f"{lang}.json") if os.path.isfile(filepath): with open(filepath, "r", encoding="utf-8") as f: _translations[lang] = json.load(f) logging.info(f"i18n: Sprache '{lang}' geladen ({filepath})") else: logging.warning(f"i18n: Datei nicht gefunden: {filepath}") if not _translations: logging.error("i18n: Keine Uebersetzungen geladen!") def get_text(key: str, lang: str = DEFAULT_LANG, **kwargs) -> str: """Gibt uebersetzten Text fuer einen Punkt-separierten Schluessel zurueck. Beispiel: get_text('nav.home', 'de') -> 'Startseite' Platzhalter: get_text('player.next_in', 'de', seconds=10)""" translations = _translations.get(lang, _translations.get(DEFAULT_LANG, {})) parts = key.split(".") value = translations for part in parts: if isinstance(value, dict): value = value.get(part) else: value = None break if value is None: # Fallback auf Default-Sprache if lang != DEFAULT_LANG: return get_text(key, DEFAULT_LANG, **kwargs) # Key als Fallback zurueckgeben return key if not isinstance(value, str): return key # Platzhalter ersetzen if kwargs: for k, v in kwargs.items(): value = value.replace(f"{{{k}}}", str(v)) return value def get_all_translations(lang: str = DEFAULT_LANG) -> dict: """Gibt alle Uebersetzungen fuer eine Sprache zurueck (fuer JS)""" return _translations.get(lang, _translations.get(DEFAULT_LANG, {})) def setup_jinja2_i18n(app) -> None: """Registriert i18n-Filter und Globals in Jinja2-Environment. Muss NACH aiohttp_jinja2.setup() aufgerufen werden.""" import aiohttp_jinja2 env = aiohttp_jinja2.get_env(app) # Filter: {{ 'nav.home'|t }} oder {{ 'nav.home'|t('en') }} def t_filter(key: str, lang: str = None) -> str: # Sprache wird pro Request gesetzt (siehe Middleware) if lang is None: lang = getattr(env, "_current_lang", DEFAULT_LANG) return get_text(key, lang) env.filters["t"] = t_filter # Global-Funktion: {{ t('nav.home') }} oder {{ t('nav.home', seconds=10) }} def t_func(key: str, lang: str = None, **kwargs) -> str: if lang is None: lang = getattr(env, "_current_lang", DEFAULT_LANG) return get_text(key, lang, **kwargs) env.globals["t"] = t_func env.globals["SUPPORTED_LANGS"] = SUPPORTED_LANGS logging.info("i18n: Jinja2-Filter und Globals registriert") def set_request_lang(app, lang: str) -> None: """Setzt die Sprache fuer den aktuellen Request. Wird vom TV-Auth-Middleware aufgerufen.""" import aiohttp_jinja2 env = aiohttp_jinja2.get_env(app) env._current_lang = lang if lang in SUPPORTED_LANGS else DEFAULT_LANG