From eb6948a5624e600cece710cb6353504424cbaff1 Mon Sep 17 00:00:00 2001 From: Jason Date: Sun, 19 Oct 2025 11:55:46 +0800 Subject: [PATCH] i18n: complete internationalization for provider and usage query panels - Add 45+ new translation keys for usage query and usage script features - Fix duplicate provider object in translation files that caused missing translations - Remove all hardcoded Chinese text and defaultValue fallbacks from components - Add proper translations for: * Usage footer (query status, plan usage display) * Usage script modal (script editor, validation, test controls) * Provider forms (basic fields, endpoints, model selectors) * Provider dialogs (add/edit hints and titles) Modified 16 files: - 2 translation files (zh.json, en.json) - 14 component files (removed defaultValue, added t() calls) All UI text now properly supports Chinese/English switching. --- src/App.tsx | 5 +- src/components/UsageFooter.tsx | 19 ++-- src/components/UsageScriptModal.tsx | 103 +++++++----------- .../providers/AddProviderDialog.tsx | 14 +-- .../providers/EditProviderDialog.tsx | 12 +- src/components/providers/ProviderActions.tsx | 8 +- src/components/providers/ProviderCard.tsx | 4 +- .../providers/ProviderEmptyState.tsx | 8 +- .../providers/forms/BasicFormFields.tsx | 8 +- .../providers/forms/ClaudeFormFields.tsx | 10 +- .../providers/forms/CodexFormFields.tsx | 10 +- .../providers/forms/CommonConfigEditor.tsx | 2 +- .../providers/forms/ProviderForm.tsx | 2 +- .../forms/ProviderPresetSelector.tsx | 4 +- src/i18n/locales/en.json | 46 +++++++- src/i18n/locales/zh.json | 46 +++++++- 16 files changed, 176 insertions(+), 125 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 25835f1..5624347 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -142,7 +142,7 @@ function App() { @@ -198,12 +198,11 @@ function App() { = ({ appType, usageEnabled, }) => { + const { t } = useTranslation(); const { data: usage, isLoading: loading, @@ -31,7 +33,7 @@ const UsageFooter: React.FC = ({
- {usage.error || "查询失败"} + {usage.error || t("usage.queryFailed")}
{/* 刷新按钮 */} @@ -39,7 +41,7 @@ const UsageFooter: React.FC = ({ onClick={() => refetch()} disabled={loading} className="p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors disabled:opacity-50 flex-shrink-0" - title="刷新用量" + title={t("usage.refreshUsage")} > @@ -58,13 +60,13 @@ const UsageFooter: React.FC = ({ {/* 标题行:包含刷新按钮 */}
- 套餐用量 + {t("usage.planUsage")} @@ -82,6 +84,7 @@ const UsageFooter: React.FC = ({ // 单个套餐数据展示组件 const UsagePlanItem: React.FC<{ data: UsageData }> = ({ data }) => { + const { t } = useTranslation(); const { planName, extra, @@ -130,7 +133,7 @@ const UsagePlanItem: React.FC<{ data: UsageData }> = ({ data }) => { )} {isExpired && ( - {invalidMessage || "已失效"} + {invalidMessage || t("usage.invalid")} )}
@@ -143,7 +146,7 @@ const UsagePlanItem: React.FC<{ data: UsageData }> = ({ data }) => { {/* 总额度 */} {total !== undefined && ( <> - 总: + {t("usage.total")} {total === -1 ? "∞" : total.toFixed(2)} @@ -154,7 +157,7 @@ const UsagePlanItem: React.FC<{ data: UsageData }> = ({ data }) => { {/* 已用额度 */} {used !== undefined && ( <> - 使用: + {t("usage.used")} {used.toFixed(2)} @@ -165,7 +168,7 @@ const UsagePlanItem: React.FC<{ data: UsageData }> = ({ data }) => { {/* 剩余额度 - 突出显示 */} {remaining !== undefined && ( <> - 剩余: + {t("usage.remaining")} = ({ onClose, onSave, }) => { + const { t } = useTranslation(); const [script, setScript] = useState(() => { return ( provider.meta?.usage_script || { enabled: false, language: "javascript", - code: PRESET_TEMPLATES["通用模板"], + code: PRESET_TEMPLATES[t("usageScript.presetTemplate") === "预设模板" ? "通用模板" : "General"], timeout: 10, } ); @@ -104,13 +106,13 @@ const UsageScriptModal: React.FC = ({ const handleSave = () => { // 验证脚本格式 if (script.enabled && !script.code.trim()) { - toast.error("脚本配置不能为空"); + toast.error(t("usageScript.scriptEmpty")); return; } // 基本的 JS 语法检查(检查是否包含 return 语句) if (script.enabled && !script.code.includes("return")) { - toast.error("脚本必须包含 return 语句", { duration: 5000 }); + toast.error(t("usageScript.mustHaveReturn"), { duration: 5000 }); return; } @@ -127,17 +129,17 @@ const UsageScriptModal: React.FC = ({ const summary = result.data .map((plan) => { const planInfo = plan.planName ? `[${plan.planName}]` : ""; - return `${planInfo} 剩余: ${plan.remaining} ${plan.unit}`; + return `${planInfo} ${t("usage.remaining")} ${plan.remaining} ${plan.unit}`; }) .join(", "); - toast.success(`测试成功!${summary}`, { duration: 3000 }); + toast.success(`${t("usageScript.testSuccess")}${summary}`, { duration: 3000 }); } else { - toast.error(`测试失败: ${result.error || "无数据返回"}`, { + toast.error(`${t("usageScript.testFailed")}: ${result.error || t("endpointTest.noResult")}`, { duration: 5000, }); } } catch (error: any) { - toast.error(`测试失败: ${error?.message || "未知错误"}`, { + toast.error(`${t("usageScript.testFailed")}: ${error?.message || t("common.unknown")}`, { duration: 5000, }); } finally { @@ -156,9 +158,9 @@ const UsageScriptModal: React.FC = ({ printWidth: 80, }); setScript({ ...script, code: formatted.trim() }); - toast.success("格式化成功", { duration: 1000 }); + toast.success(t("usageScript.formatSuccess"), { duration: 1000 }); } catch (error: any) { - toast.error(`格式化失败: ${error?.message || "语法错误"}`, { + toast.error(`${t("usageScript.formatFailed")}: ${error?.message || t("jsonEditor.invalidJson")}`, { duration: 3000, }); } @@ -175,7 +177,7 @@ const UsageScriptModal: React.FC = ({ !open && onClose()}> - 配置用量查询 - {provider.name} + {t("usageScript.title")} - {provider.name} {/* Content - Scrollable */} @@ -191,7 +193,7 @@ const UsageScriptModal: React.FC = ({ className="w-4 h-4" /> - 启用用量查询 + {t("usageScript.enableUsageQuery")} @@ -200,7 +202,7 @@ const UsageScriptModal: React.FC = ({ {/* 预设模板选择 */}
{Object.keys(PRESET_TEMPLATES).map((name) => ( @@ -218,7 +220,7 @@ const UsageScriptModal: React.FC = ({ {/* 脚本编辑器 */}
= ({ language="javascript" />

- 支持变量: {"{{apiKey}}"},{" "} - {"{{baseUrl}}"} | extractor 函数接收 API 响应的 - JSON 对象 + {t("usageScript.variablesHint", { + apiKey: "{{apiKey}}", + baseUrl: "{{baseUrl}}" + })}

@@ -237,7 +240,7 @@ const UsageScriptModal: React.FC = ({
diff --git a/src/components/providers/forms/BasicFormFields.tsx b/src/components/providers/forms/BasicFormFields.tsx index 6b0da5c..ae0341f 100644 --- a/src/components/providers/forms/BasicFormFields.tsx +++ b/src/components/providers/forms/BasicFormFields.tsx @@ -25,14 +25,12 @@ export function BasicFormFields({ form }: BasicFormFieldsProps) { render={({ field }) => ( - {t("provider.name", { defaultValue: "供应商名称" })} + {t("provider.name")} @@ -46,7 +44,7 @@ export function BasicFormFields({ form }: BasicFormFieldsProps) { render={({ field }) => ( - {t("provider.websiteUrl", { defaultValue: "官网链接" })} + {t("provider.websiteUrl")} diff --git a/src/components/providers/forms/ClaudeFormFields.tsx b/src/components/providers/forms/ClaudeFormFields.tsx index 075a2cb..a4878ec 100644 --- a/src/components/providers/forms/ClaudeFormFields.tsx +++ b/src/components/providers/forms/ClaudeFormFields.tsx @@ -137,15 +137,11 @@ export function ClaudeFormFields({ {shouldShowSpeedTest && ( onEndpointModalToggle(true)} /> )} diff --git a/src/components/providers/forms/CodexFormFields.tsx b/src/components/providers/forms/CodexFormFields.tsx index 21f2852..5a9bb6c 100644 --- a/src/components/providers/forms/CodexFormFields.tsx +++ b/src/components/providers/forms/CodexFormFields.tsx @@ -68,15 +68,11 @@ export function CodexFormFields({ {shouldShowSpeedTest && ( onEndpointModalToggle(true)} /> )} diff --git a/src/components/providers/forms/CommonConfigEditor.tsx b/src/components/providers/forms/CommonConfigEditor.tsx index b72b70a..ccbdc21 100644 --- a/src/components/providers/forms/CommonConfigEditor.tsx +++ b/src/components/providers/forms/CommonConfigEditor.tsx @@ -51,7 +51,7 @@ export function CommonConfigEditor({