diff --git a/src/App.tsx b/src/App.tsx index f42037171..7ecf95d77 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -797,7 +797,7 @@ function App() { setSettingsDefaultTab("usage"); setCurrentView("settings"); }} - title={t("settings.usage.title", { + title={t("usage.title", { defaultValue: "使用统计", })} className="hover:bg-black/5 dark:hover:bg-white/5" diff --git a/src/components/providers/forms/OmoFormFields.tsx b/src/components/providers/forms/OmoFormFields.tsx index a98545c60..2ee356a3c 100644 --- a/src/components/providers/forms/OmoFormFields.tsx +++ b/src/components/providers/forms/OmoFormFields.tsx @@ -119,7 +119,7 @@ export function OmoFormFields({ otherFieldsStr, onOtherFieldsStrChange, }: OmoFormFieldsProps) { - const { i18n } = useTranslation(); + const { t, i18n } = useTranslation(); const isZh = i18n.language?.startsWith("zh"); const [mainAgentsOpen, setMainAgentsOpen] = useState(true); @@ -183,14 +183,15 @@ export function OmoFormFields({ return [ { value: currentModel, - label: isZh - ? `${currentModel}(当前值,未启用)` - : `${currentModel} (current value, not enabled)`, + label: t("omo.currentValueNotEnabled", { + value: currentModel, + defaultValue: "{{value}} (current value, not enabled)", + }), }, ...modelOptions, ]; }, - [isZh, modelOptions], + [modelOptions, t], ); const resolveRecommendedModel = useCallback( @@ -224,17 +225,20 @@ export function OmoFormFields({ - {isZh ? "(清空)" : "(Clear)"} + {t("omo.clearWrapped", { defaultValue: "(Clear)" })} {options.length === 0 ? ( - {isZh ? "暂无已启用模型" : "No enabled models"} + {t("omo.noEnabledModels", { defaultValue: "No enabled models" })} ) : ( options.map((option) => ( @@ -282,27 +286,36 @@ export function OmoFormFields({ disabled={!hasModel} > - + - {isZh ? "(默认)" : "(Default)"} + {t("omo.defaultWrapped", { defaultValue: "(Default)" })} {!hasModel ? ( - {isZh ? "先选择模型" : "Select model first"} + {t("omo.selectModelFirst", { + defaultValue: "Select model first", + })} ) : variantOptions.length === 0 ? ( - {isZh ? "该模型无思考等级" : "No variants for model"} + {t("omo.noVariantsForModel", { + defaultValue: "No variants for model", + })} ) : ( variantOptions.map((variant, index) => ( {firstIsUnavailable && index === 0 - ? isZh - ? `${variant}(当前值,未启用)` - : `${variant} (current value, unavailable)` + ? t("omo.currentValueUnavailable", { + value: variant, + defaultValue: "{{value}} (current value, unavailable)", + }) : variant} )) @@ -498,7 +511,9 @@ export function OmoFormFields({ onBlur={(e) => { if (!handleAdvancedChange(configKey, e.target.value, store, setter)) { toast.error( - isZh ? "高级参数 JSON 无效" : "Advanced JSON is invalid", + t("omo.advancedJsonInvalid", { + defaultValue: "Advanced JSON is invalid", + }), ); } }} @@ -507,9 +522,10 @@ export function OmoFormFields({ /> {showHint && (

- {isZh - ? "temperature, top_p, budgetTokens, prompt_append, permission 等,留空使用默认值" - : "temperature, top_p, budgetTokens, prompt_append, permission, etc. Leave empty for defaults"} + {t("omo.advancedJsonHint", { + defaultValue: + "temperature, top_p, budgetTokens, prompt_append, permission, etc. Leave empty for defaults", + })}

)} @@ -518,9 +534,10 @@ export function OmoFormFields({ const handleFillAllRecommended = () => { if (modelOptions.length === 0) { toast.warning( - isZh - ? "当前没有可用的已启用模型,请先启用并配置 OpenCode 模型" - : "No enabled models available. Configure and enable OpenCode models first.", + t("omo.noEnabledModelsWarning", { + defaultValue: + "No enabled models available. Configure and enable OpenCode models first.", + }), ); return; } @@ -582,15 +599,17 @@ export function OmoFormFields({ ); setLocalFilePath(data.filePath); toast.success( - isZh - ? "已从本地文件导入并覆盖 Agent/Category/Other Fields" - : "Imported local file and replaced Agents/Categories/Other Fields", + t("omo.importLocalReplaceSuccess", { + defaultValue: + "Imported local file and replaced Agents/Categories/Other Fields", + }), ); } catch (err) { toast.error( - isZh - ? `读取本地文件失败: ${String(err)}` - : `Failed to read local file: ${String(err)}`, + t("omo.importLocalFailed", { + error: String(err), + defaultValue: "Failed to read local file: {{error}}", + }), ); } }, [ @@ -598,7 +617,7 @@ export function OmoFormFields({ onAgentsChange, onCategoriesChange, onOtherFieldsStrChange, - isZh, + t, ]); const renderBuiltinModelRow = ( @@ -641,7 +660,7 @@ export function OmoFormFields({ size="icon" className={cn("h-7 w-7 shrink-0", advStr && "text-primary")} onClick={() => toggleAdvancedEditor(scope, key, advStr, isExpanded)} - title={isZh ? "高级参数" : "Advanced"} + title={t("omo.advancedLabel", { defaultValue: "Advanced" })} > @@ -683,12 +702,8 @@ export function OmoFormFields({ const rowPrefix = isAgent ? "custom-agent" : "custom-cat"; const emptyKeyPrefix = isAgent ? "__custom_agent_" : "__custom_cat_"; const keyPlaceholder = isAgent - ? isZh - ? "agent 键名" - : "agent key" - : isZh - ? "分类键名" - : "category key"; + ? t("omo.agentKeyPlaceholder", { defaultValue: "agent key" }) + : t("omo.categoryKeyPlaceholder", { defaultValue: "category key" }); const key = item.key || `${emptyKeyPrefix}${index}`; const currentVariant = @@ -721,7 +736,7 @@ export function OmoFormFields({ {renderModelSelect( item.model, (value) => updateCustom({ model: value }), - "model-name", + t("omo.modelNamePlaceholder", { defaultValue: "model-name" }), )} {renderVariantSelect(item.model, currentVariant, (value) => { if (!item.key) return; @@ -733,7 +748,7 @@ export function OmoFormFields({ size="icon" className={cn("h-7 w-7 shrink-0", advStr && "text-primary")} onClick={() => toggleAdvancedEditor(scope, key, advStr, isExpanded)} - title={isZh ? "高级参数" : "Advanced"} + title={t("omo.advancedLabel", { defaultValue: "Advanced" })} > @@ -848,7 +863,7 @@ export function OmoFormFields({ onClick={onClick} > - {isZh ? "自定义" : "Custom"} + {t("omo.custom", { defaultValue: "Custom" })} ); @@ -874,7 +889,7 @@ export function OmoFormFields({
- {isZh - ? `已配置 ${configuredAgentCount} 个 Agent,${configuredCategoryCount} 个 Category · 点击 ⚙ 展开高级参数` - : `${configuredAgentCount} agents, ${configuredCategoryCount} categories configured · Click ⚙ for advanced params`} + {t("omo.configSummary", { + agents: configuredAgentCount, + categories: configuredCategoryCount, + defaultValue: + "{{agents}} agents, {{categories}} categories configured · Click ⚙ for advanced params", + })} ·{" "} - {isZh - ? `可选已启用模型 ${modelOptions.length} 个` - : `${modelOptions.length} enabled models available`} + {t("omo.enabledModelsCount", { + count: modelOptions.length, + defaultValue: "{{count}} enabled models available", + })} {localFilePath && ( - · {isZh ? "来源:" : "from:"}{" "} + · {t("omo.source", { defaultValue: "from:" })}{" "} {localFilePath.replace(/^.*\//, "")} @@ -926,7 +945,7 @@ export function OmoFormFields({
{renderModelSection({ - title: isZh ? "主 Agents" : "Main Agents", + title: t("omo.mainAgents", { defaultValue: "Main Agents" }), isOpen: mainAgentsOpen, onToggle: () => setMainAgentsOpen(!mainAgentsOpen), badge: `${mainAgents.length}`, @@ -934,7 +953,7 @@ export function OmoFormFields({ })} {renderModelSection({ - title: isZh ? "子 Agents" : "Sub Agents", + title: t("omo.subAgents", { defaultValue: "Sub Agents" }), isOpen: subAgentsOpen, onToggle: () => setSubAgentsOpen(!subAgentsOpen), badge: `${subAgents.length + customAgents.length}`, @@ -944,7 +963,9 @@ export function OmoFormFields({ {subAgents.map(renderAgentRow)} {customAgents.length > 0 && ( <> - {renderCustomDivider(isZh ? "自定义 Agents" : "Custom Agents")} + {renderCustomDivider( + t("omo.customAgents", { defaultValue: "Custom Agents" }), + )} {customAgents.map((a, i) => renderCustomModelRow("agent", a, i), )} @@ -955,7 +976,7 @@ export function OmoFormFields({ })} {renderModelSection({ - title: isZh ? "分类 (Categories)" : "Categories", + title: t("omo.categories", { defaultValue: "Categories" }), isOpen: categoriesOpen, onToggle: () => setCategoriesOpen(!categoriesOpen), badge: `${OMO_BUILTIN_CATEGORIES.length + customCategories.length}`, @@ -965,7 +986,11 @@ export function OmoFormFields({ {OMO_BUILTIN_CATEGORIES.map(renderCategoryRow)} {customCategories.length > 0 && ( <> - {renderCustomDivider(isZh ? "自定义分类" : "Custom Categories")} + {renderCustomDivider( + t("omo.customCategories", { + defaultValue: "Custom Categories", + }), + )} {customCategories.map((c, i) => renderCustomModelRow("category", c, i), )} @@ -976,7 +1001,9 @@ export function OmoFormFields({ })} {renderModelSection({ - title: isZh ? "其他字段 (JSON)" : "Other Fields (JSON)", + title: t("omo.otherFieldsJson", { + defaultValue: "Other Fields (JSON)", + }), isOpen: otherFieldsOpen, onToggle: () => setOtherFieldsOpen(!otherFieldsOpen), badge: diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 38362707c..05489ed9f 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -32,6 +32,7 @@ "back": "Back", "refresh": "Refresh", "refreshing": "Refreshing...", + "import": "Import", "all": "All", "search": "Search", "reset": "Reset", @@ -106,6 +107,10 @@ "duplicate": "Duplicate", "sortUpdateFailed": "Failed to update sort order", "configureUsage": "Configure usage query", + "officialPartner": "Official Partner", + "openTerminal": "Open Terminal", + "terminalOpened": "Terminal opened", + "terminalOpenFailed": "Failed to open terminal", "name": "Provider Name", "namePlaceholder": "e.g., Claude Official", "websiteUrl": "Website URL", @@ -156,7 +161,8 @@ "deleteFailed": "Failed to delete provider: {{error}}", "settingsSaved": "Settings saved", "settingsSaveFailed": "Failed to save settings: {{error}}", - "openAIChatFormatHint": "This provider uses OpenAI Chat format and requires the proxy service to be enabled" + "openAIChatFormatHint": "This provider uses OpenAI Chat format and requires the proxy service to be enabled", + "openLinkFailed": "Failed to open link" }, "confirm": { "deleteProvider": "Delete Provider", @@ -478,6 +484,7 @@ "aggregatorApiKeyHint": "💡 Only need to fill in API Key, endpoint is preset", "thirdPartyApiKeyHint": "💡 Only need to fill in API Key, endpoint is preset", "customApiKeyHint": "💡 Custom configuration requires manually filling all necessary fields", + "omoHint": "💡 OMO config manages Agent model assignments and writes to oh-my-opencode.jsonc", "officialHint": "💡 Official provider uses browser login, no API Key needed", "getApiKey": "Get API Key", "partnerPromotion": { @@ -1267,6 +1274,9 @@ "agents": { "title": "Agents" }, + "modelTest": { + "testProvider": "Test model" + }, "health": { "operational": "Operational", "degraded": "Degraded", @@ -1505,6 +1515,7 @@ "deleted": "Universal provider deleted", "addSuccess": "Universal provider added successfully", "addFailed": "Failed to add universal provider", + "hint": "Cross-app unified config, auto-sync to Claude/Codex/Gemini", "manage": "Manage", "loadError": "Failed to load universal providers", "saveError": "Failed to save universal provider", @@ -1558,6 +1569,32 @@ "commonConfigHint": "OMO common config will be merged into all OMO configs that enable it", "selectPlaceholder": "Select...", "clear": "Clear", + "clearWrapped": "(Clear)", + "defaultWrapped": "(Default)", + "variantPlaceholder": "variant", + "selectEnabledModel": "Select enabled model", + "selectModelFirst": "Select model first", + "noEnabledModels": "No enabled models", + "noVariantsForModel": "No variants for model", + "currentValueNotEnabled": "{{value}} (current value, not enabled)", + "currentValueUnavailable": "{{value}} (current value, unavailable)", + "advancedLabel": "Advanced", + "advancedJsonInvalid": "Advanced JSON is invalid", + "advancedJsonHint": "temperature, top_p, budgetTokens, prompt_append, permission, etc. Leave empty for defaults", + "noEnabledModelsWarning": "No enabled models available. Configure and enable OpenCode models first.", + "importLocalReplaceSuccess": "Imported local file and replaced Agents/Categories/Other Fields", + "importLocalFailed": "Failed to read local file: {{error}}", + "agentKeyPlaceholder": "agent key", + "categoryKeyPlaceholder": "category key", + "modelNamePlaceholder": "model-name", + "custom": "Custom", + "customCategories": "Custom Categories", + "modelConfiguration": "Model Configuration", + "fillRecommended": "Fill Recommended", + "configSummary": "{{agents}} agents, {{categories}} categories configured · Click ⚙ for advanced params", + "enabledModelsCount": "{{count}} enabled models available", + "source": "from:", + "otherFieldsJson": "Other Fields (JSON)", "searchOrType": "Search or type custom value...", "noMatches": "No matches", "jsonMustBeObject": "{{field}} must be a JSON object", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index ef88b9457..8675570cd 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -32,6 +32,7 @@ "back": "戻る", "refresh": "更新", "refreshing": "更新中...", + "import": "インポート", "all": "すべて", "search": "検索", "reset": "リセット", @@ -106,6 +107,10 @@ "duplicate": "複製", "sortUpdateFailed": "並び順の更新に失敗しました", "configureUsage": "利用状況を設定", + "officialPartner": "公式パートナー", + "openTerminal": "ターミナルを開く", + "terminalOpened": "ターミナルを開きました", + "terminalOpenFailed": "ターミナルを開けませんでした", "name": "プロバイダー名", "namePlaceholder": "例: Claude Official", "websiteUrl": "Web サイト URL", @@ -156,7 +161,8 @@ "deleteFailed": "プロバイダーの削除に失敗しました: {{error}}", "settingsSaved": "設定を保存しました", "settingsSaveFailed": "設定の保存に失敗しました: {{error}}", - "openAIChatFormatHint": "このプロバイダーは OpenAI Chat フォーマットを使用しており、プロキシサービスの有効化が必要です" + "openAIChatFormatHint": "このプロバイダーは OpenAI Chat フォーマットを使用しており、プロキシサービスの有効化が必要です", + "openLinkFailed": "リンクを開けませんでした" }, "confirm": { "deleteProvider": "プロバイダーを削除", @@ -478,6 +484,7 @@ "aggregatorApiKeyHint": "💡 API Key のみ入力すれば OK。エンドポイントはプリセット済みです", "thirdPartyApiKeyHint": "💡 API Key のみ入力すれば OK。エンドポイントはプリセット済みです", "customApiKeyHint": "💡 カスタム設定では必要な項目をすべて手動で入力してください", + "omoHint": "💡 OMO 設定は Agent のモデル割り当てを管理し、oh-my-opencode.jsonc に書き込みます", "officialHint": "💡 公式プロバイダーはブラウザログインで、API Key は不要です", "getApiKey": "API Key を取得", "partnerPromotion": { @@ -1161,6 +1168,13 @@ "codex": "Codex", "gemini": "Gemini", "opencode": "OpenCode" + }, + "installFromZip": { + "button": "ZIP からインストール", + "installing": "インストール中...", + "successSingle": "スキル {{name}} をインストールしました", + "successMultiple": "{{count}} 件のスキルをインストールしました", + "noSkillsFound": "ZIP ファイルにスキルが見つかりません(SKILL.md が必要です)" } }, "deeplink": { @@ -1258,6 +1272,9 @@ "agents": { "title": "エージェント" }, + "modelTest": { + "testProvider": "モデルテスト" + }, "health": { "operational": "正常", "degraded": "低下", @@ -1479,6 +1496,7 @@ "deleted": "統合プロバイダーを削除しました", "addSuccess": "統合プロバイダーを追加しました", "addFailed": "統合プロバイダーの追加に失敗しました", + "hint": "クロスアプリ統合設定。Claude/Codex/Gemini に自動同期します", "manage": "管理", "loadError": "統合プロバイダーの読み込みに失敗しました", "saveError": "統合プロバイダーの保存に失敗しました", @@ -1532,6 +1550,32 @@ "commonConfigHint": "OMO 共通設定は有効にしたすべての OMO 設定に統合されます", "selectPlaceholder": "選択してください...", "clear": "クリア", + "clearWrapped": "(クリア)", + "defaultWrapped": "(デフォルト)", + "variantPlaceholder": "variant", + "selectEnabledModel": "有効なモデルを選択", + "selectModelFirst": "先にモデルを選択", + "noEnabledModels": "有効なモデルがありません", + "noVariantsForModel": "このモデルには思考レベルがありません", + "currentValueNotEnabled": "{{value}} (現在値・未有効)", + "currentValueUnavailable": "{{value}} (現在値・利用不可)", + "advancedLabel": "詳細", + "advancedJsonInvalid": "詳細 JSON が不正です", + "advancedJsonHint": "temperature, top_p, budgetTokens, prompt_append, permission など。空欄でデフォルトを使用します", + "noEnabledModelsWarning": "利用可能な有効モデルがありません。先に OpenCode モデルを有効化してください。", + "importLocalReplaceSuccess": "ローカルファイルから読み込み、Agents/Categories/Other Fields を置き換えました", + "importLocalFailed": "ローカルファイルの読み込みに失敗しました: {{error}}", + "agentKeyPlaceholder": "agent キー", + "categoryKeyPlaceholder": "カテゴリキー", + "modelNamePlaceholder": "model-name", + "custom": "カスタム", + "customCategories": "カスタムカテゴリ", + "modelConfiguration": "モデル設定", + "fillRecommended": "推奨を入力", + "configSummary": "{{agents}} 個の Agent、{{categories}} 個の Category を設定済み · ⚙ で詳細を展開", + "enabledModelsCount": "有効モデル {{count}} 件", + "source": "出典:", + "otherFieldsJson": "その他のフィールド (JSON)", "searchOrType": "検索またはカスタム値を入力...", "noMatches": "一致する項目がありません", "jsonMustBeObject": "{{field}} は JSON オブジェクトである必要があります", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 172c7ad1c..350ff83e6 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -32,6 +32,7 @@ "back": "返回", "refresh": "刷新", "refreshing": "刷新中...", + "import": "导入", "all": "全部", "search": "查询", "reset": "重置", @@ -106,6 +107,10 @@ "duplicate": "复制", "sortUpdateFailed": "排序更新失败", "configureUsage": "配置用量查询", + "officialPartner": "官方合作伙伴", + "openTerminal": "打开终端", + "terminalOpened": "终端已打开", + "terminalOpenFailed": "打开终端失败", "name": "供应商名称", "namePlaceholder": "例如:Claude 官方", "websiteUrl": "官网链接", @@ -156,7 +161,8 @@ "deleteFailed": "删除供应商失败:{{error}}", "settingsSaved": "设置已保存", "settingsSaveFailed": "保存设置失败:{{error}}", - "openAIChatFormatHint": "此供应商使用 OpenAI Chat 格式,需要开启代理服务才能正常使用" + "openAIChatFormatHint": "此供应商使用 OpenAI Chat 格式,需要开启代理服务才能正常使用", + "openLinkFailed": "链接打开失败" }, "confirm": { "deleteProvider": "删除供应商", @@ -478,6 +484,7 @@ "aggregatorApiKeyHint": "💡 只需填写 API Key,请求地址已预设", "thirdPartyApiKeyHint": "💡 只需填写 API Key,请求地址已预设", "customApiKeyHint": "💡 自定义配置需手动填写所有必要字段", + "omoHint": "💡 OMO 配置管理 Agent 模型分配,写入 oh-my-opencode.jsonc", "officialHint": "💡 官方供应商使用浏览器登录,无需配置 API Key", "getApiKey": "获取 API Key", "partnerPromotion": { @@ -1267,6 +1274,9 @@ "agents": { "title": "智能体" }, + "modelTest": { + "testProvider": "测试模型" + }, "health": { "operational": "正常", "degraded": "降级", @@ -1505,6 +1515,7 @@ "deleted": "统一供应商已删除", "addSuccess": "统一供应商添加成功", "addFailed": "统一供应商添加失败", + "hint": "跨应用统一配置,自动同步到 Claude/Codex/Gemini", "manage": "管理", "loadError": "加载统一供应商失败", "saveError": "保存统一供应商失败", @@ -1558,6 +1569,32 @@ "commonConfigHint": "OMO 通用配置将合并到所有启用它的 OMO 配置中", "selectPlaceholder": "请选择...", "clear": "清空", + "clearWrapped": "(清空)", + "defaultWrapped": "(默认)", + "variantPlaceholder": "思考等级", + "selectEnabledModel": "选择已启用模型", + "selectModelFirst": "先选择模型", + "noEnabledModels": "暂无已启用模型", + "noVariantsForModel": "该模型无思考等级", + "currentValueNotEnabled": "{{value}}(当前值,未启用)", + "currentValueUnavailable": "{{value}}(当前值,未启用)", + "advancedLabel": "高级参数", + "advancedJsonInvalid": "高级参数 JSON 无效", + "advancedJsonHint": "temperature, top_p, budgetTokens, prompt_append, permission 等,留空使用默认值", + "noEnabledModelsWarning": "当前没有可用的已启用模型,请先启用并配置 OpenCode 模型", + "importLocalReplaceSuccess": "已从本地文件导入并覆盖 Agent/Category/Other Fields", + "importLocalFailed": "读取本地文件失败: {{error}}", + "agentKeyPlaceholder": "agent 键名", + "categoryKeyPlaceholder": "分类键名", + "modelNamePlaceholder": "模型名", + "custom": "自定义", + "customCategories": "自定义分类", + "modelConfiguration": "模型配置", + "fillRecommended": "填充推荐", + "configSummary": "已配置 {{agents}} 个 Agent,{{categories}} 个 Category · 点击 ⚙ 展开高级参数", + "enabledModelsCount": "可选已启用模型 {{count}} 个", + "source": "来源:", + "otherFieldsJson": "其他字段 (JSON)", "searchOrType": "搜索或输入自定义值...", "noMatches": "无匹配项", "jsonMustBeObject": "{{field}} 必须是 JSON 对象",