mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-26 00:53:54 +08:00
feat: add FastAPI manager API compatibility baseline
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user