summaryrefslogtreecommitdiff
path: root/src/ollama_web/i18n
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-06-27 14:49:12 +0900
committertanaka takashi <isazaka@gmail.com>2026-06-27 14:49:12 +0900
commitf93822cebf6258c3a31e03745183aa2c8bb56f4d (patch)
tree8edfba7593d7db039b117fc4d742fbde78b72da6 /src/ollama_web/i18n
parentb7943a7586fbd0bb0e19aad7732c11a62f72d7c1 (diff)
マルチ言語対応。MCP設定画面の言語切り替え表示にバグあり
Diffstat (limited to 'src/ollama_web/i18n')
-rw-r--r--src/ollama_web/i18n/__init__.py84
-rw-r--r--src/ollama_web/i18n/messages/en.json100
-rw-r--r--src/ollama_web/i18n/messages/ja.json100
3 files changed, 284 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
diff --git a/src/ollama_web/i18n/messages/en.json b/src/ollama_web/i18n/messages/en.json
new file mode 100644
index 0000000..2195c6e
--- /dev/null
+++ b/src/ollama_web/i18n/messages/en.json
@@ -0,0 +1,100 @@
+{
+ "common": {
+ "send": "Send",
+ "stop": "Stop",
+ "clear": "Clear",
+ "save": "Save",
+ "delete": "Delete"
+ },
+ "chat": {
+ "sessions": "Sessions",
+ "new_session": "New session",
+ "session_list": "Session list",
+ "mcp_settings": "MCP Settings",
+ "model_label": "Model:",
+ "attach_file": "Attach file",
+ "input_placeholder": "Type a message… (Enter to send / Shift+Enter for newline)",
+ "no_sessions": "No sessions",
+ "delete_session": "Delete session",
+ "create_failed": "Failed to create session",
+ "delete_confirm": "Delete this session?",
+ "delete_failed": "Failed to delete session",
+ "upload_failed": "Failed to upload files",
+ "attachment_label": "Attached: {names}",
+ "attachment_files": "Attached files: {names}",
+ "stopped": "Generation stopped",
+ "error_prefix": "Error: {message}",
+ "generating": "ollama is generating a response…",
+ "processing": "Processing…",
+ "no_response": "(No response)",
+ "clear_failed": "Failed to clear",
+ "clear_messages_failed": "Failed to clear messages"
+ },
+ "mcp": {
+ "server_settings": "MCP Server Settings",
+ "add_server": "+ Add server",
+ "server_name": "Server name",
+ "required": "(required)",
+ "optional": "(optional)",
+ "command": "Command",
+ "args": "Arguments",
+ "env": "Environment variables",
+ "cwd": "Working directory",
+ "url": "URL",
+ "headers": "Headers",
+ "timeout": "Timeout",
+ "sse": "SSE",
+ "stdio": "stdio",
+ "http_stream": "HTTP Stream",
+ "examples": {
+ "command": "e.g. npx",
+ "args": "e.g. -y @modelcontextprotocol/server-filesystem /tmp",
+ "env": "e.g. API_KEY=sk-xxx",
+ "cwd": "e.g. C:\\Users\\Home",
+ "url": "e.g. http://127.0.0.1:9000/mcp",
+ "headers": "e.g. Authorization=Bearer xxx",
+ "timeout": "e.g. 30"
+ }
+ },
+ "login": {
+ "title": "Login",
+ "pin_invalid": "Invalid PIN"
+ },
+ "roles": {
+ "system": "System",
+ "user": "You",
+ "assistant": "Assistant"
+ },
+ "status": {
+ "sending": "Sending…",
+ "fetching_response": "Fetching response…",
+ "done": "Done",
+ "connecting": "Connecting…",
+ "disconnected": "Disconnected",
+ "tool_limit_reached": "Tool call limit reached. Summarizing findings…",
+ "thinking": "Thinking process",
+ "executing_tool": "Executing {label}…",
+ "running": "Running…"
+ },
+ "errors": {
+ "generic": "An error occurred",
+ "vision_model_required": "Select a vision-capable model when sending messages containing images."
+ },
+ "session": {
+ "default_title": "New session"
+ },
+ "html": {
+ "lang": "en"
+ },
+ "tools": {
+ "web_search": "Web search",
+ "scrape_url": "Scraping",
+ "search_and_fetch": "File search and fetch",
+ "pdf_to_text": "PDF text extraction"
+ },
+ "mcp_messages": {
+ "save_failed": "Failed to save: {error}",
+ "saved": "MCP settings saved",
+ "save_error": "Failed to save MCP settings"
+ }
+}
diff --git a/src/ollama_web/i18n/messages/ja.json b/src/ollama_web/i18n/messages/ja.json
new file mode 100644
index 0000000..9d42f85
--- /dev/null
+++ b/src/ollama_web/i18n/messages/ja.json
@@ -0,0 +1,100 @@
+{
+ "common": {
+ "send": "送信",
+ "stop": "停止",
+ "clear": "クリア",
+ "save": "保存",
+ "delete": "削除"
+ },
+ "chat": {
+ "sessions": "セッション",
+ "new_session": "新規セッション",
+ "session_list": "セッション一覧",
+ "mcp_settings": "MCP設定",
+ "model_label": "モデル:",
+ "attach_file": "ファイルを添付",
+ "input_placeholder": "メッセージを入力… (Enter で送信 / Shift+Enter で改行)",
+ "no_sessions": "セッションがありません",
+ "delete_session": "セッションを削除",
+ "create_failed": "セッションの作成に失敗しました",
+ "delete_confirm": "このセッションを削除しますか?",
+ "delete_failed": "セッションの削除に失敗しました",
+ "upload_failed": "ファイルのアップロードに失敗しました",
+ "attachment_label": "添付: {names}",
+ "attachment_files": "添付ファイル: {names}",
+ "stopped": "生成を停止しました",
+ "error_prefix": "エラー: {message}",
+ "generating": "ollama が回答を生成中…",
+ "processing": "処理中…",
+ "no_response": "(応答なし)",
+ "clear_failed": "クリアに失敗しました",
+ "clear_messages_failed": "メッセージのクリアに失敗しました"
+ },
+ "mcp": {
+ "server_settings": "MCPサーバー設定",
+ "add_server": "+ サーバー追加",
+ "server_name": "サーバー名",
+ "required": "(必須)",
+ "optional": "(任意)",
+ "command": "コマンド",
+ "args": "引数",
+ "env": "環境変数",
+ "cwd": "作業ディレクトリ",
+ "url": "URL",
+ "headers": "ヘッダー",
+ "timeout": "タイムアウト",
+ "sse": "SSE",
+ "stdio": "stdio",
+ "http_stream": "HTTP Stream",
+ "examples": {
+ "command": "例: npx",
+ "args": "例: -y @modelcontextprotocol/server-filesystem /tmp",
+ "env": "例: API_KEY=sk-xxx",
+ "cwd": "例: C:\\Users\\Home",
+ "url": "例: http://127.0.0.1:9000/mcp",
+ "headers": "例: Authorization=Bearer xxx",
+ "timeout": "例: 30"
+ }
+ },
+ "login": {
+ "title": "ログイン",
+ "pin_invalid": "PINが違います"
+ },
+ "roles": {
+ "system": "システム",
+ "user": "あなた",
+ "assistant": "アシスタント"
+ },
+ "status": {
+ "sending": "送信中...",
+ "fetching_response": "応答を取得中...",
+ "done": "完了",
+ "connecting": "接続中...",
+ "disconnected": "切断しました",
+ "tool_limit_reached": "ツール呼び出し上限に達しました。これまでの調査結果を整理中…",
+ "thinking": "思考プロセス",
+ "executing_tool": "{label} を実行中…",
+ "running": "実行中…"
+ },
+ "errors": {
+ "generic": "エラーが発生しました",
+ "vision_model_required": "画像を含むメッセージを送信する場合、vision 対応モデルを選択してください。"
+ },
+ "session": {
+ "default_title": "新しいセッション"
+ },
+ "html": {
+ "lang": "ja"
+ },
+ "tools": {
+ "web_search": "Web検索",
+ "scrape_url": "スクレイピング",
+ "search_and_fetch": "ファイル検索・取得",
+ "pdf_to_text": "PDFテキスト変換"
+ },
+ "mcp_messages": {
+ "save_failed": "保存に失敗しました: {error}",
+ "saved": "MCP設定を保存しました",
+ "save_error": "MCP設定の保存に失敗しました"
+ }
+}