mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-01-20 03:40:26 +00:00
127 lines
3.7 KiB
Python
127 lines
3.7 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import logging
|
|
import shutil
|
|
from functools import lru_cache
|
|
from pathlib import Path
|
|
from typing import Any, Dict
|
|
|
|
from app.config import settings
|
|
|
|
DEFAULT_LANGUAGE = "ru"
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
_BASE_DIR = Path(__file__).resolve().parent
|
|
_DEFAULT_LOCALES_DIR = _BASE_DIR / "locales"
|
|
|
|
|
|
def _resolve_user_locales_dir() -> Path:
|
|
path = Path(settings.LOCALES_PATH).expanduser()
|
|
if not path.is_absolute():
|
|
path = Path.cwd() / path
|
|
return path
|
|
|
|
|
|
def ensure_locale_templates() -> None:
|
|
destination = _resolve_user_locales_dir()
|
|
try:
|
|
destination.mkdir(parents=True, exist_ok=True)
|
|
except Exception as error:
|
|
_logger.warning("Unable to create locales directory %s: %s", destination, error)
|
|
return
|
|
|
|
if any(destination.glob("*")):
|
|
return
|
|
|
|
if not _DEFAULT_LOCALES_DIR.exists():
|
|
_logger.debug("Default locales directory %s is missing", _DEFAULT_LOCALES_DIR)
|
|
return
|
|
|
|
for template in _DEFAULT_LOCALES_DIR.iterdir():
|
|
if not template.is_file():
|
|
continue
|
|
target_path = destination / template.name
|
|
try:
|
|
shutil.copyfile(template, target_path)
|
|
except Exception as error:
|
|
_logger.warning(
|
|
"Failed to copy default locale %s to %s: %s",
|
|
template,
|
|
target_path,
|
|
error,
|
|
)
|
|
|
|
|
|
def _load_default_locale(language: str) -> Dict[str, Any]:
|
|
default_path = _DEFAULT_LOCALES_DIR / f"{language}.json"
|
|
if not default_path.exists():
|
|
return {}
|
|
return _load_locale_file(default_path)
|
|
|
|
|
|
def _load_user_locale(language: str) -> Dict[str, Any]:
|
|
user_dir = _resolve_user_locales_dir()
|
|
for extension in (".json", ".yml", ".yaml"):
|
|
candidate = user_dir / f"{language}{extension}"
|
|
if candidate.exists():
|
|
return _load_locale_file(candidate)
|
|
return {}
|
|
|
|
|
|
def _load_locale_file(path: Path) -> Dict[str, Any]:
|
|
suffix = path.suffix.lower()
|
|
try:
|
|
if suffix == ".json":
|
|
return json.loads(path.read_text(encoding="utf-8"))
|
|
if suffix in {".yml", ".yaml"}:
|
|
try:
|
|
import yaml # type: ignore
|
|
except ModuleNotFoundError as import_error:
|
|
raise RuntimeError(
|
|
"PyYAML is required to load YAML locale files. Install PyYAML or provide JSON files."
|
|
) from import_error
|
|
return yaml.safe_load(path.read_text(encoding="utf-8")) or {}
|
|
except Exception as error:
|
|
_logger.warning("Failed to parse locale file %s: %s", path, error)
|
|
return {}
|
|
|
|
_logger.warning("Unsupported locale format for %s", path)
|
|
return {}
|
|
|
|
|
|
def _merge_dicts(base: Dict[str, Any], overrides: Dict[str, Any]) -> Dict[str, Any]:
|
|
result = dict(base)
|
|
for key, value in overrides.items():
|
|
if (
|
|
key in result
|
|
and isinstance(result[key], dict)
|
|
and isinstance(value, dict)
|
|
):
|
|
result[key] = _merge_dicts(result[key], value)
|
|
else:
|
|
result[key] = value
|
|
return result
|
|
|
|
|
|
@lru_cache(maxsize=None)
|
|
def load_locale(language: str) -> Dict[str, Any]:
|
|
language = language or DEFAULT_LANGUAGE
|
|
defaults = _load_default_locale(language)
|
|
overrides = _load_user_locale(language)
|
|
merged = _merge_dicts(defaults, overrides)
|
|
|
|
if not merged and language != DEFAULT_LANGUAGE:
|
|
_logger.warning(
|
|
"Locale %s not found. Falling back to default language %s.",
|
|
language,
|
|
DEFAULT_LANGUAGE,
|
|
)
|
|
return load_locale(DEFAULT_LANGUAGE)
|
|
return merged
|
|
|
|
|
|
def clear_locale_cache() -> None:
|
|
load_locale.cache_clear()
|