mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 07:03:53 +08:00
91 lines
2.9 KiB
Python
91 lines
2.9 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
from functools import lru_cache
|
|
from pathlib import Path
|
|
|
|
from app.core.config import get_settings
|
|
|
|
LANGUAGE_FILES: dict[str, str] = {
|
|
"zh-CN": "messages_zh_CN.properties",
|
|
"zh-TW": "messages_zh_TW.properties",
|
|
"en-US": "messages_en_US.properties",
|
|
"de-DE": "messages_de_DE.properties",
|
|
"vi-VN": "messages_vi_VN.properties",
|
|
"pt-BR": "messages_pt_BR.properties",
|
|
}
|
|
_UNICODE_ESCAPE = re.compile(r"\\u([0-9a-fA-F]{4})")
|
|
|
|
|
|
def resolve_language(accept_language: str | None) -> str:
|
|
if not accept_language:
|
|
return "zh-CN"
|
|
primary = accept_language.split(",", 1)[0].split(";", 1)[0].strip().replace("_", "-")
|
|
exact = {key.lower(): key for key in LANGUAGE_FILES}
|
|
if primary.lower() in exact:
|
|
return exact[primary.lower()]
|
|
prefix = primary.lower().split("-", 1)[0]
|
|
return {
|
|
"zh": "zh-CN",
|
|
"en": "en-US",
|
|
"de": "de-DE",
|
|
"vi": "vi-VN",
|
|
"pt": "pt-BR",
|
|
}.get(prefix, "zh-CN")
|
|
|
|
|
|
def _unescape(value: str) -> str:
|
|
decoded = _UNICODE_ESCAPE.sub(lambda match: chr(int(match.group(1), 16)), value)
|
|
return (
|
|
decoded.replace("\\t", "\t")
|
|
.replace("\\n", "\n")
|
|
.replace("\\r", "\r")
|
|
.replace("\\f", "\f")
|
|
.replace("\\=", "=")
|
|
.replace("\\:", ":")
|
|
.replace("\\ ", " ")
|
|
.replace("\\\\", "\\")
|
|
)
|
|
|
|
|
|
def _load_properties(path: Path) -> dict[str, str]:
|
|
messages: dict[str, str] = {}
|
|
if not path.exists():
|
|
return messages
|
|
continuation = ""
|
|
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
|
line = continuation + raw_line
|
|
if line.endswith("\\") and not line.endswith("\\\\"):
|
|
continuation = line[:-1]
|
|
continue
|
|
continuation = ""
|
|
stripped = line.strip()
|
|
if not stripped or stripped.startswith(("#", "!")):
|
|
continue
|
|
delimiter = "=" if "=" in line else ":"
|
|
if delimiter not in line:
|
|
continue
|
|
key, value = line.split(delimiter, 1)
|
|
messages[key.strip()] = _unescape(value.strip())
|
|
return messages
|
|
|
|
|
|
@lru_cache(maxsize=16)
|
|
def messages_for(language: str, i18n_dir: str | None = None) -> dict[str, str]:
|
|
directory = Path(i18n_dir) if i18n_dir else get_settings().i18n_dir
|
|
default_messages = _load_properties(directory / "messages.properties")
|
|
default_messages.update(_load_properties(directory / LANGUAGE_FILES.get(language, LANGUAGE_FILES["zh-CN"])))
|
|
return default_messages
|
|
|
|
|
|
def message_for(code: int, accept_language: str | None, *params: object) -> str:
|
|
language = resolve_language(accept_language)
|
|
template = messages_for(language).get(str(code), str(code))
|
|
for index, param in enumerate(params):
|
|
template = template.replace("{" + str(index) + "}", str(param))
|
|
return template
|
|
|
|
|
|
def clear_i18n_cache() -> None:
|
|
messages_for.cache_clear()
|