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

@@ -14,6 +14,7 @@ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } f
import { toast } from 'sonner';
import { useConfirm } from '@/components/ui/confirm-dialog';
import { buildNodeApi, serverApi, type BuildNode } from '@/api';
import i18n from '@/i18n';
interface ServerItem {
id: number;
@@ -58,7 +59,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
const srvList = (srvRes as { list?: ServerItem[] })?.list || [];
setServers(srvList);
} catch {
toast.error('加载构建节点失败');
toast.error(i18n.t('pipeline.buildnodesmanag.sb9faa2a3'));
} finally {
setLoading(false);
}
@@ -90,7 +91,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
const handleSubmit = async () => {
if (!name.trim() || !serverId) {
toast.error('请填写名称并选择服务器');
toast.error(i18n.t('pipeline.buildnodesmanag.sda51a736'));
return;
}
setSubmitting(true);
@@ -105,29 +106,29 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
};
if (editNode) {
await buildNodeApi.update(editNode.id, payload);
toast.success('已更新');
toast.success(i18n.t('deploy.toast.updated'));
} else {
await buildNodeApi.create(payload);
toast.success('已创建');
toast.success(i18n.t('kubernetes.event.created'));
}
setDialogOpen(false);
load();
} catch {
toast.error('操作失败');
toast.error(i18n.t('cloud.operationFailed'));
} finally {
setSubmitting(false);
}
};
const handleDelete = async (node: BuildNode) => {
const ok = await confirm({ title: '删除节点', description: `确定删除构建节点「${node.name}」?` });
const ok = await confirm({ title: i18n.t('kubernetes.topology.deleteNode'), description: `确定删除构建节点「${node.name}」?` });
if (!ok) return;
try {
await buildNodeApi.delete(node.id);
toast.success('已删除');
toast.success(i18n.t('monitor.silenceRules.deleted'));
load();
} catch {
toast.error('删除失败,请确认无流水线绑定');
toast.error(i18n.t('pipeline.buildnodesmanag.s577b185d'));
}
};
@@ -138,10 +139,10 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
if (res?.success) {
toast.success(`连通成功: ${res.message || ''}`);
} else {
toast.error(res?.message || '连接失败');
toast.error(res?.message || i18n.t('aiops.llmTestFailed'));
}
} catch {
toast.error('测试失败');
toast.error(i18n.t('monitor.alertChannels.testFailed'));
} finally {
setTestingId(null);
}
@@ -152,14 +153,14 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
await buildNodeApi.update(node.id, { enabled: !node.enabled });
load();
} catch {
toast.error('更新失败');
toast.error(i18n.t('kubernetes.deployment.imageUpdateFailed'));
}
};
const handleSaveMaster = async () => {
const n = Number(masterMax);
if (!n || n < 1) {
toast.error('并发数至少为 1');
toast.error(i18n.t('pipeline.buildnodesmanag.s5f25f49a'));
return;
}
setSavingMaster(true);
@@ -171,9 +172,9 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
max_executors: m?.max_executors ?? n,
running_count: m?.running_count ?? prev.running_count,
}));
toast.success('Master 并发已更新');
toast.success(i18n.t('pipeline.buildnodesmanag.scb1ab6ab'));
} catch {
toast.error('更新失败');
toast.error(i18n.t('kubernetes.deployment.imageUpdateFailed'));
} finally {
setSavingMaster(false);
}
@@ -184,7 +185,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
<ContentPanel
toolbar={(
<div className="flex items-center justify-between w-full">
<span className="text-sm font-medium text-[var(--dg-text-primary)]">构建节点</span>
<span className="text-sm font-medium text-[var(--dg-text-primary)]"> {i18n.t('pipeline.buildNodes')} </span>
<div className="flex gap-2">
<Button variant="ghost" size="sm" onClick={load} disabled={loading}>
<RefreshCw className={`h-3.5 w-3.5 mr-1${loading ? ' animate-spin' : ''}`} />刷新
@@ -201,13 +202,13 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
<div className="flex items-center gap-2">
<Cpu className="h-4 w-4 text-[var(--dg-text-muted)]" />
<span className="text-sm font-medium text-[var(--dg-text-primary)]">Master(内置)</span>
<Badge variant="outline" className="text-[10px]">默认调度</Badge>
<Badge variant="outline" className="text-[10px]"> {i18n.t('pipeline.buildnodesmanag.sa419fe03')} </Badge>
<span className="text-xs text-[var(--dg-text-muted)]">
占用 {master.running_count}/{master.max_executors}
</span>
</div>
<div className="flex items-center gap-2">
<Label className="text-xs text-[var(--dg-text-muted)] shrink-0">最大并发</Label>
<Label className="text-xs text-[var(--dg-text-muted)] shrink-0"> {i18n.t('pipeline.buildnodesmanag.s8e408e28')} </Label>
<Input
type="number"
min={1}
@@ -217,7 +218,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
className="h-7 w-16 text-xs dg-input"
/>
<Button size="sm" className="h-7 text-xs" onClick={handleSaveMaster} disabled={savingMaster}>
{savingMaster ? '保存中' : '保存'}
{savingMaster ? i18n.t('pipeline.buildnodesmanag.s7cbf2d09') : i18n.t('task.script.save')}
</Button>
</div>
</div>
@@ -228,9 +229,9 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
{nodes.length === 0 ? (
<PageEmpty
title="暂无远程构建节点"
description="关联服务器资产添加 SSH 节点,将重型构建任务调度到高性能机器"
action={<Button size="sm" onClick={openCreate}><Plus className="h-3.5 w-3.5 mr-1" />添加节点</Button>}
title={i18n.t('pipeline.buildnodesmanag.s2795bd4a')}
description={i18n.t('pipeline.buildnodesmanag.s90fb5e18')}
action={<Button size="sm" onClick={openCreate}><Plus className="h-3.5 w-3.5 mr-1" /> {i18n.t('kubernetes.topology.addNode')} </Button>}
/>
) : (
<div className="space-y-2">
@@ -243,7 +244,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
<div className="flex items-center gap-2 flex-wrap">
<span className="font-medium text-sm text-[var(--dg-text-primary)]">{node.name}</span>
<Badge variant={node.enabled ? 'default' : 'secondary'} className="text-[10px]">
{node.enabled ? '启用' : '禁用'}
{node.enabled ? i18n.t('system.userActive') : i18n.t('system.userInactive')}
</Badge>
<span className="text-[10px] text-[var(--dg-text-muted)] font-mono">
{node.hostname || node.ip || `server#${node.server_id}`}
@@ -257,10 +258,10 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
</div>
<div className="flex items-center gap-1 shrink-0">
<Button variant="ghost" size="sm" className="h-7 text-xs" disabled={testingId === node.id} onClick={() => handleTest(node)}>
{testingId === node.id ? '测试中' : '测试'}
{testingId === node.id ? i18n.t('pipeline.buildnodesmanag.sf85549cd') : i18n.t('pipeline.testTool')}
</Button>
<Button variant="ghost" size="sm" className="h-7 text-xs" onClick={() => handleToggle(node)}>
{node.enabled ? '禁用' : '启用'}
{node.enabled ? i18n.t('system.userInactive') : i18n.t('system.userActive')}
</Button>
<Button variant="ghost" size="icon" className="h-7 w-7" onClick={() => openEdit(node)}>
<Pencil className="h-3.5 w-3.5" />
@@ -281,22 +282,22 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
<DialogContent className="max-w-md bg-[var(--dg-bg-primary)] border-[var(--dg-border)]">
<DialogHeader>
<DialogTitle>{editNode ? '编辑节点' : '添加节点'}</DialogTitle>
<DialogTitle>{editNode ? i18n.t('kubernetes.topology.editNode') : i18n.t('kubernetes.topology.addNode')}</DialogTitle>
<DialogDescription className="text-[var(--dg-text-muted)]">
基于服务器 SSH 凭据注册构建 Agent,流水线可绑定或自动调度
</DialogDescription>
</DialogHeader>
<div className="space-y-3">
<div className="space-y-1">
<Label className="text-xs">节点名称</Label>
<Label className="text-xs"> {i18n.t('kubernetes.node.nodeName')} </Label>
<Input value={name} onChange={(e) => setName(e.target.value)} className="h-8 text-xs dg-input" placeholder="build-node-01" />
</div>
<div className="space-y-1">
<Label className="text-xs">关联服务器</Label>
<Label className="text-xs"> {i18n.t('pipeline.buildnodesmanag.sdfdaf1c5')} </Label>
<Select value={serverId || '__none'} onValueChange={(v) => setServerId(v === '__none' ? '' : v)}>
<SelectTrigger className="h-8 text-xs dg-input"><SelectValue placeholder="选择服务器" /></SelectTrigger>
<SelectTrigger className="h-8 text-xs dg-input"><SelectValue placeholder={i18n.t('server.webshelltsx.s030235d1')} /></SelectTrigger>
<SelectContent>
<SelectItem value="__none">请选择</SelectItem>
<SelectItem value="__none"> {i18n.t('k8s.storage.pleaseSelect')} </SelectItem>
{servers.map((s) => (
<SelectItem key={s.id} value={String(s.id)}>
{s.hostname} ({s.ip})
@@ -307,25 +308,25 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
</div>
<div className="grid grid-cols-2 gap-2">
<div className="space-y-1">
<Label className="text-xs">最大并发</Label>
<Label className="text-xs"> {i18n.t('pipeline.buildnodesmanag.s8e408e28')} </Label>
<Input value={maxExecutors} onChange={(e) => setMaxExecutors(e.target.value)} type="number" min={1} className="h-8 text-xs dg-input" />
</div>
<div className="space-y-1">
<Label className="text-xs">工作区路径</Label>
<Label className="text-xs"> {i18n.t('pipeline.buildnodesmanag.sbcfcd088')} </Label>
<Input value={workspacePath} onChange={(e) => setWorkspacePath(e.target.value)} className="h-8 text-xs font-mono dg-input" />
</div>
</div>
<div className="space-y-1">
<Label className="text-xs">标签 <span className="text-[var(--dg-text-muted)]">(可选)</span></Label>
<Label className="text-xs"> {i18n.t('kubernetes.namespace.labels')} <span className="text-[var(--dg-text-muted)]">(可选)</span></Label>
<Input value={labels} onChange={(e) => setLabels(e.target.value)} className="h-8 text-xs dg-input" placeholder="linux,docker,high-mem" />
</div>
<div className="space-y-1">
<Label className="text-xs">描述</Label>
<Label className="text-xs"> {i18n.t('aiops.llmModelDesc')} </Label>
<Input value={description} onChange={(e) => setDescription(e.target.value)} className="h-8 text-xs dg-input" />
</div>
<div className="flex justify-end gap-2 pt-2">
<Button variant="outline" onClick={() => setDialogOpen(false)}>取消</Button>
<Button onClick={handleSubmit} disabled={submitting}>{submitting ? '保存中' : '保存'}</Button>
<Button variant="outline" onClick={() => setDialogOpen(false)}> {i18n.t('rbac.jobRole.cancel')} </Button>
<Button onClick={handleSubmit} disabled={submitting}>{submitting ? i18n.t('pipeline.buildnodesmanag.s7cbf2d09') : i18n.t('task.script.save')}</Button>
</div>
</div>
</DialogContent>
@@ -342,7 +343,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
}
return (
<PageShell title="构建节点" subtitle="Master 与远程 SSH 构建节点">
<PageShell title={i18n.t('pipeline.buildNodes')} subtitle={i18n.t('pipeline.buildnodesmanag.sb2b825e4')}>
{panel}
{dialog}
</PageShell>