Files
mailova/app/i18n.py

63 lines
1.8 KiB
Python

"""i18n module — loads translations from app/locales/*.json.
Usage in templates: {{ 'key'|t(user.language) }}
Usage in Python: from app.i18n import t; t("key", lang)
To add a new language:
1. Create app/locales/<lang>.json with translation keys
2. Done — it will be auto-detected and available in the UI
"""
import json
from pathlib import Path
_LOCALES_DIR = Path(__file__).resolve().parent / "locales"
_cache: dict[str, dict[str, str]] = {}
_loaded = False
def _load() -> None:
global _loaded
if _loaded:
return
_LOCALES_DIR.mkdir(exist_ok=True)
for f in _LOCALES_DIR.glob("*.json"):
lang = f.stem # e.g. "zh", "en", "ja"
with open(f, encoding="utf-8") as fh:
_cache[lang] = json.load(fh)
_loaded = True
def t(key: str, lang: str = "zh") -> str:
"""Translate *key* to *lang*. Falls back to zh, then to the raw key."""
_load()
# Try requested language
val = _cache.get(lang, {}).get(key)
if val is not None:
return val
# Fallback to zh
if lang != "zh":
val = _cache.get("zh", {}).get(key)
if val is not None:
return val
# Fallback to any available language
for translations in _cache.values():
if key in translations:
return translations[key]
return key
def get_languages() -> dict[str, str]:
"""Return available languages as {code: native_name}.
Each JSON file can optionally include a top-level ``_meta`` key
with ``{"name": "语言名称"}`` to set the display name.
If absent, the language code is used as-is.
"""
_load()
result: dict[str, str] = {}
for lang in sorted(_cache):
meta = _cache[lang].get("_meta", {})
result[lang] = meta.get("name", lang)
return result