- i18n: complete remaining internationalization across the UI
- Locales: add and align keys (common.enterValidValue, apiKeyInput.*, jsonEditor.*, claudeConfig.*); fix zh common.unknown mapping - ProviderForm: localize labels/placeholders/hints/errors; unify JSON/auth validation to providerForm.*; add wizard CTA for Codex custom with i18n; cancel uses common.cancel - CodexConfigEditor: i18n for quick wizard, labels/placeholders/hints, common config modal (title/help/buttons) - ClaudeConfigEditor: i18n for main label, common-config toggle/button, modal title/help, footer buttons - EndpointSpeedTest: localize failed/noEndpoints/done and aria labels - ApiKeyInput: i18n for placeholder and show/hide aria - JsonEditor: i18n linter messages - PresetSelector: remove hardcoded defaults, use i18n keys - UpdateBadge: i18n close aria - Build/typecheck: pass; scan shows no visible hardcoded Chinese strings outside locales
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Zap, Loader2, Plus, X, AlertCircle } from "lucide-react";
|
||||
import { isLinux } from "../../lib/platform";
|
||||
|
||||
@@ -74,6 +75,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
||||
onClose,
|
||||
onCustomEndpointsChange,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [entries, setEntries] = useState<EndpointEntry[]>(() =>
|
||||
buildInitialEntries(initialEndpoints, value),
|
||||
);
|
||||
@@ -127,14 +129,14 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
||||
return Array.from(map.values());
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("加载自定义端点失败:", error);
|
||||
console.error(t("endpointTest.loadEndpointsFailed"), error);
|
||||
}
|
||||
};
|
||||
|
||||
if (visible) {
|
||||
loadCustomEndpoints();
|
||||
}
|
||||
}, [appType, visible, providerId]);
|
||||
}, [appType, visible, providerId, t]);
|
||||
|
||||
useEffect(() => {
|
||||
setEntries((prev) => {
|
||||
@@ -214,7 +216,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
||||
let errorMsg: string | null = null;
|
||||
|
||||
if (!candidate) {
|
||||
errorMsg = "请输入有效的 URL";
|
||||
errorMsg = t("endpointTest.enterValidUrl");
|
||||
}
|
||||
|
||||
let parsed: URL | null = null;
|
||||
@@ -222,12 +224,12 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
||||
try {
|
||||
parsed = new URL(candidate);
|
||||
} catch {
|
||||
errorMsg = "URL 格式不正确";
|
||||
errorMsg = t("endpointTest.invalidUrlFormat");
|
||||
}
|
||||
}
|
||||
|
||||
if (!errorMsg && parsed && !parsed.protocol.startsWith("http")) {
|
||||
errorMsg = "仅支持 HTTP/HTTPS";
|
||||
errorMsg = t("endpointTest.onlyHttps");
|
||||
}
|
||||
|
||||
let sanitized = "";
|
||||
@@ -236,7 +238,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
||||
// 使用当前 entries 做去重校验,避免依赖可能过期的 addError
|
||||
const isDuplicate = entries.some((entry) => entry.url === sanitized);
|
||||
if (isDuplicate) {
|
||||
errorMsg = "该地址已存在";
|
||||
errorMsg = t("endpointTest.urlExists");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,11 +279,11 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
||||
} catch (error) {
|
||||
const message =
|
||||
error instanceof Error ? error.message : String(error);
|
||||
setAddError(message || "保存失败,请重试");
|
||||
console.error("添加自定义端点失败:", error);
|
||||
setAddError(message || t("endpointTest.saveFailed"));
|
||||
console.error(t("endpointTest.addEndpointFailed"), error);
|
||||
}
|
||||
},
|
||||
[customUrl, entries, normalizedSelected, onChange, appType, providerId],
|
||||
[customUrl, entries, normalizedSelected, onChange, appType, providerId, t],
|
||||
);
|
||||
|
||||
const handleRemoveEndpoint = useCallback(
|
||||
@@ -291,7 +293,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
||||
try {
|
||||
await window.api.removeCustomEndpoint(appType, providerId, entry.url);
|
||||
} catch (error) {
|
||||
console.error("删除自定义端点失败:", error);
|
||||
console.error(t("endpointTest.removeEndpointFailed"), error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -306,18 +308,18 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
||||
return next;
|
||||
});
|
||||
},
|
||||
[normalizedSelected, onChange, appType, providerId],
|
||||
[normalizedSelected, onChange, appType, providerId, t],
|
||||
);
|
||||
|
||||
const runSpeedTest = useCallback(async () => {
|
||||
const urls = entries.map((entry) => entry.url);
|
||||
if (urls.length === 0) {
|
||||
setLastError("请先添加端点");
|
||||
setLastError(t("endpointTest.pleaseAddEndpoint"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof window === "undefined" || !window.api?.testApiEndpoints) {
|
||||
setLastError("测速功能不可用");
|
||||
setLastError(t("endpointTest.testUnavailable"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -350,7 +352,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
||||
...entry,
|
||||
latency: null,
|
||||
status: undefined,
|
||||
error: "未返回结果",
|
||||
error: t("endpointTest.noResult"),
|
||||
};
|
||||
}
|
||||
return {
|
||||
@@ -374,12 +376,12 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
||||
}
|
||||
} catch (error) {
|
||||
const message =
|
||||
error instanceof Error ? error.message : `测速失败: ${String(error)}`;
|
||||
error instanceof Error ? error.message : `${t("endpointTest.testFailed", { error: String(error) })}`;
|
||||
setLastError(message);
|
||||
} finally {
|
||||
setIsTesting(false);
|
||||
}
|
||||
}, [entries, autoSelect, appType, normalizedSelected, onChange]);
|
||||
}, [entries, autoSelect, appType, normalizedSelected, onChange, t]);
|
||||
|
||||
const handleSelect = useCallback(
|
||||
async (url: string) => {
|
||||
@@ -431,13 +433,13 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-6 border-b border-gray-200 dark:border-gray-800">
|
||||
<h3 className="text-base font-medium text-gray-900 dark:text-gray-100">
|
||||
请求地址管理
|
||||
{t("endpointTest.title")}
|
||||
</h3>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="p-1 text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-md transition-colors"
|
||||
aria-label="关闭"
|
||||
aria-label={t("common.close")}
|
||||
>
|
||||
<X size={16} />
|
||||
</button>
|
||||
@@ -448,7 +450,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
||||
{/* 测速控制栏 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="text-sm text-gray-600 dark:text-gray-400">
|
||||
{entries.length} 个端点
|
||||
{entries.length} {t("endpointTest.endpoints")}
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<label className="flex items-center gap-1.5 text-xs text-gray-600 dark:text-gray-400">
|
||||
@@ -458,7 +460,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
||||
onChange={(event) => setAutoSelect(event.target.checked)}
|
||||
className="h-3.5 w-3.5 rounded border-gray-300 dark:border-gray-600"
|
||||
/>
|
||||
自动选择
|
||||
{t("endpointTest.autoSelect")}
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
@@ -469,12 +471,12 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
||||
{isTesting ? (
|
||||
<>
|
||||
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
||||
测速中
|
||||
{t("endpointTest.testing")}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Zap className="h-3.5 w-3.5" />
|
||||
测速
|
||||
{t("endpointTest.testSpeed")}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
@@ -487,7 +489,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
||||
<input
|
||||
type="url"
|
||||
value={customUrl}
|
||||
placeholder="https://api.example.com"
|
||||
placeholder={t("endpointTest.addEndpointPlaceholder")}
|
||||
onChange={(event) => setCustomUrl(event.target.value)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter") {
|
||||
@@ -567,7 +569,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
||||
) : isTesting ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin text-gray-400" />
|
||||
) : entry.error ? (
|
||||
<div className="text-xs text-gray-400">失败</div>
|
||||
<div className="text-xs text-gray-400">{t("endpointTest.failed")}</div>
|
||||
) : (
|
||||
<div className="text-xs text-gray-400">—</div>
|
||||
)}
|
||||
@@ -589,7 +591,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-md border border-dashed border-gray-200 bg-gray-50 py-8 text-center text-xs text-gray-500 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-400">
|
||||
暂无端点
|
||||
{t("endpointTest.noEndpoints")}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -609,7 +611,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 bg-blue-500 dark:bg-blue-600 text-white rounded-lg hover:bg-blue-600 dark:hover:bg-blue-700 transition-colors text-sm font-medium"
|
||||
>
|
||||
完成
|
||||
{t("endpointTest.done")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user