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 | |
| parent | b7943a7586fbd0bb0e19aad7732c11a62f72d7c1 (diff) | |
マルチ言語対応。MCP設定画面の言語切り替え表示にバグあり
Diffstat (limited to 'src/ollama_web/i18n')
| -rw-r--r-- | src/ollama_web/i18n/__init__.py | 84 | ||||
| -rw-r--r-- | src/ollama_web/i18n/messages/en.json | 100 | ||||
| -rw-r--r-- | src/ollama_web/i18n/messages/ja.json | 100 |
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設定の保存に失敗しました" + } +} |
