From d8cea1968acfab5fc264c2337323a3597940e075 Mon Sep 17 00:00:00 2001 From: little_penguin66 Date: Wed, 25 Dec 2024 20:56:43 +0800 Subject: [PATCH 1/2] Add Japanese localization for i18n --- src/main/utils/locales.ts | 2 + src/renderer/src/context/AntdProvider.tsx | 3 + src/renderer/src/i18n/index.ts | 2 + src/renderer/src/i18n/locales/ja-jp.json | 571 ++++++++++++++++++ .../pages/agents/agentGroupTranslations.ts | 103 ++-- .../src/pages/settings/GeneralSettings.tsx | 1 + src/renderer/src/types/index.ts | 2 +- 7 files changed, 649 insertions(+), 35 deletions(-) create mode 100644 src/renderer/src/i18n/locales/ja-jp.json diff --git a/src/main/utils/locales.ts b/src/main/utils/locales.ts index 2ab840aa1..f673e3836 100644 --- a/src/main/utils/locales.ts +++ b/src/main/utils/locales.ts @@ -1,4 +1,5 @@ import EnUs from '../../renderer/src/i18n/locales/en-us.json' +import JaJP from '../../renderer/src/i18n/locales/ja-jp.json' import RuRu from '../../renderer/src/i18n/locales/ru-ru.json' import ZhCn from '../../renderer/src/i18n/locales/zh-cn.json' import ZhTw from '../../renderer/src/i18n/locales/zh-tw.json' @@ -7,6 +8,7 @@ const locales = { 'en-US': EnUs, 'zh-CN': ZhCn, 'zh-TW': ZhTw, + 'ja-JP': JaJP, 'ru-RU': RuRu } diff --git a/src/renderer/src/context/AntdProvider.tsx b/src/renderer/src/context/AntdProvider.tsx index 20cc5b242..3dd8fcb26 100644 --- a/src/renderer/src/context/AntdProvider.tsx +++ b/src/renderer/src/context/AntdProvider.tsx @@ -2,6 +2,7 @@ import { useSettings } from '@renderer/hooks/useSettings' import { LanguageVarious } from '@renderer/types' import { ConfigProvider, theme } from 'antd' import enUS from 'antd/locale/en_US' +import jaJP from 'antd/locale/ja_JP' import ruRU from 'antd/locale/ru_RU' import zhCN from 'antd/locale/zh_CN' import zhTW from 'antd/locale/zh_TW' @@ -59,6 +60,8 @@ function getAntdLocale(language: LanguageVarious) { return enUS case 'ru-RU': return ruRU + case 'ja-JP': + return jaJP default: return zhCN diff --git a/src/renderer/src/i18n/index.ts b/src/renderer/src/i18n/index.ts index 13b49e9a4..7f2eb32c2 100644 --- a/src/renderer/src/i18n/index.ts +++ b/src/renderer/src/i18n/index.ts @@ -2,6 +2,7 @@ import i18n from 'i18next' import { initReactI18next } from 'react-i18next' import enUS from './locales/en-us.json' +import jaJP from './locales/ja-jp.json' import ruRU from './locales/ru-ru.json' import zhCN from './locales/zh-cn.json' import zhTW from './locales/zh-tw.json' @@ -10,6 +11,7 @@ const resources = { 'en-US': enUS, 'zh-CN': zhCN, 'zh-TW': zhTW, + 'ja-JP': jaJP, 'ru-RU': ruRU } diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json new file mode 100644 index 000000000..7209dbe94 --- /dev/null +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -0,0 +1,571 @@ +{ + "translation": { + "agents": { + "add.button": "アシスタントに追加", + "add.name": "名前", + "add.name.placeholder": "名前を入力", + "add.prompt": "プロンプト", + "add.prompt.placeholder": "プロンプトを入力", + "add.title": "エージェントを作成", + "delete.popup.content": "このエージェントを削除してもよろしいですか?", + "edit.message.add.title": "追加", + "edit.message.assistant.placeholder": "アシスタントのメッセージを入力", + "edit.message.assistant.title": "アシスタント", + "edit.message.empty.content": "会話の入力内容が空です", + "edit.message.group.title": "メッセージグループ", + "edit.message.title": "プリセットメッセージ", + "edit.message.user.placeholder": "ユーザーメッセージを入力", + "edit.message.user.title": "ユーザー", + "edit.model.select.title": "モデルを選択", + "edit.settings.hide_preset_messages": "プリセットメッセージを非表示", + "edit.title": "エージェントを編集", + "manage.title": "エージェントを管理", + "my_agents": "マイエージェント", + "search.no_results": "結果が見つかりません", + "sorting.title": "並び替え", + "tag.agent": "エージェント", + "tag.default": "デフォルト", + "tag.new": "新規", + "tag.system": "システム", + "title": "エージェント" + }, + "assistants": { + "abbr": "アシスタント", + "clear.content": "トピックをクリアすると、アシスタント内のすべてのトピックとファイルが削除されます。続行しますか?", + "clear.title": "トピックをクリア", + "copy.title": "アシスタントをコピー", + "delete.content": "アシスタントを削除すると、そのアシスタントのすべてのトピックとファイルが削除されます。削除しますか?", + "delete.title": "アシスタントを削除", + "edit.title": "アシスタントを編集", + "save.success": "保存に成功しました", + "save.title": "エージェントに保存", + "search": "アシスタントを検索...", + "settings.auto_reset_model": "自動リセットモデル", + "settings.auto_reset_model.tip": "新しいトピックを作成する際にモデルを自動的にリセットします", + "settings.default_model": "デフォルトモデル", + "settings.model": "モデル設定", + "settings.preset_messages": "プリセットメッセージ", + "settings.prompt": "プロンプト設定", + "title": "アシスタント" + }, + "button": { + "add": "追加", + "added": "追加済み", + "collapse": "折りたたむ", + "manage": "管理", + "select_model": "モデルを選択", + "show.all": "すべて表示" + }, + "chat": { + "add.assistant.title": "アシスタントを追加", + "artifacts.button.download": "ダウンロード", + "artifacts.button.preview": "プレビュー", + "assistant.search.placeholder": "検索", + "default.description": "こんにちは、私はデフォルトのアシスタントです。すぐにチャットを始められます。", + "default.name": "⭐️ デフォルトアシスタント", + "default.topic.name": "デフォルトトピック", + "input.clear": "クリア", + "input.clear.content": "現在のトピックのすべてのメッセージをクリアしますか?", + "input.clear.title": "すべてのメッセージをクリアしますか?", + "input.collapse": "折りたたむ", + "input.context_count.tip": "コンテキスト数", + "input.estimated_tokens.tip": "推定トークン数", + "input.expand": "展開", + "input.new.context": "コンテキストをクリア", + "input.new_topic": "新しいトピック {{Command}}", + "input.pause": "一時停止", + "input.placeholder": "ここにメッセージを入力...", + "input.send": "送信", + "input.settings": "設定", + "input.topics": " トピック ", + "input.translate": "英語に翻訳", + "input.upload": "画像またはドキュメントをアップロード", + "input.web_search": "ウェブ検索を有効にする", + "input.knowledge_base": "ナレッジベース", + "message.new.branch": "新しいブランチ", + "message.new.branch.created": "新しいブランチが作成されました", + "message.regenerate.model": "モデルを切り替え", + "message.new.context": "新しいコンテキスト", + "save": "保存", + "settings.code_collapsible": "コードブロックを折りたたむ", + "settings.context_count": "コンテキスト", + "settings.context_count.tip": "コンテキストに保持する以前のメッセージの数", + "settings.max": "最大", + "settings.max_tokens": "最大トークン制限を有効にする", + "settings.max_tokens.tip": "モデルが生成できる最大トークン数。通常のチャットでは500-800、短いテキスト生成では800-2000、コード生成では2000-3600、長いテキスト生成では4000以上を推奨", + "settings.reset": "リセット", + "settings.set_as_default": "デフォルトのアシスタントに適用", + "settings.show_line_numbers": "コードに行番号を表示", + "settings.temperature": "温度", + "settings.temperature.tip": "低い値はモデルをより創造的で予測不可能にし、高い値はより決定論的で正確にします", + "settings.top_p": "Top-P", + "settings.top_p.tip": "デフォルト値は1で、値が小さいほど回答の多様性が減り、理解しやすくなります。値が大きいほど、AIの語彙範囲が広がり、多様性が増します", + "suggestions.title": "提案された質問", + "topics.auto_rename": "自動リネーム", + "topics.clear.title": "メッセージをクリア", + "topics.edit.placeholder": "新しい名前を入力", + "topics.edit.title": "名前を編集", + "topics.export.image": "画像としてエクスポート", + "topics.export.md": "Markdownとしてエクスポート", + "topics.export.title": "エクスポート", + "topics.export.word": "Wordとしてエクスポート", + "topics.list": "トピックリスト", + "topics.move_to": "移動先", + "topics.title": "トピック", + "translate": "翻訳" + }, + "common": { + "and": "と", + "assistant": "アシスタント", + "avatar": "アバター", + "back": "戻る", + "cancel": "キャンセル", + "chat": "チャット", + "close": "閉じる", + "copy": "コピー", + "cut": "切り取り", + "default": "デフォルト", + "delete": "削除", + "description": "説明", + "docs": "ドキュメント", + "download": "ダウンロード", + "duplicate": "複製", + "edit": "編集", + "footnotes": "脚注", + "language": "言語", + "model": "モデル", + "models": "モデル", + "name": "名前", + "paste": "貼り付け", + "prompt": "プロンプト", + "provider": "プロバイダー", + "regenerate": "再生成", + "rename": "名前を変更", + "reset": "リセット", + "save": "保存", + "search": "検索", + "select": "選択", + "topics": "トピック", + "warning": "警告", + "you": "あなた", + "clear": "クリア", + "add": "追加" + }, + "error": { + "backup.file_format": "バックアップファイルの形式エラー", + "chat.response": "エラーが発生しました。APIキーが設定されていない場合は、設定 > プロバイダーでキーを設定してください", + "no_api_key": "APIキーが設定されていません", + "provider_disabled": "モデルプロバイダーが有効になっていません", + "render": { + "title": "レンダリングエラー", + "description": "数式のレンダリングに失敗しました。数式の形式が正しいか確認してください" + } + }, + "export": { + "assistant": "アシスタント", + "attached_files": "添付ファイル", + "conversation_details": "会話の詳細", + "conversation_history": "会話履歴", + "created": "作成日", + "last_updated": "最終更新日", + "messages": "メッセージ", + "user": "ユーザー" + }, + "files": { + "actions": "操作", + "all": "すべてのファイル", + "count": "数", + "created_at": "作成日", + "document": "ドキュメント", + "file": "ファイル", + "image": "画像", + "name": "名前", + "open": "開く", + "size": "サイズ", + "text": "テキスト", + "title": "ファイル" + }, + "history": { + "continue_chat": "チャットを続ける", + "locate.message": "メッセージを探す", + "search.messages": "すべてのメッセージを検索", + "search.placeholder": "トピックまたはメッセージを検索...", + "search.topics.empty": "トピックが見つかりませんでした。Enterキーを押してすべてのメッセージを検索", + "title": "トピック検索" + }, + "languages": { + "arabic": "アラビア語", + "chinese": "中国語", + "chinese-traditional": "繁体字中国語", + "english": "英語", + "french": "フランス語", + "italian": "イタリア語", + "japanese": "日本語", + "korean": "韓国語", + "portuguese": "ポルトガル語", + "russian": "ロシア語", + "spanish": "スペイン語" + }, + "mermaid": { + "download": { + "png": "PNGをダウンロード", + "svg": "SVGをダウンロード" + }, + "tabs": { + "preview": "プレビュー", + "source": "ソース" + }, + "title": "Mermaid図" + }, + "message": { + "api.connection.failed": "接続に失敗しました", + "api.connection.success": "接続に成功しました", + "assistant.added.content": "アシスタントが追加されました", + "backup.failed": "バックアップに失敗しました", + "backup.success": "バックアップに成功しました", + "backup.start.success": "バックアップを開始しました", + "chat.completion.paused": "チャットの完了が一時停止されました", + "copied": "コピーしました!", + "error.enter.api.host": "APIホストを入力してください", + "error.enter.api.key": "APIキーを入力してください", + "error.enter.model": "モデルを選択してください", + "error.invalid.proxy.url": "無効なプロキシURL", + "error.invalid.webdav": "無効なWebDAV設定", + "message.code_style": "コードスタイル", + "message.delete.content": "このメッセージを削除してもよろしいですか?", + "message.delete.title": "メッセージを削除", + "message.style": "メッセージスタイル", + "message.style.bubble": "バブル", + "message.style.plain": "プレーン", + "reset.confirm.content": "すべてのデータをリセットしてもよろしいですか?", + "reset.double.confirm.content": "すべてのデータが失われます。続行しますか?", + "reset.double.confirm.title": "データが失われます!!!", + "restore.success": "復元に成功しました", + "save.success.title": "保存に成功しました", + "switch.disabled": "アシスタントが生成中は切り替えが無効です", + "topic.added": "新しいトピックが追加されました", + "upgrade.success.button": "再起動", + "upgrade.success.content": "アップグレードを完了するためにアプリケーションを再起動してください", + "upgrade.success.title": "アップグレードに成功しました", + "regenerate.confirm": "再生成すると現在のメッセージが置き換えられます", + "copy.success": "コピーしました!" + }, + "minapp": { + "title": "ミニアプリ" + }, + "ollama": { + "keep_alive_time.description": "モデルがメモリに保持される時間(デフォルト:5分)", + "keep_alive_time.placeholder": "分", + "keep_alive_time.title": "保持時間", + "title": "Ollama" + }, + "paintings": { + "button.delete.image": "画像を削除", + "button.delete.image.confirm": "この画像を削除してもよろしいですか?", + "button.new.image": "新しい画像", + "guidance_scale": "ガイダンススケール", + "guidance_scale_tip": "分類器なしのガイダンス。モデルが関連する画像を探す際にプロンプトにどれだけ従うかを制御します", + "image.size": "画像サイズ", + "inference_steps": "推論ステップ数", + "inference_steps_tip": "実行する推論ステップ数。ステップ数が多いほど品質が向上しますが、時間がかかります", + "negative_prompt": "ネガティブプロンプト", + "negative_prompt_tip": "画像に含めたくない内容を説明します", + "number_images": "生成数", + "number_images_tip": "生成する画像の数(1-4)", + "prompt_placeholder": "作成したい画像を説明します。例:夕日の湖畔、遠くに山々", + "regenerate.confirm": "これにより、既存の生成画像が置き換えられます。続行しますか?", + "seed": "シード", + "seed_tip": "同じシードとプロンプトで似た画像を生成できます", + "title": "画像" + }, + "provider": { + "aihubmix": "AiHubMix", + "anthropic": "Anthropic", + "azure-openai": "Azure OpenAI", + "baichuan": "百川", + "dashscope": "Alibaba Cloud", + "deepseek": "DeepSeek", + "doubao": "豆包", + "fireworks": "Fireworks", + "gemini": "Gemini", + "github": "GitHub Models", + "graphrag-kylin-mountain": "GraphRAG", + "grok": "Grok", + "groq": "Groq", + "hunyuan": "腾讯混元", + "hyperbolic": "Hyperbolic", + "jina": "Jina", + "minimax": "MiniMax", + "mistral": "Mistral", + "moonshot": "月の暗面", + "nvidia": "NVIDIA", + "ocoolai": "ocoolAI", + "ollama": "Ollama", + "openai": "OpenAI", + "openrouter": "OpenRouter", + "silicon": "SiliconFlow", + "stepfun": "StepFun", + "together": "Together", + "yi": "零一万物", + "zhinao": "360智脳", + "zhipu": "智譜AI" + }, + "settings": { + "about": "について", + "about.checkUpdate": "更新を確認", + "about.checkUpdate.available": "今すぐ更新", + "about.checkingUpdate": "更新を確認中...", + "about.contact.button": "メール", + "about.contact.title": "連絡先", + "about.description": "クリエイターのための強力なAIアシスタント", + "about.downloading": "ダウンロード中...", + "about.feedback.button": "フィードバック", + "about.feedback.title": "フィードバック", + "about.license.button": "ライセンス", + "about.license.title": "ライセンス", + "about.releases.button": "リリース", + "about.releases.title": "リリースノート", + "about.title": "について", + "about.updateAvailable": "新しいバージョン {{version}} が見つかりました", + "about.updateError": "更新エラー", + "about.updateNotAvailable": "最新バージョンを使用しています", + "about.website.button": "ウェブサイト", + "about.website.title": "公式ウェブサイト", + "about.social.title": "ソーシャルアカウント", + "advanced.auto_switch_to_topics": "トピックに自動的に切り替える", + "advanced.title": "詳細設定", + "assistant": "デフォルトアシスタント", + "assistant.model_params": "モデルパラメータ", + "assistant.title": "デフォルトアシスタント", + "data": { + "app_data": "アプリデータ", + "app_logs": "アプリログ", + "clear_cache": { + "button": "キャッシュをクリア", + "confirm": "キャッシュをクリアすると、アプリのキャッシュデータ(ミニアプリデータを含む)が削除されます。この操作は元に戻せません。続行しますか?", + "error": "キャッシュのクリアに失敗しました", + "success": "キャッシュがクリアされました", + "title": "キャッシュをクリア" + }, + "data.title": "データディレクトリ", + "title": "データ設定", + "webdav.backup.button": "WebDAVにバックアップ", + "webdav.host": "WebDAVホスト", + "webdav.host.placeholder": "http://localhost:8080", + "webdav.password": "WebDAVパスワード", + "webdav.path": "WebDAVパス", + "webdav.path.placeholder": "/backup", + "webdav.autoSync": "自動同期", + "webdav.minutes": "分", + "webdav.restore.button": "WebDAVから復元", + "webdav.title": "WebDAV", + "webdav.user": "WebDAVユーザー" + }, + "display.title": "表示設定", + "font_size.title": "メッセージのフォントサイズ", + "general": "一般設定", + "general.backup.button": "バックアップ", + "general.backup.title": "データのバックアップと復元", + "general.manually_check_update.title": "更新チェックを無効にする", + "general.reset.button": "リセット", + "general.reset.title": "データをリセット", + "general.restore.button": "復元", + "general.title": "一般設定", + "general.user_name": "ユーザー名", + "general.user_name.placeholder": "ユーザー名を入力", + "general.view_webdav_settings": "WebDAV設定を表示", + "general.display.title": "表示設定", + "display.sidebar.minapp.icon": "ミニアプリのアイコンを表示", + "display.sidebar.files.icon": "ファイルのアイコンを表示", + "display.sidebar.title": "サイドバー設定", + "display.topic.title": "トピック設定", + "input.auto_translate_with_space": "スペースを3回押して翻訳", + "messages.divider": "メッセージ間に区切り線を表示", + "messages.input.paste_long_text_as_file": "長いテキストをファイルとして貼り付け", + "messages.input.send_shortcuts": "送信ショートカット", + "messages.input.show_estimated_tokens": "推定トークン数を表示", + "messages.metrics": "最初のトークンまでの時間 {{time_first_token_millsec}}ms | トークン速度 {{token_speed}} tok/sec", + "messages.input.title": "入力設定", + "messages.markdown_rendering_input_message": "Markdownで入力メッセージをレンダリング", + "messages.math_engine": "数式エンジン", + "messages.model.title": "モデル設定", + "messages.title": "メッセージ設定", + "messages.use_serif_font": "セリフフォントを使用", + "messages.input.paste_long_text_threshold": "長いテキストの長さ", + "model": "デフォルトモデル", + "models.add.add_model": "モデルを追加", + "models.add.group_name": "グループ名", + "models.add.group_name.placeholder": "例:ChatGPT", + "models.add.group_name.tooltip": "例:ChatGPT", + "models.add.model_id": "モデルID", + "models.add.model_id.placeholder": "必須 例:gpt-3.5-turbo", + "models.add.model_id.tooltip": "例:gpt-3.5-turbo", + "models.add.model_name": "モデル名", + "models.add.model_name.placeholder": "例:GPT-3.5", + "models.default_assistant_model": "デフォルトアシスタントモデル", + "models.default_assistant_model_description": "新しいアシスタントを作成する際に使用されるモデル。アシスタントがモデルを設定していない場合、このモデルが使用されます", + "models.empty": "モデルが見つかりません", + "models.topic_naming_model": "トピック命名モデル", + "models.topic_naming_model_description": "新しいトピックを自動的に命名する際に使用されるモデル", + "models.translate_model": "翻訳モデル", + "models.translate_model_description": "翻訳サービスに使用されるモデル", + "models.translate_model_prompt_message": "翻訳モデルのプロンプトを入力してください", + "models.translate_model_prompt_title": "翻訳モデルのプロンプト", + "models.topic_naming_model_setting_title": "トピック命名モデルの設定", + "models.enable_topic_naming": "トピックの自動命名", + "provider": { + "add.name": "プロバイダー名", + "add.name.placeholder": "例:OpenAI", + "add.title": "プロバイダーを追加", + "add.type": "プロバイダータイプ", + "api.url.preview": "プレビュー: {{url}}", + "api.url.reset": "リセット", + "api.url.tip": "/で終わる場合、v1を無視します。#で終わる場合、入力されたアドレスを強制的に使用します", + "api_host": "APIホスト", + "api_key": "APIキー", + "api_key.tip": "複数のキーはカンマで区切ります", + "api_version": "APIバージョン", + "check": "チェック", + "check_all_keys": "すべてのキーをチェック", + "check_multiple_keys": "複数のAPIキーをチェック", + "delete.content": "このプロバイダーを削除してもよろしいですか?", + "delete.title": "プロバイダーを削除", + "docs_check": "チェック", + "docs_more_details": "詳細を確認", + "get_api_key": "APIキーを取得", + "no_models": "API接続をチェックする前に、モデルを追加してください", + "not_checked": "未チェック", + "remove_duplicate_keys": "重複キーを削除", + "remove_invalid_keys": "無効なキーを削除", + "search_placeholder": "モデルIDまたは名前を検索", + "title": "モデルプロバイダー" + }, + "proxy": { + "mode": { + "custom": "カスタムプロキシ", + "none": "プロキシを使用しない", + "system": "システムプロキシ", + "title": "プロキシモード" + }, + "title": "プロキシ設定" + }, + "proxy.title": "プロキシアドレス", + "shortcuts": { + "action": "操作", + "key": "キー", + "new_topic": "新しいトピック", + "title": "ショートカット", + "zoom_in": "ズームイン", + "zoom_out": "ズームアウト", + "zoom_reset": "ズームをリセット", + "show_app": "アプリを表示", + "reset_defaults": "デフォルトのショートカットをリセット", + "reset_defaults_confirm": "すべてのショートカットをリセットしてもよろしいですか?", + "press_shortcut": "ショートカットを押す", + "alt_warning": "MacではOption + 文字をショートカットとして使用できません", + "reset_to_default": "デフォルトにリセット", + "clear_shortcut": "ショートカットをクリア", + "toggle_show_assistants": "アシスタントの表示を切り替え", + "toggle_show_topics": "トピックの表示を切り替え", + "copy_last_message": "最後のメッセージをコピー" + }, + "theme.auto": "自動", + "theme.dark": "ダークテーマ", + "theme.light": "ライトテーマ", + "theme.title": "テーマ", + "theme.window.style.opaque": "不透明ウィンドウ", + "theme.window.style.title": "ウィンドウスタイル", + "theme.window.style.transparent": "透明ウィンドウ", + "title": "設定", + "topic.position": "トピックの位置", + "topic.position.left": "左", + "topic.position.right": "右", + "topic.show.time": "トピックの時間を表示", + "tray.title": "システムトレイアイコンを有効にする" + }, + "translate": { + "any.language": "任意の言語", + "button.translate": "翻訳", + "confirm": { + "content": "翻訳すると元のテキストが上書きされます。続行しますか?", + "title": "翻訳確認" + }, + "error.not_configured": "翻訳モデルが設定されていません", + "error.failed": "翻訳に失敗しました", + "input.placeholder": "翻訳するテキストを入力", + "output.placeholder": "翻訳", + "processing": "翻訳中...", + "title": "翻訳", + "close": "閉じる" + }, + "tray": { + "quit": "終了", + "show_window": "ウィンドウを表示" + }, + "words": { + "knowledgeGraph": "ナレッジグラフ", + "visualization": "可視化", + "show_window": "ウィンドウを表示", + "quit": "終了" + }, + "knowledge_base": { + "title": "ナレッジベース", + "search": "ナレッジベースを検索", + "empty": "ナレッジベースが見つかりません", + "drag_file": "ファイルをここにドラッグ", + "file_hint": "{{file_types}} 形式をサポート", + "add": { + "title": "ナレッジベースを追加" + }, + "notes": "ノート", + "notes_placeholder": "このナレッジベースの追加情報やコンテキストを入力...", + "delete": "削除", + "rename": "名前を変更", + "urls": "URL", + "add_url": "URLを追加", + "url_placeholder": "URLを入力", + "invalid_url": "無効なURL", + "add_file": "ファイルを追加", + "status": "状態", + "index_all": "すべてをインデックス", + "index_started": "インデックスを開始", + "cancel_index": "インデックスをキャンセル", + "index_cancelled": "インデックスがキャンセルされました", + "status_new": "追加済み", + "status_pending": "保留中", + "status_processing": "処理中", + "status_completed": "完了", + "status_failed": "失敗", + "url_added": "URLが追加されました", + "search_placeholder": "検索するテキストを入力", + "add_note": "ノートを追加", + "no_bases": "ナレッジベースがありません", + "clear_selection": "選択をクリア", + "delete_confirm": "このナレッジベースを削除してもよろしいですか?", + "sitemaps": "サイトマップ", + "add_sitemap": "サイトマップを追加", + "sitemap_placeholder": "サイトマップURLを入力", + "directories": "ディレクトリ", + "add_directory": "ディレクトリを追加", + "directory_placeholder": "ディレクトリパスを入力" + }, + "models": { + "pinned": "固定済み", + "search": "モデルを検索...", + "stream_output": "ストリーム出力", + "type": { + "select": "モデルタイプを選択", + "text": "テキスト", + "vision": "画像", + "embedding": "埋め込み" + }, + "all": "すべて", + "vision": "画像モデル", + "websearch": "ウェブ検索モデル", + "free": "無料モデル", + "embedding": "埋め込みモデル", + "embedding_model": "埋め込みモデル", + "embedding_model_tooltip": "設定->モデルサービス->管理で追加" + } + } +} diff --git a/src/renderer/src/pages/agents/agentGroupTranslations.ts b/src/renderer/src/pages/agents/agentGroupTranslations.ts index 449f4f707..b5330a3f4 100644 --- a/src/renderer/src/pages/agents/agentGroupTranslations.ts +++ b/src/renderer/src/pages/agents/agentGroupTranslations.ts @@ -4,6 +4,7 @@ export type GroupTranslations = { 'zh-CN': string 'zh-TW': string 'ru-RU': string + 'ja-JP': string } } @@ -12,204 +13,238 @@ export const groupTranslations: GroupTranslations = { 'en-US': 'My Agents', 'zh-CN': '我的', 'zh-TW': '我的', - 'ru-RU': 'Мои агенты' + 'ru-RU': 'Мои агенты', + 'ja-JP': '私のエージェント' }, 职业: { 'en-US': 'Career', 'zh-CN': '职业', 'zh-TW': '職業', - 'ru-RU': 'Карьера' + 'ru-RU': 'Карьера', + 'ja-JP': 'キャリア' }, 商业: { 'en-US': 'Business', 'zh-CN': '商业', 'zh-TW': '商業', - 'ru-RU': 'Бизнес' + 'ru-RU': 'Бизнес', + 'ja-JP': 'ビジネス' }, 工具: { 'en-US': 'Tools', 'zh-CN': '工具', 'zh-TW': '工具', - 'ru-RU': 'Инструменты' + 'ru-RU': 'Инструменты', + 'ja-JP': 'ツール' }, 语言: { 'en-US': 'Language', 'zh-CN': '语言', 'zh-TW': '語言', - 'ru-RU': 'Язык' + 'ru-RU': 'Язык', + 'ja-JP': '言語' }, 办公: { 'en-US': 'Office', 'zh-CN': '办公', 'zh-TW': '辦公', - 'ru-RU': 'Офис' + 'ru-RU': 'Офис', + 'ja-JP': 'オフィス' }, 通用: { 'en-US': 'General', 'zh-CN': '通用', 'zh-TW': '通用', - 'ru-RU': 'Общее' + 'ru-RU': 'Общее', + 'ja-JP': '一般' }, 写作: { 'en-US': 'Writing', 'zh-CN': '写作', 'zh-TW': '寫作', - 'ru-RU': 'Письмо' + 'ru-RU': 'Письмо', + 'ja-JP': '書き込み' }, 精选: { 'en-US': 'Featured', 'zh-CN': '精选', 'zh-TW': '精選', - 'ru-RU': 'Избранное' + 'ru-RU': 'Избранное', + 'ja-JP': '特集' }, 编程: { 'en-US': 'Programming', 'zh-CN': '编程', 'zh-TW': '編程', - 'ru-RU': 'Программирование' + 'ru-RU': 'Программирование', + 'ja-JP': 'プログラミング' }, 情感: { 'en-US': 'Emotion', 'zh-CN': '情感', 'zh-TW': '情感', - 'ru-RU': 'Эмоции' + 'ru-RU': 'Эмоции', + 'ja-JP': '感情' }, 教育: { 'en-US': 'Education', 'zh-CN': '教育', 'zh-TW': '教育', - 'ru-RU': 'Образование' + 'ru-RU': 'Образование', + 'ja-JP': '教育' }, 创意: { 'en-US': 'Creative', 'zh-CN': '创意', 'zh-TW': '創意', - 'ru-RU': 'Креатив' + 'ru-RU': 'Креатив', + 'ja-JP': 'クリエイティブ' }, 学术: { 'en-US': 'Academic', 'zh-CN': '学术', 'zh-TW': '學術', - 'ru-RU': 'Академический' + 'ru-RU': 'Академический', + 'ja-JP': 'アカデミック' }, 设计: { 'en-US': 'Design', 'zh-CN': '设计', 'zh-TW': '設計', - 'ru-RU': 'Дизайн' + 'ru-RU': 'Дизайн', + 'ja-JP': 'デザイン' }, 艺术: { 'en-US': 'Art', 'zh-CN': '艺术', 'zh-TW': '藝術', - 'ru-RU': 'Искусство' + 'ru-RU': 'Искусство', + 'ja-JP': 'アート' }, 娱乐: { 'en-US': 'Entertainment', 'zh-CN': '娱乐', 'zh-TW': '娛樂', - 'ru-RU': 'Развлечения' + 'ru-RU': 'Развлечения', + 'ja-JP': 'エンターテイメント' }, 生活: { 'en-US': 'Life', 'zh-CN': '生活', 'zh-TW': '生活', - 'ru-RU': 'Жизнь' + 'ru-RU': 'Жизнь', + 'ja-JP': '生活' }, 医疗: { 'en-US': 'Medical', 'zh-CN': '医疗', 'zh-TW': '醫療', - 'ru-RU': 'Медицина' + 'ru-RU': 'Медицина', + 'ja-JP': '医療' }, 游戏: { 'en-US': 'Games', 'zh-CN': '游戏', 'zh-TW': '遊戲', - 'ru-RU': 'Игры' + 'ru-RU': 'Игры', + 'ja-JP': 'ゲーム' }, 翻译: { 'en-US': 'Translation', 'zh-CN': '翻译', 'zh-TW': '翻譯', - 'ru-RU': 'Перевод' + 'ru-RU': 'Перевод', + 'ja-JP': '翻訳' }, 音乐: { 'en-US': 'Music', 'zh-CN': '音乐', 'zh-TW': '音樂', - 'ru-RU': 'Музыка' + 'ru-RU': 'Музыка', + 'ja-JP': '音楽' }, 点评: { 'en-US': 'Review', 'zh-CN': '点评', 'zh-TW': '點評', - 'ru-RU': 'Обзор' + 'ru-RU': 'Обзор', + 'ja-JP': 'レビュー' }, 文案: { 'en-US': 'Copywriting', 'zh-CN': '文案', 'zh-TW': '文案', - 'ru-RU': 'Копирайтинг' + 'ru-RU': 'Копирайтинг', + 'ja-JP': 'コピーライティング' }, 百科: { 'en-US': 'Encyclopedia', 'zh-CN': '百科', 'zh-TW': '百科', - 'ru-RU': 'Энциклопедия' + 'ru-RU': 'Энциклопедия', + 'ja-JP': '百科事典' }, 健康: { 'en-US': 'Health', 'zh-CN': '健康', 'zh-TW': '健康', - 'ru-RU': 'Здоровье' + 'ru-RU': 'Здоровье', + 'ja-JP': '健康' }, 营销: { 'en-US': 'Marketing', 'zh-CN': '营销', 'zh-TW': '營銷', - 'ru-RU': 'Маркетинг' + 'ru-RU': 'Маркетинг', + 'ja-JP': 'マーケティング' }, 科学: { 'en-US': 'Science', 'zh-CN': '科学', 'zh-TW': '科學', - 'ru-RU': 'Наука' + 'ru-RU': 'Наука', + 'ja-JP': '科学' }, 分析: { 'en-US': 'Analysis', 'zh-CN': '分析', 'zh-TW': '分析', - 'ru-RU': 'Анализ' + 'ru-RU': 'Анализ', + 'ja-JP': '分析' }, 法律: { 'en-US': 'Legal', 'zh-CN': '法律', 'zh-TW': '法律', - 'ru-RU': 'Право' + 'ru-RU': 'Право', + 'ja-JP': '法律' }, 咨询: { 'en-US': 'Consulting', 'zh-CN': '咨询', 'zh-TW': '諮詢', - 'ru-RU': 'Консалтинг' + 'ru-RU': 'Консалтинг', + 'ja-JP': 'コンサルティング' }, 金融: { 'en-US': 'Finance', 'zh-CN': '金融', 'zh-TW': '金融', - 'ru-RU': 'Финансы' + 'ru-RU': 'Финансы', + 'ja-JP': '金融' }, 旅游: { 'en-US': 'Travel', 'zh-CN': '旅游', 'zh-TW': '旅遊', - 'ru-RU': 'Путешествия' + 'ru-RU': 'Путешествия', + 'ja-JP': '旅行' }, 管理: { 'en-US': 'Management', 'zh-CN': '管理', 'zh-TW': '管理', - 'ru-RU': 'Управление' + 'ru-RU': 'Управление', + 'ja-JP': '管理' } } diff --git a/src/renderer/src/pages/settings/GeneralSettings.tsx b/src/renderer/src/pages/settings/GeneralSettings.tsx index b0b37e79f..47466cdf4 100644 --- a/src/renderer/src/pages/settings/GeneralSettings.tsx +++ b/src/renderer/src/pages/settings/GeneralSettings.tsx @@ -63,6 +63,7 @@ const GeneralSettings: FC = () => { { value: 'zh-CN', label: '中文', flag: '🇨🇳' }, { value: 'zh-TW', label: '中文(繁体)', flag: '🇭🇰' }, { value: 'en-US', label: 'English', flag: '🇺🇸' }, + { value: 'ja-JP', label: '日本語', flag: '🇯🇵' }, { value: 'ru-RU', label: 'Russian', flag: '🇷🇺' } ] diff --git a/src/renderer/src/types/index.ts b/src/renderer/src/types/index.ts index afbbf4383..8d11925ed 100644 --- a/src/renderer/src/types/index.ts +++ b/src/renderer/src/types/index.ts @@ -154,7 +154,7 @@ export enum ThemeMode { dark = 'dark', auto = 'auto' } -export type LanguageVarious = 'zh-CN' | 'zh-TW' | 'en-US' | 'ru-RU' +export type LanguageVarious = 'zh-CN' | 'zh-TW' | 'en-US' | 'ru-RU' | 'ja-JP' export type CodeStyleVarious = BuiltinTheme | 'auto' export type WebDavConfig = { From 0d2f1534ef3a85339fbe76c4d936ba79b152b56c Mon Sep 17 00:00:00 2001 From: little_penguin66 Date: Sun, 29 Dec 2024 03:56:11 +0800 Subject: [PATCH 2/2] fix webdav auto backup bug --- .../settings/DataSettings/WebDavSettings.tsx | 7 +- src/renderer/src/services/BackupService.ts | 75 ++++++++++++++----- 2 files changed, 61 insertions(+), 21 deletions(-) diff --git a/src/renderer/src/pages/settings/DataSettings/WebDavSettings.tsx b/src/renderer/src/pages/settings/DataSettings/WebDavSettings.tsx index 5fb80d48c..e6480a655 100644 --- a/src/renderer/src/pages/settings/DataSettings/WebDavSettings.tsx +++ b/src/renderer/src/pages/settings/DataSettings/WebDavSettings.tsx @@ -1,7 +1,7 @@ import { FolderOpenOutlined, SaveOutlined } from '@ant-design/icons' import { HStack } from '@renderer/components/Layout' import { useSettings } from '@renderer/hooks/useSettings' -import { backupToWebdav, restoreFromWebdav } from '@renderer/services/BackupService' +import { backupToWebdav, restoreFromWebdav, startAutoSync, stopAutoSync } from '@renderer/services/BackupService' import { useAppDispatch } from '@renderer/store' import { setWebdavAutoSync, @@ -66,6 +66,11 @@ const WebDavSettings: FC = () => { const onToggleAutoSync = (checked: boolean) => { dispatch(setWebdavAutoSync(checked)) + if (checked) { + startAutoSync() + } else { + stopAutoSync() + } } const onSyncIntervalChange = (value: number) => { diff --git a/src/renderer/src/services/BackupService.ts b/src/renderer/src/services/BackupService.ts index d655c3985..51672c80f 100644 --- a/src/renderer/src/services/BackupService.ts +++ b/src/renderer/src/services/BackupService.ts @@ -59,6 +59,10 @@ export async function reset() { // 备份到 webdav export async function backupToWebdav({ showMessage = true }: { showMessage?: boolean } = {}) { + if (isManualBackupRunning) { + console.log('[Backup] Manual backup already in progress') + return + } const { webdavHost, webdavUser, webdavPass, webdavPath } = store.getState().settings const backupData = await getBackupData() @@ -83,6 +87,8 @@ export async function backupToWebdav({ showMessage = true }: { showMessage?: boo title: i18n.t('message.backup.failed'), content: error.message }) + } finally { + isManualBackupRunning = false } } @@ -109,40 +115,69 @@ export async function restoreFromWebdav() { } } -let syncInterval: NodeJS.Timeout | null = null +let autoSyncStarted = false +let syncTimeout: NodeJS.Timeout | null = null +let isAutoBackupRunning = false +let isManualBackupRunning = false + export function startAutoSync() { + if (autoSyncStarted) { + return + } + const { webdavAutoSync, webdavHost, webdavSyncInterval } = store.getState().settings - if (syncInterval) { - stopAutoSync() + if (!webdavAutoSync || !webdavHost || webdavSyncInterval <= 0) { + console.log('[AutoSync] Invalid sync settings, auto sync disabled') + return } - if (webdavAutoSync && webdavHost) { - console.log('[AutoSync] Starting auto sync with interval:', webdavSyncInterval, 'minutes') - - const performBackup = async () => { - try { - console.log('[AutoSync] Performing backup...') - await backupToWebdav({ showMessage: false }) - window.message.success({ content: i18n.t('message.backup.success'), key: 'webdav-sync' }) - } catch (error) { - console.error('[AutoSync] Backup failed:', error) - window.message.error({ content: i18n.t('message.backup.failed'), key: 'webdav-sync' }) - } + autoSyncStarted = true + + stopAutoSync() + + scheduleNextBackup() + + function scheduleNextBackup() { + if (syncTimeout) { + clearTimeout(syncTimeout) + syncTimeout = null } - syncInterval = setInterval(performBackup, webdavSyncInterval * 60 * 1000) + syncTimeout = setTimeout(performAutoBackup, webdavSyncInterval * 60 * 1000) + console.log(`[AutoSync] Next sync scheduled in ${webdavSyncInterval} minutes`) + } + + async function performAutoBackup() { + if (isAutoBackupRunning || isManualBackupRunning) { + console.log('[AutoSync] Backup already in progress, rescheduling') + scheduleNextBackup() + return + } - console.log(`[AutoSync] Sync interval set up: ${webdavSyncInterval} minutes`) + isAutoBackupRunning = true + try { + console.log('[AutoSync] Performing auto backup...') + await backupToWebdav({ showMessage: false }) + window.message.success({ content: i18n.t('message.backup.success'), key: 'webdav-auto-sync' }) + } catch (error) { + console.error('[AutoSync] Auto backup failed:', error) + window.message.error({ content: i18n.t('message.backup.failed'), key: 'webdav-auto-sync' }) + } finally { + isAutoBackupRunning = false + scheduleNextBackup() + } } } export function stopAutoSync() { - if (syncInterval) { + if (syncTimeout) { console.log('[AutoSync] Stopping auto sync') - clearInterval(syncInterval) - syncInterval = null + clearTimeout(syncTimeout) + syncTimeout = null } + isAutoBackupRunning = false + autoSyncStarted = false } async function getBackupData() {