diff --git a/src/components/ColorPicker.tsx b/src/components/ColorPicker.tsx index 9948c6d0..2a5c0b07 100644 --- a/src/components/ColorPicker.tsx +++ b/src/components/ColorPicker.tsx @@ -2,6 +2,7 @@ import React from "react"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { cn } from "@/lib/utils"; +import { useTranslation } from "react-i18next"; interface ColorPickerProps { value?: string; @@ -28,12 +29,14 @@ const DEFAULT_PRESETS = [ export const ColorPicker: React.FC = ({ value = "#4285F4", onValueChange, - label = "图标颜色", + label, presets = DEFAULT_PRESETS, }) => { + const { t } = useTranslation(); + const displayLabel = label ?? t("providerIcon.color", "图标颜色"); return (
- + {/* 颜色预设 */}
diff --git a/src/components/proxy/CircuitBreakerConfigPanel.tsx b/src/components/proxy/CircuitBreakerConfigPanel.tsx index a5776799..31c72b75 100644 --- a/src/components/proxy/CircuitBreakerConfigPanel.tsx +++ b/src/components/proxy/CircuitBreakerConfigPanel.tsx @@ -146,15 +146,24 @@ export function CircuitBreakerConfigPanel() { }; if (isLoading) { - return
加载中...
; + return ( +
+ {t("circuitBreaker.loading", "加载中...")} +
+ ); } return (
-

熔断器配置

+

+ {t("circuitBreaker.title", "熔断器配置")} +

- 调整熔断器参数以控制故障检测和恢复行为 + {t( + "circuitBreaker.description", + "调整熔断器参数以控制故障检测和恢复行为", + )}

@@ -163,7 +172,9 @@ export function CircuitBreakerConfigPanel() {
{/* 失败阈值 */}
- +

- 连续失败多少次后打开熔断器 + {t( + "circuitBreaker.failureThresholdHint", + "连续失败多少次后打开熔断器", + )}

{/* 超时时间 */}
- +

- 熔断器打开后多久尝试恢复(半开状态) + {t( + "circuitBreaker.timeoutSecondsHint", + "熔断器打开后多久尝试恢复(半开状态)", + )}

{/* 成功阈值 */}
- +

- 半开状态下成功多少次后关闭熔断器 + {t( + "circuitBreaker.successThresholdHint", + "半开状态下成功多少次后关闭熔断器", + )}

{/* 错误率阈值 */}
- +

- 错误率超过此值时打开熔断器 + {t( + "circuitBreaker.errorRateThresholdHint", + "错误率超过此值时打开熔断器", + )}

{/* 最小请求数 */}
- +

- 计算错误率前的最小请求数 + {t("circuitBreaker.minRequestsHint", "计算错误率前的最小请求数")}

{/* 说明信息 */}
-

配置说明

+

+ {t("circuitBreaker.instructionsTitle", "配置说明")} +

  • - • 失败阈值:连续失败达到此次数时,熔断器打开 + •{" "} + {t("circuitBreaker.failureThreshold", "失败阈值")} + : + {t( + "circuitBreaker.instructions.failureThreshold", + "连续失败达到此次数时,熔断器打开", + )}
  • - • 超时时间:熔断器打开后,等待此时间后尝试半开 + • {t("circuitBreaker.timeoutSeconds", "超时时间")} + : + {t( + "circuitBreaker.instructions.timeout", + "熔断器打开后,等待此时间后尝试半开", + )}
  • - • 成功阈值:半开状态下,成功达到此次数时关闭熔断器 + •{" "} + {t("circuitBreaker.successThreshold", "成功阈值")} + : + {t( + "circuitBreaker.instructions.successThreshold", + "半开状态下,成功达到此次数时关闭熔断器", + )}
  • - • 错误率阈值:错误率超过此值时,熔断器打开 + •{" "} + + {t("circuitBreaker.errorRateThreshold", "错误率阈值")} + + : + {t( + "circuitBreaker.instructions.errorRate", + "错误率超过此值时,熔断器打开", + )}
  • - • 最小请求数:只有请求数达到此值后才计算错误率 + • {t("circuitBreaker.minRequests", "最小请求数")}: + {t( + "circuitBreaker.instructions.minRequests", + "只有请求数达到此值后才计算错误率", + )}
diff --git a/src/components/proxy/ProxyToggle.tsx b/src/components/proxy/ProxyToggle.tsx index 462c3d9b..61c831ed 100644 --- a/src/components/proxy/ProxyToggle.tsx +++ b/src/components/proxy/ProxyToggle.tsx @@ -44,12 +44,17 @@ export function ProxyToggle({ className, activeApp }: ProxyToggleProps) { const tooltipText = takeoverEnabled ? isRunning ? t("proxy.takeover.tooltip.active", { + appLabel, + address: status?.address, + port: status?.port, defaultValue: `${appLabel} 已接管 - ${status?.address}:${status?.port}\n切换该应用供应商为热切换`, }) : t("proxy.takeover.tooltip.broken", { + appLabel, defaultValue: `${appLabel} 已接管,但代理服务未运行`, }) : t("proxy.takeover.tooltip.inactive", { + appLabel, defaultValue: `接管 ${appLabel} 的 Live 配置,让该应用请求走本地代理`, }); diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 90dc313c..4571f452 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -38,7 +38,9 @@ "reset": "Reset", "actions": "Actions", "deleting": "Deleting...", - "auto": "Auto" + "auto": "Auto", + "enabled": "Enabled", + "notSet": "Not Set" }, "apiKeyInput": { "placeholder": "Enter API Key", @@ -579,7 +581,8 @@ "pricingLoadFailed": "Failed to load pricing defaults: {{error}}", "defaultCostMultiplierRequired": "Default multiplier is required", "defaultCostMultiplierInvalid": "Invalid multiplier format" - } + }, + "saveFailedGeneric": "Save failed, please try again" }, "apps": { "claude": "Claude", @@ -633,7 +636,14 @@ "daysAgo": "{{count}} days ago", "roleUser": "User", "roleSystem": "System", - "roleTool": "Tool" + "roleTool": "Tool", + "resume": "Resume Session", + "resumeTooltip": "Resume this session in terminal", + "noResumeCommand": "This session cannot be resumed", + "copyCommand": "Copy Command", + "copyMessage": "Copy Message", + "messageCopied": "Message copied", + "conversationHistory": "Conversation History" }, "console": { "providerSwitchReceived": "Received provider switch event:", @@ -755,7 +765,8 @@ "pleaseAddEndpoint": "Please add an endpoint first", "testUnavailable": "Speed test unavailable", "noResult": "No result returned", - "testFailed": "Speed test failed: {{error}}" + "testFailed": "Speed test failed: {{error}}", + "empty": "No endpoints" }, "providerAdvanced": { "testConfig": "Model Test Config", @@ -799,7 +810,9 @@ "extractNoCommonConfig": "No common config available to extract from editor", "extractFailed": "Extract failed: {{error}}", "saveFailed": "Save failed: {{error}}", - "modelNameHint": "Specify the model to use, will be auto-updated in config.toml" + "modelNameHint": "Specify the model to use, will be auto-updated in config.toml", + "modelName": "Model Name", + "modelNamePlaceholder": "e.g., gpt-5-codex" }, "geminiConfig": { "envFile": "Environment Variables (.env)", @@ -944,7 +957,28 @@ "costBreakdown": "Cost Breakdown", "performance": "Performance", "latency": "Latency", - "errorMessage": "Error Message" + "errorMessage": "Error Message", + "requests": "Requests", + "tokens": "Tokens", + "avgCost": "Average Cost", + "avgLatency": "Average Latency", + "successRate": "Success Rate", + "requestId": "Request ID", + "never": "Never", + "modelId": "Model ID", + "modelIdRequired": "Model ID is required", + "inputCostPerMillion": "Input Cost (per million tokens, USD)", + "outputCostPerMillion": "Output Cost (per million tokens, USD)", + "invalidPrice": "Price must be non-negative", + "invalidTimeRange": "Please select complete start/end time", + "invalidTimeRangeOrder": "Start time cannot be later than end time", + "timeRangeTooLarge": "Time range is too large, please narrow it down", + "addPricing": "Add Pricing", + "editPricing": "Edit Pricing", + "pricingAdded": "Pricing added", + "pricingUpdated": "Pricing updated", + "cacheReadCostPerMillion": "Cache Read Cost (per million tokens, USD)", + "cacheCreationCostPerMillion": "Cache Write Cost (per million tokens, USD)" }, "usageScript": { "title": "Configure Usage Query", @@ -1367,7 +1401,9 @@ "stringifiedEnvVars": "env.vars should be an object, but the current value looks stringified or malformed.", "stringifiedShellEnv": "env.shellEnv should be an object, but the current value looks stringified or malformed.", "parseFailed": "openclaw.json could not be parsed as valid JSON5. Fix the file before editing it here." - } + }, + "primaryModel": "Primary Model", + "fallbackModel": "Fallback Model" }, "env": { "warning": { @@ -1587,7 +1623,14 @@ "skillsPath": "Skills Path", "hint": "This will add the Skill repository to the list.", "hintDetail": "After adding, you can install specific Skills from the Skills management page." - } + }, + "usageScript": "Usage Query", + "usageScriptEnabled": "Enabled", + "usageScriptDisabled": "Disabled", + "usageApiKey": "Usage API Key", + "usageBaseUrl": "Usage Query URL", + "usageAutoInterval": "Auto Query", + "usageAutoIntervalValue": "Every {{minutes}} minutes" }, "iconPicker": { "search": "Search Icons", @@ -1606,7 +1649,8 @@ "selectIcon": "Select Icon", "preview": "Preview", "clickToChange": "Click to change icon", - "clickToSelect": "Click to select icon" + "clickToSelect": "Click to select icon", + "color": "Icon Color" }, "migration": { "success": "Configuration migrated successfully", @@ -1743,7 +1787,12 @@ "hint": "Select apps to take over — once enabled, requests from that app will be routed through the local proxy", "enabled": "{{app}} takeover enabled", "disabled": "{{app}} takeover disabled", - "failed": "Failed to toggle takeover" + "failed": "Failed to toggle takeover", + "tooltip": { + "active": "{{appLabel}} is intercepting - {{address}}:{{port}}\nSwitch provider for hot switching", + "broken": "{{appLabel}} is intercepting, but proxy service is not running", + "inactive": "Intercept {{appLabel}}'s live config to route requests through local proxy" + } }, "failover": { "proxyRequired": "Proxy service must be started to configure failover", @@ -1793,8 +1842,28 @@ "successThresholdLabel": "Recovery Success Threshold", "successThresholdExplain": "In half-open state, close circuit breaker after this many successes, making provider available again", "errorRateLabel": "Error Rate Threshold", - "errorRateExplain": "Open circuit breaker when error rate exceeds this value, even if failure threshold not reached" - } + "errorRateExplain": "Open circuit breaker when error rate exceeds this value, even if failure threshold not reached", + "maxRetries": "Max Retries", + "timeoutSettings": "Timeout Settings", + "streamingFirstByte": "Streaming First Byte Timeout", + "streamingIdle": "Streaming Idle Timeout", + "nonStreaming": "Non-Streaming Timeout", + "maxRetriesHint": "Number of retries on request failure (0-10)", + "streamingFirstByteHint": "Max time to wait for first data chunk, range 1-120s, default 60s", + "streamingIdleHint": "Max interval between data chunks, range 60-600s, 0 to disable (prevents mid-stream stalls)", + "nonStreamingHint": "Total timeout for non-streaming requests, range 60-1200s, default 600s (10 min)" + }, + "logging": { + "enabled": "Logging enabled", + "disabled": "Logging disabled", + "failed": "Failed to toggle logging" + }, + "server": { + "started": "Proxy service started - {{address}}:{{port}}", + "startFailed": "Failed to start proxy service: {{detail}}" + }, + "stoppedWithRestore": "Proxy service stopped, all takeover configs restored", + "stopWithRestoreFailed": "Stop failed: {{detail}}" }, "streamCheck": { "configSaved": "Health check config saved", @@ -1807,7 +1876,11 @@ "timeout": "Timeout (seconds)", "maxRetries": "Max Retries", "degradedThreshold": "Degraded Threshold (ms)", - "testPrompt": "Test Prompt" + "testPrompt": "Test Prompt", + "operational": "{{providerName}} is operational ({{responseTimeMs}}ms)", + "degraded": "{{providerName}} is slow ({{responseTimeMs}}ms)", + "failed": "{{providerName}} check failed: {{message}}", + "error": "{{providerName}} check error: {{error}}" }, "proxyConfig": { "proxyEnabled": "Proxy Enabled", @@ -1829,11 +1902,28 @@ "failureThreshold": "Failure Threshold", "successThreshold": "Success Threshold", "timeoutSeconds": "Timeout (seconds)", - "errorRateThreshold": "Error Rate Threshold", - "minRequests": "Min Requests", + "errorRateThreshold": "Error Rate Threshold (%)", + "minRequests": "Minimum Requests", "validationFailed": "The following fields are out of valid range: {{fields}}", - "configSaved": "Circuit breaker config saved", - "saveFailed": "Failed to save" + "configSaved": "Circuit breaker configuration saved", + "saveFailed": "Save failed", + "loading": "Loading...", + "title": "Circuit Breaker Configuration", + "description": "Adjust circuit breaker parameters to control fault detection and recovery behavior", + "failureThresholdHint": "How many consecutive failures trigger the circuit breaker", + "timeoutSecondsHint": "How long to wait before attempting recovery (half-open state)", + "successThresholdHint": "How many successes in half-open state to close the circuit breaker", + "errorRateThresholdHint": "Open circuit breaker when error rate exceeds this value", + "minRequestsHint": "Minimum requests before calculating error rate", + "saveConfig": "Save Configuration", + "instructionsTitle": "Configuration Instructions", + "instructions": { + "failureThreshold": "Circuit breaker opens when consecutive failures reach this count", + "timeout": "After circuit breaker opens, wait this time before attempting half-open", + "successThreshold": "In half-open state, close circuit breaker when successes reach this count", + "errorRate": "Circuit breaker opens when error rate exceeds this value", + "minRequests": "Error rate is only calculated after request count reaches this value" + } }, "universalProvider": { "title": "Universal Provider", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 7eaf863e..336a4970 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -38,7 +38,9 @@ "reset": "リセット", "actions": "操作", "deleting": "削除中...", - "auto": "自動" + "auto": "自動", + "enabled": "有効", + "notSet": "未設定" }, "apiKeyInput": { "placeholder": "API Key を入力", @@ -579,7 +581,8 @@ "pricingLoadFailed": "課金設定の読み込みに失敗しました: {{error}}", "defaultCostMultiplierRequired": "デフォルト倍率は必須です", "defaultCostMultiplierInvalid": "デフォルト倍率の形式が正しくありません" - } + }, + "saveFailedGeneric": "保存に失敗しました。もう一度お試しください" }, "apps": { "claude": "Claude", @@ -633,7 +636,14 @@ "daysAgo": "{{count}}日前", "roleUser": "ユーザー", "roleSystem": "システム", - "roleTool": "ツール" + "roleTool": "ツール", + "resume": "セッションを再開", + "resumeTooltip": "ターミナルでこのセッションを再開", + "noResumeCommand": "このセッションは再開できません", + "copyCommand": "コマンドをコピー", + "copyMessage": "メッセージをコピー", + "messageCopied": "メッセージがコピーされました", + "conversationHistory": "会話履歴" }, "console": { "providerSwitchReceived": "プロバイダー切り替えイベントを受信:", @@ -755,7 +765,8 @@ "pleaseAddEndpoint": "まずエンドポイントを追加してください", "testUnavailable": "速度テストを実行できません", "noResult": "結果がありません", - "testFailed": "速度テストに失敗しました: {{error}}" + "testFailed": "速度テストに失敗しました: {{error}}", + "empty": "エンドポイントがありません" }, "providerAdvanced": { "testConfig": "モデルテスト設定", @@ -799,7 +810,9 @@ "extractNoCommonConfig": "編集内容から抽出できる共通設定がありません", "extractFailed": "抽出に失敗しました: {{error}}", "saveFailed": "保存に失敗しました: {{error}}", - "modelNameHint": "使用するモデルを指定します。config.toml に自動更新されます" + "modelNameHint": "使用するモデルを指定します。config.toml に自動更新されます", + "modelName": "モデル名", + "modelNamePlaceholder": "例: gpt-5-codex" }, "geminiConfig": { "envFile": "環境変数 (.env)", @@ -944,7 +957,28 @@ "costBreakdown": "コスト明細", "performance": "パフォーマンス", "latency": "レイテンシー", - "errorMessage": "エラーメッセージ" + "errorMessage": "エラーメッセージ", + "requests": "リクエスト数", + "tokens": "トークン", + "avgCost": "平均コスト", + "avgLatency": "平均レイテンシ", + "successRate": "成功率", + "requestId": "リクエスト ID", + "never": "なし", + "modelId": "モデル ID", + "modelIdRequired": "モデル ID は必須です", + "inputCostPerMillion": "入力コスト(100万トークンあたり、USD)", + "outputCostPerMillion": "出力コスト(100万トークンあたり、USD)", + "invalidPrice": "価格は負でない数値である必要があります", + "invalidTimeRange": "開始/終了時刻を完全に選択してください", + "invalidTimeRangeOrder": "開始時刻は終了時刻より前である必要があります", + "timeRangeTooLarge": "時間範囲が大きすぎます。範囲を縮小してください", + "addPricing": "価格設定を追加", + "editPricing": "価格設定を編集", + "pricingAdded": "価格設定が追加されました", + "pricingUpdated": "価格設定が更新されました", + "cacheReadCostPerMillion": "キャッシュ読み取りコスト(100万トークンあたり、USD)", + "cacheCreationCostPerMillion": "キャッシュ書き込みコスト(100万トークンあたり、USD)" }, "usageScript": { "title": "利用状況を設定", @@ -1367,7 +1401,9 @@ "stringifiedEnvVars": "env.vars はオブジェクトである必要がありますが、現在の値は文字列化または破損しているようです。", "stringifiedShellEnv": "env.shellEnv はオブジェクトである必要がありますが、現在の値は文字列化または破損しているようです。", "parseFailed": "openclaw.json を有効な JSON5 として解析できませんでした。ここで編集する前にファイルを修正してください。" - } + }, + "primaryModel": "プライマリモデル", + "fallbackModel": "フォールバックモデル" }, "env": { "warning": { @@ -1587,7 +1623,14 @@ "skillsPath": "スキルパス", "hint": "この操作でスキルリポジトリが一覧に追加されます。", "hintDetail": "追加後、スキル管理ページから個別のスキルをインストールできます。" - } + }, + "usageScript": "使用量クエリ", + "usageScriptEnabled": "有効", + "usageScriptDisabled": "無効", + "usageApiKey": "使用量 API キー", + "usageBaseUrl": "使用量クエリ URL", + "usageAutoInterval": "自動クエリ", + "usageAutoIntervalValue": "{{minutes}} 分ごと" }, "iconPicker": { "search": "アイコンを検索", @@ -1606,7 +1649,8 @@ "selectIcon": "アイコンを選択", "preview": "プレビュー", "clickToChange": "クリックでアイコンを変更", - "clickToSelect": "クリックでアイコンを選択" + "clickToSelect": "クリックでアイコンを選択", + "color": "アイコンカラー" }, "migration": { "success": "設定の移行が完了しました", @@ -1743,7 +1787,12 @@ "hint": "テイクオーバーするアプリを選択します。有効にすると、そのアプリのリクエストはローカルプロキシ経由で転送されます", "enabled": "{{app}} テイクオーバー有効", "disabled": "{{app}} テイクオーバー無効", - "failed": "テイクオーバーの切り替えに失敗しました" + "failed": "テイクオーバーの切り替えに失敗しました", + "tooltip": { + "active": "{{appLabel}} がインターセプト中 - {{address}}:{{port}}\nホットスイッチングのためプロバイダを切り替え", + "broken": "{{appLabel}} がインターセプト中ですが、プロキシサービスが実行されていません", + "inactive": "{{appLabel}} のライブ設定をインターセプトしてリクエストをローカルプロキシ経由でルーティング" + } }, "failover": { "proxyRequired": "フェイルオーバーを設定するには、プロキシサービスを先に起動する必要があります", @@ -1793,8 +1842,28 @@ "successThresholdLabel": "回復成功しきい値", "successThresholdExplain": "半開状態でこの回数成功するとサーキットブレーカーが閉じ、プロバイダーが再び利用可能になります", "errorRateLabel": "エラー率しきい値", - "errorRateExplain": "失敗しきい値に達していなくても、エラー率がこの値を超えるとサーキットブレーカーが開きます" - } + "errorRateExplain": "失敗しきい値に達していなくても、エラー率がこの値を超えるとサーキットブレーカーが開きます", + "maxRetries": "最大リトライ回数", + "timeoutSettings": "タイムアウト設定", + "streamingFirstByte": "ストリーミング最初のバイトタイムアウト", + "streamingIdle": "ストリーミングアイドルタイムアウト", + "nonStreaming": "非ストリーミングタイムアウト", + "maxRetriesHint": "リクエスト失敗時のリトライ回数(0-10)", + "streamingFirstByteHint": "最初のデータチャンクを待つ最大時間、範囲 1-120 秒、デフォルト 60 秒", + "streamingIdleHint": "データチャンク間の最大間隔、範囲 60-600 秒、0 で無効化(途中停止を防止)", + "nonStreamingHint": "非ストリーミングリクエストの合計タイムアウト、範囲 60-1200 秒、デフォルト 600 秒(10 分)" + }, + "logging": { + "enabled": "ログ記録が有効になりました", + "disabled": "ログ記録が無効になりました", + "failed": "ログ状態の切り替えに失敗しました" + }, + "server": { + "started": "プロキシサービスが開始されました - {{address}}:{{port}}", + "startFailed": "プロキシサービスの開始に失敗しました: {{detail}}" + }, + "stoppedWithRestore": "プロキシサービスが停止し、すべてのテイクオーバー設定が復元されました", + "stopWithRestoreFailed": "停止に失敗しました: {{detail}}" }, "streamCheck": { "configSaved": "ヘルスチェック設定を保存しました", @@ -1807,7 +1876,11 @@ "timeout": "タイムアウト(秒)", "maxRetries": "最大リトライ回数", "degradedThreshold": "劣化しきい値(ミリ秒)", - "testPrompt": "テストプロンプト" + "testPrompt": "テストプロンプト", + "operational": "{{providerName}} は正常に動作しています ({{responseTimeMs}}ms)", + "degraded": "{{providerName}} の応答が遅いです ({{responseTimeMs}}ms)", + "failed": "{{providerName}} のチェックに失敗しました: {{message}}", + "error": "{{providerName}} のチェックでエラーが発生しました: {{error}}" }, "proxyConfig": { "proxyEnabled": "プロキシ有効", @@ -1826,14 +1899,31 @@ "nonStreaming": "非ストリーミングタイムアウト" }, "circuitBreaker": { - "failureThreshold": "失敗しきい値", - "successThreshold": "成功しきい値", + "failureThreshold": "失敗閾値", + "successThreshold": "成功閾値", "timeoutSeconds": "タイムアウト(秒)", - "errorRateThreshold": "エラー率しきい値", + "errorRateThreshold": "エラー率閾値 (%)", "minRequests": "最小リクエスト数", "validationFailed": "以下のフィールドが有効範囲外です: {{fields}}", - "configSaved": "サーキットブレーカー設定を保存しました", - "saveFailed": "保存に失敗しました" + "configSaved": "サーキットブレーカー設定が保存されました", + "saveFailed": "保存に失敗しました", + "loading": "読み込み中...", + "title": "サーキットブレーカー設定", + "description": "サーキットブレーカーパラメータを調整して、障害検出と復旧動作を制御します", + "failureThresholdHint": "連続失敗後にサーキットブレーカーを開く回数", + "timeoutSecondsHint": "サーキットブレーカーを開いた後、復旧を試みるまでの時間(半開状態)", + "successThresholdHint": "半開状態で成功してサーキットブレーカーを閉じる回数", + "errorRateThresholdHint": "エラー率がこの値を超えるとサーキットブレーカーを開く", + "minRequestsHint": "エラー率を計算する前の最小リクエスト数", + "saveConfig": "設定を保存", + "instructionsTitle": "設定説明", + "instructions": { + "failureThreshold": "連続失敗がこの回数に達するとサーキットブレーカーが開く", + "timeout": "サーキットブレーカーを開いた後、この時間待機してから半開を試みる", + "successThreshold": "半開状態で成功がこの回数に達するとサーキットブレーカーを閉じる", + "errorRate": "エラー率がこの値を超えるとサーキットブレーカーが開く", + "minRequests": "リクエスト数がこの値に達した後にのみエラー率が計算される" + } }, "universalProvider": { "title": "統合プロバイダー", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 2efdc196..aa5c881d 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -38,7 +38,9 @@ "reset": "重置", "actions": "操作", "deleting": "删除中...", - "auto": "自动" + "auto": "自动", + "enabled": "已开启", + "notSet": "未设置" }, "apiKeyInput": { "placeholder": "请输入API Key", @@ -579,7 +581,8 @@ "pricingLoadFailed": "加载计费配置失败:{{error}}", "defaultCostMultiplierRequired": "默认倍率不能为空", "defaultCostMultiplierInvalid": "默认倍率格式不正确" - } + }, + "saveFailedGeneric": "保存失败,请重试" }, "apps": { "claude": "Claude", @@ -633,7 +636,14 @@ "daysAgo": "{{count}} 天前", "roleUser": "用户", "roleSystem": "系统", - "roleTool": "工具" + "roleTool": "工具", + "resume": "恢复会话", + "resumeTooltip": "在终端中恢复此会话", + "noResumeCommand": "此会话无法恢复", + "copyCommand": "复制命令", + "copyMessage": "复制消息", + "messageCopied": "已复制消息内容", + "conversationHistory": "对话记录" }, "console": { "providerSwitchReceived": "收到供应商切换事件:", @@ -755,7 +765,8 @@ "pleaseAddEndpoint": "请先添加端点", "testUnavailable": "测速功能不可用", "noResult": "未返回结果", - "testFailed": "测速失败: {{error}}" + "testFailed": "测速失败: {{error}}", + "empty": "暂无端点" }, "providerAdvanced": { "testConfig": "模型测试配置", @@ -799,7 +810,9 @@ "extractNoCommonConfig": "当前编辑内容没有可提取的通用配置", "extractFailed": "提取失败: {{error}}", "saveFailed": "保存失败: {{error}}", - "modelNameHint": "指定使用的模型,将自动更新到 config.toml 中" + "modelNameHint": "指定使用的模型,将自动更新到 config.toml 中", + "modelName": "模型名称", + "modelNamePlaceholder": "例如: gpt-5-codex" }, "geminiConfig": { "envFile": "环境变量 (.env)", @@ -944,7 +957,28 @@ "costBreakdown": "成本明细", "performance": "性能信息", "latency": "延迟", - "errorMessage": "错误信息" + "errorMessage": "错误信息", + "requests": "请求数", + "tokens": "Tokens", + "avgCost": "平均成本", + "avgLatency": "平均延迟", + "successRate": "成功率", + "requestId": "请求 ID", + "never": "从不", + "modelId": "模型 ID", + "modelIdRequired": "模型 ID 不能为空", + "inputCostPerMillion": "输入成本 (每百万 tokens, USD)", + "outputCostPerMillion": "输出成本 (每百万 tokens, USD)", + "invalidPrice": "价格必须为非负数", + "invalidTimeRange": "请选择完整的开始/结束时间", + "invalidTimeRangeOrder": "开始时间不能晚于结束时间", + "timeRangeTooLarge": "时间范围过大,请缩小范围", + "addPricing": "新增定价", + "editPricing": "编辑定价", + "pricingAdded": "定价已添加", + "pricingUpdated": "定价已更新", + "cacheReadCostPerMillion": "缓存读取成本 (每百万 tokens, USD)", + "cacheCreationCostPerMillion": "缓存写入成本 (每百万 tokens, USD)" }, "usageScript": { "title": "配置用量查询", @@ -1367,7 +1401,9 @@ "stringifiedEnvVars": "env.vars 应为对象,但当前值看起来像被字符串化或已损坏。", "stringifiedShellEnv": "env.shellEnv 应为对象,但当前值看起来像被字符串化或已损坏。", "parseFailed": "openclaw.json 不是合法 JSON5。请先修复文件,再通过这里编辑。" - } + }, + "primaryModel": "默认模型", + "fallbackModel": "回退模型" }, "env": { "warning": { @@ -1587,7 +1623,14 @@ "skillsPath": "Skills 路径", "hint": "此操作将添加 Skill 仓库到列表。", "hintDetail": "添加后,您可以在 Skills 管理界面中选择安装具体的 Skill。" - } + }, + "usageScript": "用量查询", + "usageScriptEnabled": "已启用", + "usageScriptDisabled": "未启用", + "usageApiKey": "用量 API Key", + "usageBaseUrl": "用量查询地址", + "usageAutoInterval": "自动查询", + "usageAutoIntervalValue": "每 {{minutes}} 分钟" }, "iconPicker": { "search": "搜索图标", @@ -1606,7 +1649,8 @@ "selectIcon": "选择图标", "preview": "预览", "clickToChange": "点击更换图标", - "clickToSelect": "点击选择图标" + "clickToSelect": "点击选择图标", + "color": "图标颜色" }, "migration": { "success": "配置迁移成功", @@ -1743,7 +1787,12 @@ "hint": "选择要接管的应用,启用后该应用的请求将通过本地代理转发", "enabled": "{{app}} 接管已启用", "disabled": "{{app}} 接管已关闭", - "failed": "切换接管状态失败" + "failed": "切换接管状态失败", + "tooltip": { + "active": "{{appLabel}} 已接管 - {{address}}:{{port}}\n切换该应用供应商为热切换", + "broken": "{{appLabel}} 已接管,但代理服务未运行", + "inactive": "接管 {{appLabel}} 的 Live 配置,让该应用请求走本地代理" + } }, "failover": { "proxyRequired": "需要先启动代理服务才能配置故障转移", @@ -1793,8 +1842,28 @@ "successThresholdLabel": "恢复成功阈值", "successThresholdExplain": "半开状态下,成功达到此次数时关闭熔断器,供应商恢复可用", "errorRateLabel": "错误率阈值", - "errorRateExplain": "错误率超过此值时,即使未达到失败阈值也会打开熔断器" - } + "errorRateExplain": "错误率超过此值时,即使未达到失败阈值也会打开熔断器", + "maxRetries": "最大重试次数", + "timeoutSettings": "超时配置", + "streamingFirstByte": "流式首字节超时", + "streamingIdle": "流式静默超时", + "nonStreaming": "非流式超时", + "maxRetriesHint": "请求失败时的重试次数(0-10)", + "streamingFirstByteHint": "等待首个数据块的最大时间,范围 1-120 秒,默认 60 秒", + "streamingIdleHint": "数据块之间的最大间隔,范围 60-600 秒,填 0 禁用(防止中途卡住)", + "nonStreamingHint": "非流式请求的总超时时间,范围 60-1200 秒,默认 600 秒(10 分钟)" + }, + "logging": { + "enabled": "日志记录已启用", + "disabled": "日志记录已关闭", + "failed": "切换日志状态失败" + }, + "server": { + "started": "代理服务已启动 - {{address}}:{{port}}", + "startFailed": "启动代理服务失败: {{detail}}" + }, + "stoppedWithRestore": "代理服务已关闭,已恢复所有接管配置", + "stopWithRestoreFailed": "停止失败: {{detail}}" }, "streamCheck": { "configSaved": "健康检查配置已保存", @@ -1807,7 +1876,11 @@ "timeout": "超时时间(秒)", "maxRetries": "最大重试次数", "degradedThreshold": "降级阈值(毫秒)", - "testPrompt": "检查提示词" + "testPrompt": "检查提示词", + "operational": "{{providerName}} 运行正常 ({{responseTimeMs}}ms)", + "degraded": "{{providerName}} 响应较慢 ({{responseTimeMs}}ms)", + "failed": "{{providerName}} 检查失败: {{message}}", + "error": "{{providerName}} 检查出错: {{error}}" }, "proxyConfig": { "proxyEnabled": "代理总开关", @@ -1828,12 +1901,29 @@ "circuitBreaker": { "failureThreshold": "失败阈值", "successThreshold": "成功阈值", - "timeoutSeconds": "超时时间", - "errorRateThreshold": "错误率阈值", + "timeoutSeconds": "超时时间(秒)", + "errorRateThreshold": "错误率阈值 (%)", "minRequests": "最小请求数", "validationFailed": "以下字段超出有效范围: {{fields}}", "configSaved": "熔断器配置已保存", - "saveFailed": "保存失败" + "saveFailed": "保存失败", + "loading": "加载中...", + "title": "熔断器配置", + "description": "调整熔断器参数以控制故障检测和恢复行为", + "failureThresholdHint": "连续失败多少次后打开熔断器", + "timeoutSecondsHint": "熔断器打开后多久尝试恢复(半开状态)", + "successThresholdHint": "半开状态下成功多少次后关闭熔断器", + "errorRateThresholdHint": "错误率超过此值时打开熔断器", + "minRequestsHint": "计算错误率前的最小请求数", + "saveConfig": "保存配置", + "instructionsTitle": "配置说明", + "instructions": { + "failureThreshold": "连续失败达到此次数时,熔断器打开", + "timeout": "熔断器打开后,等待此时间后尝试半开", + "successThreshold": "半开状态下,成功达到此次数时关闭熔断器", + "errorRate": "错误率超过此值时,熔断器打开", + "minRequests": "只有请求数达到此值后才计算错误率" + } }, "universalProvider": { "title": "统一供应商",