import gettext import logging import os from pathlib import Path LOG = logging.getLogger("frigate-camera-control-bridge") TRANSLATION_DOMAIN = "frigate_camera_control_bridge" DEFAULT_LANGUAGE = "en" DEFAULT_LOCALE_DIR = Path(__file__).resolve().parents[1] / "locale" LOCALE_DIR = Path(os.environ.get("LOCALE_DIR", DEFAULT_LOCALE_DIR)) _TRANSLATIONS = {} _AVAILABLE_LANGUAGES = None def parse_po_language_name(path): # gettext compiled catalogs do not expose a friendly language name, so the # editable .po file may carry an optional Language-Name header for the GUI. try: text = path.read_text(encoding="utf-8") except OSError: return None for line in text.splitlines(): if line.startswith('"Language-Name:'): return line.split(":", 1)[1].strip().strip("\\n\"") return None def available_languages(): # Discover languages dynamically from locale/*/LC_MESSAGES/*.mo. Adding a # language should not require a code change; ship a new .po/.mo pair instead. global _AVAILABLE_LANGUAGES if _AVAILABLE_LANGUAGES is not None: return _AVAILABLE_LANGUAGES languages = {DEFAULT_LANGUAGE: DEFAULT_LANGUAGE.upper()} if LOCALE_DIR.exists(): for item in sorted(LOCALE_DIR.iterdir()): if not item.is_dir(): continue code = item.name catalog = item / "LC_MESSAGES" / f"{TRANSLATION_DOMAIN}.mo" if catalog.exists(): languages[code] = ( parse_po_language_name(item / "LC_MESSAGES" / f"{TRANSLATION_DOMAIN}.po") or code.upper() ) _AVAILABLE_LANGUAGES = languages return languages def normalize_language(value): if not value: return DEFAULT_LANGUAGE base = str(value).lower().replace("_", "-").split("-", 1)[0] return base if base in available_languages() else DEFAULT_LANGUAGE def translation_for(lang): # fallback=True makes English msgids work even if a translation file is # missing, which is useful while developing a new language catalog. lang = normalize_language(lang) if lang not in _TRANSLATIONS: _TRANSLATIONS[lang] = gettext.translation( TRANSLATION_DOMAIN, localedir=str(LOCALE_DIR), languages=[lang], fallback=True, ) return _TRANSLATIONS[lang] def text_for(lang, msgid, **kwargs): text = translation_for(lang).gettext(msgid) if kwargs: try: return text.format(**kwargs) except KeyError as exc: LOG.warning("missing translation placeholder %s for %s", exc, msgid) return text