i18n: complete internationalization for v3.8+ features

- Add health status translations (operational, degraded, failed, circuitOpen)
- Add proxy panel translations (serviceAddress, stats, stopped state)
- Add usage filter translations (appType, statusCode, searchPlaceholder)
- Add providerIcon click hints (clickToChange, clickToSelect)
- Add config load error translations for main.tsx
- Complete Japanese proxy section (failoverQueue, autoFailover)
- Fix date/time locale in usage charts and tables
- Use t() function in all hardcoded UI strings
This commit is contained in:
Jason
2025-12-20 21:38:37 +08:00
parent ec649e7718
commit 2fb3b5405a
17 changed files with 373 additions and 82 deletions
+6 -2
View File
@@ -106,7 +106,9 @@ export function PricingEditModal({
onChange={(e) =>
setFormData({ ...formData, modelId: e.target.value })
}
placeholder="例如: claude-3-5-sonnet-20241022"
placeholder={t("usage.modelIdPlaceholder", {
defaultValue: "例如: claude-3-5-sonnet-20241022",
})}
required
/>
</div>
@@ -122,7 +124,9 @@ export function PricingEditModal({
onChange={(e) =>
setFormData({ ...formData, displayName: e.target.value })
}
placeholder="例如: Claude 3.5 Sonnet"
placeholder={t("usage.displayNamePlaceholder", {
defaultValue: "例如: Claude 3.5 Sonnet",
})}
required
/>
</div>
+10 -2
View File
@@ -16,8 +16,14 @@ export function RequestDetailPanel({
requestId,
onClose,
}: RequestDetailPanelProps) {
const { t } = useTranslation();
const { t, i18n } = useTranslation();
const { data: request, isLoading } = useRequestDetail(requestId);
const dateLocale =
i18n.language === "zh"
? "zh-CN"
: i18n.language === "ja"
? "ja-JP"
: "en-US";
if (isLoading) {
return (
@@ -69,7 +75,9 @@ export function RequestDetailPanel({
{t("usage.time", "时间")}
</dt>
<dd>
{new Date(request.createdAt * 1000).toLocaleString("zh-CN")}
{new Date(request.createdAt * 1000).toLocaleString(
dateLocale,
)}
</dd>
</div>
<div>
+24 -8
View File
@@ -23,7 +23,7 @@ import type { LogFilters } from "@/types/usage";
import { ChevronLeft, ChevronRight, RefreshCw, Search, X } from "lucide-react";
export function RequestLogTable() {
const { t } = useTranslation();
const { t, i18n } = useTranslation();
const queryClient = useQueryClient();
// 默认时间范围:过去24小时
@@ -62,6 +62,13 @@ export function RequestLogTable() {
});
};
const dateLocale =
i18n.language === "zh"
? "zh-CN"
: i18n.language === "ja"
? "ja-JP"
: "en-US";
return (
<div className="space-y-4">
{/* 筛选栏 */}
@@ -77,10 +84,12 @@ export function RequestLogTable() {
}
>
<SelectTrigger className="w-[130px] bg-background">
<SelectValue placeholder={t("usage.endpoint", "端点")} />
<SelectValue placeholder={t("usage.appType", "应用类型")} />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">{t("common.all", "全部端点")}</SelectItem>
<SelectItem value="all">
{t("usage.allApps", { defaultValue: "全部应用" })}
</SelectItem>
<SelectItem value="claude">Claude</SelectItem>
<SelectItem value="codex">Codex</SelectItem>
<SelectItem value="gemini">Gemini</SelectItem>
@@ -97,7 +106,7 @@ export function RequestLogTable() {
}
>
<SelectTrigger className="w-[130px] bg-background">
<SelectValue placeholder={t("usage.status", "状态码")} />
<SelectValue placeholder={t("usage.statusCode", "状态码")} />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">{t("common.all", "全部状态")}</SelectItem>
@@ -113,7 +122,10 @@ export function RequestLogTable() {
<div className="relative flex-1">
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
placeholder={t("usage.provider", "搜索供应商...")}
placeholder={t(
"usage.searchProviderPlaceholder",
"搜索供应商...",
)}
className="pl-9 bg-background"
value={tempFilters.providerName || ""}
onChange={(e) =>
@@ -125,7 +137,7 @@ export function RequestLogTable() {
/>
</div>
<Input
placeholder={t("usage.model", "搜索模型...")}
placeholder={t("usage.searchModelPlaceholder", "搜索模型...")}
className="w-[180px] bg-background"
value={tempFilters.model || ""}
onChange={(e) =>
@@ -140,7 +152,9 @@ export function RequestLogTable() {
<div className="flex flex-wrap items-center justify-between gap-3">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<span className="whitespace-nowrap">:</span>
<span className="whitespace-nowrap">
{t("usage.timeRange", { defaultValue: "时间范围" })}:
</span>
<Input
type="datetime-local"
className="h-8 w-[200px] bg-background"
@@ -267,7 +281,9 @@ export function RequestLogTable() {
logs.map((log) => (
<TableRow key={log.requestId}>
<TableCell>
{new Date(log.createdAt * 1000).toLocaleString("zh-CN")}
{new Date(log.createdAt * 1000).toLocaleString(
dateLocale,
)}
</TableCell>
<TableCell>
{log.providerName ||
+17 -17
View File
@@ -56,15 +56,15 @@ export function UsageSummaryCards({ days }: UsageSummaryCardsProps) {
color: "text-purple-500",
bg: "bg-purple-500/10",
subValue: (
<div className="flex flex-col gap-1 text-xs text-muted-foreground mt-3 pt-3 border-t border-border/50">
<div className="flex flex-col gap-1 text-xs text-muted-foreground mt-3 pt-3 border-t border-border/50">
<div className="flex justify-between items-center">
<span>Input</span>
<span>{t("usage.input", { defaultValue: "Input" })}</span>
<span className="text-foreground/80">
{(inputTokens / 1000).toFixed(1)}k
</span>
</div>
<div className="flex justify-between items-center">
<span>Output</span>
<span>{t("usage.output", { defaultValue: "Output" })}</span>
<span className="text-foreground/80">
{(outputTokens / 1000).toFixed(1)}k
</span>
@@ -78,21 +78,21 @@ export function UsageSummaryCards({ days }: UsageSummaryCardsProps) {
icon: Database,
color: "text-orange-500",
bg: "bg-orange-500/10",
subValue: (
<div className="flex flex-col gap-1 text-xs text-muted-foreground mt-3 pt-3 border-t border-border/50">
<div className="flex justify-between items-center">
<span>Write</span>
<span className="text-foreground/80">
{(cacheWriteTokens / 1000).toFixed(1)}k
</span>
subValue: (
<div className="flex flex-col gap-1 text-xs text-muted-foreground mt-3 pt-3 border-t border-border/50">
<div className="flex justify-between items-center">
<span>{t("usage.cacheWrite", { defaultValue: "Write" })}</span>
<span className="text-foreground/80">
{(cacheWriteTokens / 1000).toFixed(1)}k
</span>
</div>
<div className="flex justify-between items-center">
<span>{t("usage.cacheRead", { defaultValue: "Read" })}</span>
<span className="text-foreground/80">
{(cacheReadTokens / 1000).toFixed(1)}k
</span>
</div>
</div>
<div className="flex justify-between items-center">
<span>Read</span>
<span className="text-foreground/80">
{(cacheReadTokens / 1000).toFixed(1)}k
</span>
</div>
</div>
),
},
];
+9 -3
View File
@@ -17,7 +17,7 @@ interface UsageTrendChartProps {
}
export function UsageTrendChart({ days }: UsageTrendChartProps) {
const { t } = useTranslation();
const { t, i18n } = useTranslation();
const { data: trends, isLoading } = useUsageTrends(days);
if (isLoading) {
@@ -29,14 +29,20 @@ export function UsageTrendChart({ days }: UsageTrendChartProps) {
}
const isToday = days === 1;
const dateLocale =
i18n.language === "zh"
? "zh-CN"
: i18n.language === "ja"
? "ja-JP"
: "en-US";
const chartData =
trends?.map((stat) => {
const pointDate = new Date(stat.date);
return {
rawDate: stat.date,
label: isToday
? pointDate.toLocaleTimeString("zh-CN", { hour: "2-digit" })
: pointDate.toLocaleDateString("zh-CN", {
? pointDate.toLocaleTimeString(dateLocale, { hour: "2-digit" })
: pointDate.toLocaleDateString(dateLocale, {
month: "2-digit",
day: "2-digit",
}),