feat(i18n): 安全批量转换 135 文件 — 覆盖率 34%→62%

使用 i18n.t() 全局方式(避免 hook 定位问题):
- 288 目标文件中 135 个成功转换
- 2228 处中文替换为 i18n.t() 调用
- 1006 个新翻译 key 添加到 zh-CN.json + en-US.json
- 词典总量: 3554 keys (22 namespaces)
- tsc 零错误 + Vite 构建成功
- 6 个含代码示例/数据字符串的文件安全回退
This commit is contained in:
attacker committed 2026-07-03 02:14:20 +08:00
1 parent 72bfb0e12d
commit 55f216cfbb
131 files changed
+4049 -1908

No files matched your search

@@ -5,6 +5,7 @@ import { TrendingUp, Loader2, AlertCircle } from 'lucide-react';
import { LineChart, Line, ResponsiveContainer, XAxis, YAxis, Tooltip, CartesianGrid } from 'recharts';
import type { Panel } from './GrafanaDashboard';
import { prometheusProxyApi, promDatasourceApi } from '@/api';
import i18n from '@/i18n';
// 默认 Prometheus URL 缓存
let _defaultPromURL: string | null = null;
@@ -82,7 +83,7 @@ export function GrafanaPanelContent({ panel, datasourceUrl }: { panel: Panel; da
try {
const url = await getDefaultPromURL(datasourceUrl);
if (!url) {
setError('未配置 Prometheus 数据源');
setError(i18n.t('monitor.dashboard.prometheus.emptyTitle'));
setLoading(false);
return;
}
@@ -100,16 +101,16 @@ export function GrafanaPanelContent({ panel, datasourceUrl }: { panel: Panel; da
});
const parsed = parseRangeResult(res);
setChartData(parsed.length > 0 ? parsed : []);
if (parsed.length === 0) setError('无数据');
if (parsed.length === 0) setError(i18n.t('kubernetes.configmap.noData'));
} else {
// stat / gauge / table 用即时查询
const res = await prometheusProxyApi.query(url, panel.query);
const parsed = parseInstantResult(res);
setInstantData(parsed);
if (parsed.length === 0) setError('无数据');
if (parsed.length === 0) setError(i18n.t('kubernetes.configmap.noData'));
}
} catch (e: any) {
setError(e?.message ? String(e.message).slice(0, 80) : '查询失败');
setError(e?.message ? String(e.message).slice(0, 80) : i18n.t('monitor.logPanel.queryFailed'));
} finally {
setLoading(false);
}
@@ -153,7 +154,7 @@ export function GrafanaPanelContent({ panel, datasourceUrl }: { panel: Panel; da
{unit && <span className="text-sm text-[var(--dg-text-muted)] mt-2">{unit}</span>}
<div className="flex items-center gap-1 mt-2 dg-text-green text-sm">
<TrendingUp className="w-4 h-4" />
<span>实时</span>
<span> {i18n.t('monitor.hostDashboard.realtime')} </span>
</div>
</div>
);
@@ -187,7 +188,7 @@ export function GrafanaPanelContent({ panel, datasourceUrl }: { panel: Panel; da
);
}
case 'table': {
if (instantData.length === 0) return <div className="text-center py-4 text-xs text-[var(--dg-text-muted)]">无数据</div>;
if (instantData.length === 0) return <div className="text-center py-4 text-xs text-[var(--dg-text-muted)]"> {i18n.t('kubernetes.configmap.noData')} </div>;
return (
<div className="overflow-auto max-h-[200px]">
<table className="w-full text-[11px]">
@@ -216,7 +217,7 @@ export function GrafanaPanelContent({ panel, datasourceUrl }: { panel: Panel; da
case 'graph':
default: {
if (chartData.length === 0) {
return <div className="text-center py-4 text-xs text-[var(--dg-text-muted)]">无数据</div>;
return <div className="text-center py-4 text-xs text-[var(--dg-text-muted)]"> {i18n.t('kubernetes.configmap.noData')} </div>;
}
return (
<div className="h-full min-h-[120px]">
@@ -8,6 +8,7 @@ import { StatStrip } from '@/components/ui/stat-strip';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { prometheusProxyApi } from '@/api';
import type { JsonData } from '@/types/common';
import i18n from '@/i18n';
interface Props { dsUrl?: string; }
@@ -58,7 +59,7 @@ const K8sPanel: React.FC<{ d: JsonData}> = ({ d }) => (
]}
/>
<div className="rounded-lg bg-[var(--dg-bg-primary)] border border-[var(--dg-border)] p-3 max-h-60 overflow-y-auto">
<div className="text-xs font-medium mb-2 flex items-center gap-1"><Cpu className="w-3 h-3 dg-text-purple" />节点 CPU / 内存利用率</div>
<div className="text-xs font-medium mb-2 flex items-center gap-1"><Cpu className="w-3 h-3 dg-text-purple" /> {i18n.t('monitor.prometheusdashb.se7fb2b41')} </div>
{(d.nodeCPU || []).map((n: JsonData, i: number) => {
const mem = (d.nodeMem || []).find((m: JsonData) => m.node === n.node);
return <div key={i} className="flex items-center gap-3 text-[11px] mb-1.5"><span className="w-28 truncate" title={n.node}>{n.node}</span><MiniBar val={Number(n.value||0)} label={`${Number(n.value||0).toFixed(1)}%`} /><MiniBar val={Number(mem?.value||0)} label={`${Number(mem?.value||0).toFixed(1)}%`} color="bg-blue-500" /></div>;
@@ -70,7 +71,7 @@ const K8sPanel: React.FC<{ d: JsonData}> = ({ d }) => (
const NodePanel: React.FC<{ d: JsonData}> = ({ d }) => (
<div className="overflow-x-auto rounded-lg border border-[var(--dg-border)]">
<table className="w-full text-xs">
<thead className="bg-[var(--dg-bg-tertiary)]"><tr><th className="px-3 py-2 text-left">节点</th><th className="px-3 py-2 text-left">状态</th><th className="px-3 py-2 text-left">CPU</th><th className="px-3 py-2 text-left">内存</th><th className="px-3 py-2 text-left">磁盘</th><th className="px-3 py-2 text-left">流入</th><th className="px-3 py-2 text-left">流出</th></tr></thead>
<thead className="bg-[var(--dg-bg-tertiary)]"><tr><th className="px-3 py-2 text-left"> {i18n.t('kubernetes.node.title')} </th><th className="px-3 py-2 text-left"> {i18n.t('system.terminalStatus')} </th><th className="px-3 py-2 text-left">CPU</th><th className="px-3 py-2 text-left"> {i18n.t('kubernetes.pod.memory')} </th><th className="px-3 py-2 text-left"> {i18n.t('monitor.dashboard.col.disk')} </th><th className="px-3 py-2 text-left"> {i18n.t('monitor.prometheusdashb.s361ffa7f')} </th><th className="px-3 py-2 text-left"> {i18n.t('monitor.prometheusdashb.s37cc0a9a')} </th></tr></thead>
<tbody>
{(d.nodes || []).map((n: JsonData, i: number) => {
const inst = n.instance || n.exported_instance || '';
@@ -104,7 +105,7 @@ const PodPanel: React.FC<{ d: JsonData}> = ({ d }) => (
]}
/>
<div className="rounded-lg bg-[var(--dg-bg-primary)] p-3 max-h-40 overflow-y-auto"><div className="text-xs font-medium mb-2">CPU Top 10</div>{(d.cpuTop||[]).map((p: JsonData, i:number)=><div key={i} className="flex items-center gap-2 text-[11px] mb-1"><span className="w-10 text-right text-[var(--dg-text-muted)]">{i+1}</span><span className="w-32 truncate" title={p.pod}>{p.pod}</span><MiniBar val={Number(p.value||0)*100} label={`${Number(p.value||0).toFixed(2)}c`} color="bg-purple-500" /></div>)}</div>
<div className="rounded-lg bg-[var(--dg-bg-primary)] p-3 max-h-40 overflow-y-auto"><div className="text-xs font-medium mb-2">内存 Top 10</div>{(d.memTop||[]).map((p: JsonData, i:number)=><div key={i} className="flex items-center gap-2 text-[11px] mb-1"><span className="w-10 text-right text-[var(--dg-text-muted)]">{i+1}</span><span className="w-32 truncate" title={p.pod}>{p.pod}</span><span className="ml-auto">{fmtBytes(Number(p.value||0))}</span></div>)}</div>
<div className="rounded-lg bg-[var(--dg-bg-primary)] p-3 max-h-40 overflow-y-auto"><div className="text-xs font-medium mb-2"> {i18n.t('monitor.prometheusdashb.sb0f653eb')} </div>{(d.memTop||[]).map((p: JsonData, i:number)=><div key={i} className="flex items-center gap-2 text-[11px] mb-1"><span className="w-10 text-right text-[var(--dg-text-muted)]">{i+1}</span><span className="w-32 truncate" title={p.pod}>{p.pod}</span><span className="ml-auto">{fmtBytes(Number(p.value||0))}</span></div>)}</div>
</div>
);
@@ -143,7 +144,7 @@ export const PrometheusDashboard: React.FC<Props> = ({ dsUrl }) => {
const data: JsonData = await prometheusProxyApi.connect(u);
setConnected(true); setTargets(data.targets||0);
loadAll(u);
} catch (e) { setConnected(false); setError((e as JsonData).message||'连接失败'); }
} catch (e) { setConnected(false); setError((e as JsonData).message||i18n.t('aiops.llmTestFailed')); }
finally { setLoading(false); }
};
@@ -171,11 +172,11 @@ export const PrometheusDashboard: React.FC<Props> = ({ dsUrl }) => {
<Activity className="h-4 w-4 dg-text-purple" />
<CardTitle className="text-sm font-medium">Prometheus 监控</CardTitle>
{connected && <Badge className="text-[10px] bg-purple-500/10 dg-text-purple">{targets} targets</Badge>}
{connected && <div className="ml-auto"><Button variant="ghost" size="sm" className="h-6 px-2 text-[10px]" onClick={refresh}><RefreshCw className="w-3 h-3 mr-1" />刷新</Button></div>}
{connected && <div className="ml-auto"><Button variant="ghost" size="sm" className="h-6 px-2 text-[10px]" onClick={refresh}><RefreshCw className="w-3 h-3 mr-1" /> {i18n.t('monitor.dashboard.refresh')} </Button></div>}
</div>
</CardHeader>
<CardContent className="px-4 pb-3 pt-0">
{loading ? (<div className="text-center py-12"><Loader2 className="h-6 w-6 mx-auto mb-2 animate-spin dg-text-purple" /><p className="text-xs text-[var(--dg-text-muted)]">连接中…</p></div>)
{loading ? (<div className="text-center py-12"><Loader2 className="h-6 w-6 mx-auto mb-2 animate-spin dg-text-purple" /><p className="text-xs text-[var(--dg-text-muted)]"> {i18n.t('monitor.prometheusdashb.s5e8eb212')} </p></div>)
: error ? (<div className="p-3 rounded-lg bg-red-500/10 border border-red-500/20 text-xs dg-text-red"><AlertTriangle className="h-4 w-4 inline mr-1" />{error}</div>)
: (
<Tabs value={tab} onValueChange={setTab}>
@@ -18,6 +18,7 @@ import { Badge } from '@/components/ui/badge';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { zabbixProxyApi } from '@/api';
import type { JsonData } from '@/types/common';
import i18n from '@/i18n';
interface ZabbixHost {
hostid: string; hostname: string; name: string; ip: string; available: string;
@@ -50,7 +51,7 @@ export const ZabbixDashboard: React.FC<Props> = ({ dsUrl, dsUsername, dsPassword
setHosts(data.hosts || []);
setVersion(data.version || '');
setConnected(true);
} catch (e) { setConnected(false); setError((e as JsonData).message || '连接失败'); }
} catch (e) { setConnected(false); setError((e as JsonData).message || i18n.t('aiops.llmTestFailed')); }
finally { setLoading(false); }
};
@@ -97,21 +98,21 @@ export const ZabbixDashboard: React.FC<Props> = ({ dsUrl, dsUsername, dsPassword
<CardTitle className="text-sm font-medium">Zabbix 主机监控</CardTitle>
{version && <Badge className="text-[10px] bg-blue-500/10 dg-text-blue">v{version}</Badge>}
{connected && <span className="text-[10px] text-[var(--dg-text-muted)] ml-2">{hosts.length} 台</span>}
{connected && <div className="ml-auto flex gap-1"><Button variant="ghost" size="sm" className="h-6 px-2 text-[10px]" onClick={refresh}><RefreshCw className="w-3 h-3 mr-1" />刷新</Button></div>}
{connected && <div className="ml-auto flex gap-1"><Button variant="ghost" size="sm" className="h-6 px-2 text-[10px]" onClick={refresh}><RefreshCw className="w-3 h-3 mr-1" /> {i18n.t('monitor.dashboard.refresh')} </Button></div>}
</div>
</CardHeader>
<CardContent className="px-4 pb-3 pt-0">
{loading ? (<div className="text-center py-12"><Loader2 className="h-6 w-6 mx-auto mb-2 animate-spin dg-text-blue" /><p className="text-xs text-[var(--dg-text-muted)]">连接中…</p></div>)
{loading ? (<div className="text-center py-12"><Loader2 className="h-6 w-6 mx-auto mb-2 animate-spin dg-text-blue" /><p className="text-xs text-[var(--dg-text-muted)]"> {i18n.t('monitor.prometheusdashb.s5e8eb212')} </p></div>)
: error ? (<div className="p-3 rounded-lg bg-red-500/10 border border-red-500/20 text-xs dg-text-red flex items-start gap-2"><AlertTriangle className="h-4 w-4 mt-0.5" /><span>{error}</span></div>)
: hosts.length === 0 ? (<div className="text-center py-8 text-[var(--dg-text-muted)]"><Server className="h-8 w-8 mx-auto mb-2 dg-text-muted" /><p className="text-sm">未找到被监控的 Zabbix 主机</p></div>)
: hosts.length === 0 ? (<div className="text-center py-8 text-[var(--dg-text-muted)]"><Server className="h-8 w-8 mx-auto mb-2 dg-text-muted" /><p className="text-sm"> {i18n.t('monitor.zabbixdashboard.sdb131ab4')} </p></div>)
: (
<div className="overflow-x-auto rounded-lg border border-[var(--dg-border)]">
<table className="w-full text-xs">
<thead className="bg-[var(--dg-bg-tertiary)] border-b border-[var(--dg-border)]"><tr>
{[
{ f: 'hostname' as SortField, l: '主机名' },{ f: 'cpu_usage' as SortField, l: 'CPU' },{ f: 'memory_usage' as SortField, l: '内存' },
{ f: 'disk_usage' as SortField, l: '磁盘' },{ f: 'load5' as SortField, l: 'Load5' },{ f: 'net_in_bps' as SortField, l: '流入' },
{ f: 'net_out_bps' as SortField, l: '流出' },{ f: 'problem_count' as SortField, l: '问题' },{ f: 'uptime_days' as SortField, l: '运行' },
{ f: 'hostname' as SortField, l: i18n.t('kubernetes.workload.hostname') },{ f: 'cpu_usage' as SortField, l: 'CPU' },{ f: 'memory_usage' as SortField, l: i18n.t('kubernetes.pod.memory') },
{ f: 'disk_usage' as SortField, l: i18n.t('monitor.dashboard.col.disk') },{ f: 'load5' as SortField, l: 'Load5' },{ f: 'net_in_bps' as SortField, l: i18n.t('monitor.prometheusdashb.s361ffa7f') },
{ f: 'net_out_bps' as SortField, l: i18n.t('monitor.prometheusdashb.s37cc0a9a') },{ f: 'problem_count' as SortField, l: i18n.t('monitor.zabbixdashboard.s5dc99f6e') },{ f: 'uptime_days' as SortField, l: i18n.t('automation.scriptRun') },
].map(({ f, l }) => (
<th key={f} className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)] cursor-pointer hover:text-[var(--dg-text-primary)] select-none whitespace-nowrap" onClick={() => toggleSort(f)}>
<div className="flex items-center gap-1">{l}<SI f={f} /></div></th>