82 lines
2.6 KiB
Python
82 lines
2.6 KiB
Python
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
|