initial commit, functinal software
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
"""i18n helpers."""
|
||||
|
||||
from orisys_app_shared.i18n.translator import Translator, get_translator, set_locale, t
|
||||
|
||||
__all__ = ["Translator", "get_translator", "set_locale", "t"]
|
||||
@@ -0,0 +1,80 @@
|
||||
"""Simple JSON locale translator with en_US fallback."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from orisys_app_shared.paths import locales_dir
|
||||
|
||||
|
||||
class Translator:
|
||||
def __init__(self, locales_path: Path | None = None, locale: str = "zh_CN") -> None:
|
||||
self._locales_path = locales_path or locales_dir()
|
||||
self._locale = locale
|
||||
self._strings: dict[str, Any] = {}
|
||||
self._fallback: dict[str, Any] = {}
|
||||
self.set_locale(locale)
|
||||
|
||||
@property
|
||||
def locale(self) -> str:
|
||||
return self._locale
|
||||
|
||||
def set_locale(self, locale: str) -> None:
|
||||
self._locale = locale
|
||||
self._fallback = self._load("en_US")
|
||||
self._strings = self._load(locale)
|
||||
|
||||
def _load(self, locale: str) -> dict[str, Any]:
|
||||
path = self._locales_path / f"{locale}.json"
|
||||
if not path.is_file():
|
||||
return {}
|
||||
try:
|
||||
data = json.loads(path.read_text(encoding="utf-8"))
|
||||
except (OSError, json.JSONDecodeError):
|
||||
return {}
|
||||
return data if isinstance(data, dict) else {}
|
||||
|
||||
def t(self, key: str, **kwargs: Any) -> str:
|
||||
value = self._lookup(self._strings, key)
|
||||
if value is None:
|
||||
value = self._lookup(self._fallback, key)
|
||||
if value is None:
|
||||
return key
|
||||
text = str(value)
|
||||
if kwargs:
|
||||
try:
|
||||
return text.format(**kwargs)
|
||||
except (KeyError, ValueError):
|
||||
return text
|
||||
return text
|
||||
|
||||
@staticmethod
|
||||
def _lookup(data: dict[str, Any], key: str) -> Any | None:
|
||||
node: Any = data
|
||||
for part in key.split("."):
|
||||
if not isinstance(node, dict) or part not in node:
|
||||
return None
|
||||
node = node[part]
|
||||
return node
|
||||
|
||||
|
||||
_translator: Translator | None = None
|
||||
|
||||
|
||||
def get_translator() -> Translator:
|
||||
global _translator
|
||||
if _translator is None:
|
||||
_translator = Translator()
|
||||
return _translator
|
||||
|
||||
|
||||
def t(key: str, **kwargs: Any) -> str:
|
||||
return get_translator().t(key, **kwargs)
|
||||
|
||||
|
||||
def set_locale(locale: str) -> Translator:
|
||||
tr = get_translator()
|
||||
tr.set_locale(locale)
|
||||
return tr
|
||||
Reference in New Issue
Block a user