Modularize bridge and improve control GUI
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
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
|
||||
Reference in New Issue
Block a user