diff options
| author | tanaka takashi <isazaka@gmail.com> | 2026-06-27 14:49:12 +0900 |
|---|---|---|
| committer | tanaka takashi <isazaka@gmail.com> | 2026-06-27 14:49:12 +0900 |
| commit | f93822cebf6258c3a31e03745183aa2c8bb56f4d (patch) | |
| tree | 8edfba7593d7db039b117fc4d742fbde78b72da6 /src/ollama_web/i18n/__init__.py | |
| parent | b7943a7586fbd0bb0e19aad7732c11a62f72d7c1 (diff) | |
マルチ言語対応。MCP設定画面の言語切り替え表示にバグあり
Diffstat (limited to 'src/ollama_web/i18n/__init__.py')
| -rw-r--r-- | src/ollama_web/i18n/__init__.py | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/src/ollama_web/i18n/__init__.py b/src/ollama_web/i18n/__init__.py new file mode 100644 index 0000000..56dd217 --- /dev/null +++ b/src/ollama_web/i18n/__init__.py @@ -0,0 +1,84 @@ +"""Internationalization support for ollama-web. + +Messages live in JSON files under ``i18n/messages/``. The default language is +read from ``config.settings.language`` and can be overridden per-call via the +``lang`` argument. +""" + +from __future__ import annotations + +import json +from pathlib import Path +from typing import Any + +from ollama_web.config import settings + +_DIR = Path(__file__).resolve().parent +_MESSAGES_DIR = _DIR / "messages" + + +def _load_messages(lang: str) -> dict[str, Any]: + """Load messages for a language, falling back to an empty dict on error.""" + path = _MESSAGES_DIR / f"{lang}.json" + try: + with path.open("r", encoding="utf-8") as f: + return json.load(f) + except (FileNotFoundError, json.JSONDecodeError): + return {} + + +_MESSAGES_CACHE: dict[str, dict[str, Any]] = {} + + +def get_messages(lang: str | None = None) -> dict[str, Any]: + """Return the full message dictionary for the requested language.""" + lang = resolve_language(lang) + if lang not in _MESSAGES_CACHE: + _MESSAGES_CACHE[lang] = _load_messages(lang) + return _MESSAGES_CACHE[lang] + + +def _resolve(key: str, data: dict[str, Any]) -> Any: + """Resolve a dot-separated key in a nested dictionary.""" + current: Any = data + for part in key.split("."): + if not isinstance(current, dict) or part not in current: + return None + current = current[part] + return current + + +def resolve_language(lang_source: str | None = None) -> str: + """Resolve the effective language. + + Priority: + 1. Explicit ``lang_source`` argument if it is a supported filename. + 2. ``settings.language`` if it is supported. + 3. ``"ja"`` as a safe default. + + Supported languages are determined by the JSON files present in + ``i18n/messages/``. + """ + supported = {p.stem for p in _MESSAGES_DIR.glob("*.json")} + candidates = [lang_source, settings.language] + for candidate in candidates: + if candidate and candidate in supported: + return candidate + return "ja" + + +def t(key: str, lang: str | None = None, fallback: str | None = None) -> str: + """Return a localized message string. + + Args: + key: Dot-separated path, e.g. ``"chat.input_placeholder"``. + lang: Optional language override. Uses ``settings.language`` when omitted. + fallback: Value returned when the key is missing. Defaults to the key itself. + + Returns: + The localized string, or the fallback value if missing. + """ + value = _resolve(key, get_messages(resolve_language(lang))) + if isinstance(value, str): + return value + return fallback if fallback is not None else key |
