refactor(i18n): U27 多模块 phantom key 批量清理 — Cloud/Deploy/Agent/Monitor/System 5 模块 25 文件, 180 个 phantom key → 51 残留, 129 个替换为语义化键, 新增 12 子命名空间, zh/en 7185→7294 +109 键对齐, tsc -b=0, npm build✓ (16.31s), ESLint=0/0/0, go build=0, 17/17 测试通过

This commit is contained in:
attacker committed 2026-07-12 03:00:20 +08:00
1 parent 7f5295cfc2
commit b812b3b20c
27 files changed
+459 -148

No files matched your search

@@ -59,7 +59,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
const srvList = (srvRes as { list?: ServerItem[] })?.list || [];
setServers(srvList);
} catch {
toast.error(i18n.t('加载构建节点失败'));
toast.error(i18n.t('deploy.buildNodes.loadFailed'));
} finally {
setLoading(false);
}
@@ -91,7 +91,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
const handleSubmit = async () => {
if (!name.trim() || !serverId) {
toast.error(i18n.t('加载构建节点失败'));
toast.error(i18n.t('deploy.buildNodes.loadFailed'));
return;
}
setSubmitting(true);
@@ -121,14 +121,14 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
};
const handleDelete = async (node: BuildNode) => {
const ok = await confirm({ title: i18n.t('kubernetes.topology.deleteNode'), description: `确定删除构建节点「${node.name}」?` });
const ok = await confirm({ title: i18n.t('kubernetes.topology.deleteNode'), description: i18n.t('deploy.buildNodes.deleteConfirm', { name: node.name }) });
if (!ok) return;
try {
await buildNodeApi.delete(node.id);
toast.success(i18n.t('monitor.silenceRules.deleted'));
load();
} catch {
toast.error(i18n.t('加载构建节点失败'));
toast.error(i18n.t('deploy.buildNodes.loadFailed'));
}
};
@@ -137,7 +137,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
try {
const res = await buildNodeApi.test(node.id) as { success?: boolean; message?: string };
if (res?.success) {
toast.success(`连通成功: ${res.message || ''}`);
toast.success(i18n.t('deploy.buildNodes.testSuccess') + (res.message ? `: ${res.message}` : ''));
} else {
toast.error(res?.message || i18n.t('aiops.llmTestFailed'));
}
@@ -160,7 +160,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
const handleSaveMaster = async () => {
const n = Number(masterMax);
if (!n || n < 1) {
toast.error(i18n.t('加载构建节点失败'));
toast.error(i18n.t('deploy.buildNodes.loadFailed'));
return;
}
setSavingMaster(true);
@@ -172,7 +172,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
max_executors: m?.max_executors ?? n,
running_count: m?.running_count ?? prev.running_count,
}));
toast.success(i18n.t('已删除'));
toast.success(i18n.t('deploy.buildNodes.deleted'));
} catch {
toast.error(i18n.t('kubernetes.deployment.imageUpdateFailed'));
} finally {
@@ -201,10 +201,10 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
<div className="flex flex-wrap items-center justify-between gap-3">
<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>
<span className="text-sm font-medium text-[var(--dg-text-primary)]">{i18n.t('deploy.buildNodes.masterBuiltin')}</span>
<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}
{i18n.t('deploy.buildNodes.executorUsage', { running: master.running_count, max: master.max_executors })}
</span>
</div>
<div className="flex items-center gap-2">
@@ -218,19 +218,19 @@ 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 ? i18n.t('保存中') : i18n.t('task.script.save')}
{savingMaster ? i18n.t('deploy.buildNodes.saving') : i18n.t('task.script.save')}
</Button>
</div>
</div>
<p className="text-[11px] text-[var(--dg-text-muted)] mt-2">
未指定节点时优先在 Master 执行;并发设为 1 时按触发顺序串行排队;满载后自动调度到空闲远程节点
{i18n.t('deploy.buildNodes.schedulingHint')}
</p>
</div>
{nodes.length === 0 ? (
<PageEmpty
title={i18n.t('暂无远程构建节点')}
description={i18n.t('关联服务器资产添加 SSH 节点,将重型构建任务调度到高性能机器')}
title={i18n.t('deploy.buildNodes.noRemoteNodes')}
description={i18n.t('deploy.buildNodes.noRemoteDesc')}
action={<Button size="sm" onClick={openCreate}><Plus className="h-3.5 w-3.5 mr-1" /> {i18n.t('kubernetes.topology.addNode')} </Button>}
/>
) : (
@@ -251,14 +251,14 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
</span>
</div>
<div className="flex items-center gap-3 mt-1 text-[11px] text-[var(--dg-text-muted)]">
<span>执行器 {node.running_count || 0}/{node.max_executors}</span>
{node.labels && <span>标签 {node.labels}</span>}
<span>{i18n.t('deploy.buildNodes.executorLabel', { running: node.running_count || 0, max: node.max_executors })}</span>
{node.labels && <span>{i18n.t('deploy.buildNodes.labelsLabel', { labels: node.labels })}</span>}
<span className="truncate">{node.workspace_path}</span>
</div>
</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 ? i18n.t('测试中') : i18n.t('pipeline.testTool')}
{testingId === node.id ? i18n.t('deploy.buildNodes.testing') : i18n.t('pipeline.testTool')}
</Button>
<Button variant="ghost" size="sm" className="h-7 text-xs" onClick={() => handleToggle(node)}>
{node.enabled ? i18n.t('system.userInactive') : i18n.t('system.userActive')}
@@ -295,7 +295,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
<div className="space-y-1">
<Label className="text-xs"> {i18n.t('>标签 <span className=')} </Label>
<Select value={serverId || '__none'} onValueChange={(v) => setServerId(v === '__none' ? '' : v)}>
<SelectTrigger className="h-8 text-xs dg-input"><SelectValue placeholder={i18n.t('选择服务器')} /></SelectTrigger>
<SelectTrigger className="h-8 text-xs dg-input"><SelectValue placeholder={i18n.t('deploy.buildNodes.selectServer')} /></SelectTrigger>
<SelectContent>
<SelectItem value="__none"> {i18n.t('k8s.storage.pleaseSelect')} </SelectItem>
{servers.map((s) => (
@@ -326,7 +326,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
</div>
<div className="flex justify-end gap-2 pt-2">
<Button variant="outline" onClick={() => setDialogOpen(false)}> {i18n.t('rbac.jobRole.cancel')} </Button>
<Button onClick={handleSubmit} disabled={submitting}>{submitting ? i18n.t('保存中') : i18n.t('task.script.save')}</Button>
<Button onClick={handleSubmit} disabled={submitting}>{submitting ? i18n.t('deploy.buildNodes.saving') : i18n.t('task.script.save')}</Button>
</div>
</div>
</DialogContent>
@@ -343,7 +343,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
}
return (
<PageShell title={i18n.t('pipeline.buildNodes')} subtitle={i18n.t('构建节点')}>
<PageShell title={i18n.t('pipeline.buildNodes')} subtitle={i18n.t('deploy.pipeline.buildNodes')}>
{panel}
{dialog}
</PageShell>
@@ -112,7 +112,7 @@ export default function DeployRBAC() {
const handleCreate = async () => {
if (!newName.trim() || !newCode.trim() || !newPattern.trim()) {
toast.error(i18n.t('请填写名称、标识与正则')); return;
toast.error(i18n.t('deploy.rbac.policy.fillFields')); return;
}
setSaving(true);
try {
@@ -147,7 +147,7 @@ export default function DeployRBAC() {
const handleDelete = async (role: ItemRole) => {
const ok = await _confirm({
title: i18n.t('删除权限'),
title: i18n.t('deploy.rbac.policy.deletePolicy'),
description: `确定删除「${role.name}」?将影响 ${matchCount[role.id]?.length || 0} 个发布任务`,
variant: 'destructive',
});
@@ -165,7 +165,7 @@ export default function DeployRBAC() {
};
const handleAssign = async () => {
if (!selectedUserId || !assignRoleId) { toast.error(i18n.t('请选择用户和权限')); return; }
if (!selectedUserId || !assignRoleId) { toast.error(i18n.t('deploy.rbac.policy.selectUserPermission')); return; }
setAssignLoading(true);
try {
await deployApi.assignItemRole(Number(selectedUserId), {
@@ -190,8 +190,8 @@ export default function DeployRBAC() {
const selectedUser = users.find((u) => String(u.id) === selectedUserId);
const pageTabs = [
{ id: 'roles', label: i18n.t('权限策略'), badge: roles.length || undefined },
{ id: 'users', label: i18n.t('用户授权') },
{ id: 'roles', label: i18n.t('deploy.rbac.policy.policyStrategy'), badge: roles.length || undefined },
{ id: 'users', label: i18n.t('deploy.rbac.policy.userAuth') },
{ id: 'groups', label: i18n.t('pipeline.groupLabel') },
{ id: 'preview', label: i18n.t('pipeline.matchPreview') },
];
@@ -200,7 +200,7 @@ export default function DeployRBAC() {
<PageShell
variant="list"
title={i18n.t('deploy.deployNav.jobPermission')}
subtitle={i18n.t('按流水线 code / Job 名称正则分配发布权限')}
subtitle={i18n.t('deploy.rbac.policy.subtitle')}
icon={<Shield className="h-5 w-5" />}
loading={loading}
tabs={pageTabs}
@@ -217,8 +217,8 @@ export default function DeployRBAC() {
roles.length === 0 ? (
<PageEmpty
icon={<Shield className="h-10 w-10" />}
title={i18n.t('暂无 Job 权限策略')}
description={i18n.t('创建策略后,用户将按正则自动获得匹配流水线的访问权限')}
title={i18n.t('deploy.rbac.policy.noPolicy')}
description={i18n.t('deploy.rbac.policy.createHint')}
action={<Button onClick={() => setCreateOpen(true)}><Plus className="h-4 w-4 mr-1" /> {i18n.t('monitor.logrbactsx.s6058845c')} </Button>}
/>
) : (
@@ -296,7 +296,7 @@ export default function DeployRBAC() {
</Select>
</div>
<div className="space-y-1 w-48">
<Label className="text-xs"> {i18n.t('权限策略')} </Label>
<Label className="text-xs"> {i18n.t('deploy.rbac.policy.policyStrategy')} </Label>
<Select value={assignRoleId} onValueChange={setAssignRoleId}>
<SelectTrigger className="h-8 text-sm dg-input"><SelectValue placeholder={i18n.t('kubernetes.hpa.selectPolicy')} /></SelectTrigger>
<SelectContent>
@@ -99,8 +99,8 @@ export default function UserGroupsManager({ embedded = false }: { embedded?: boo
const listBody = !loading && groups.length === 0 ? (
<PageEmpty
icon={<Users className="h-10 w-10" />}
title={i18n.t('暂无用户组')}
description={i18n.t('点击「新建用户组」创建第一个用户组')}
title={i18n.t('deploy.userGroup.noGroups')}
description={i18n.t('deploy.userGroup.createFirstHint')}
action={(
<Button size="sm" onClick={() => { setEditId(null); setFormName(''); setFormDesc(''); setFormOpen(true); }}>
<Plus className="w-4 h-4 mr-1" />新建用户组
@@ -165,7 +165,7 @@ export default function UserGroupsManager({ embedded = false }: { embedded?: boo
<DialogContent>
<DialogHeader><DialogTitle>{editId ? i18n.t('deploy.rbac.groupEditTitle') : i18n.t('pipeline.newGroup')}</DialogTitle></DialogHeader>
<div className="space-y-3">
<div className="space-y-1"><Label> {i18n.t('monitor.groupName')} </Label><Input value={formName} onChange={e => setFormName(e.target.value)} placeholder={i18n.t('例如: 后端开发组')} /></div>
<div className="space-y-1"><Label> {i18n.t('monitor.groupName')} </Label><Input value={formName} onChange={e => setFormName(e.target.value)} placeholder={i18n.t('deploy.userGroup.namePlaceholder')} /></div>
<div className="space-y-1"><Label> {i18n.t('aiops.llmModelDesc')} </Label><Input value={formDesc} onChange={e => setFormDesc(e.target.value)} placeholder={i18n.t('rbac.jobRole.placeholder.description')} /></div>
</div>
<div className="flex justify-end gap-2 mt-4 pt-4 border-t">
@@ -190,14 +190,14 @@ export default function UserGroupsManager({ embedded = false }: { embedded?: boo
<PageShell
variant="list"
title={i18n.t('pipeline.releaseTitle')}
subtitle={i18n.t('用户组 — 批量授权流水线访问权限')}
subtitle={i18n.t('deploy.userGroup.subtitle')}
icon={<Users className="h-5 w-5" />}
loading={loading}
metrics={(
<StatStrip
items={[
{ label: i18n.t('pipeline.groupLabel'), value: groups.length, color: 'blue' },
{ label: i18n.t('成员总数'), value: memberCount, color: 'purple' },
{ label: i18n.t('deploy.userGroup.memberCount'), value: memberCount, color: 'purple' },
]}
/>
)}
@@ -149,8 +149,8 @@ export function DeployTargetFields({ value, onChange, requireTarget = true }: De
<div className="space-y-3">
<div className="flex gap-1 p-1 rounded-lg bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)]">
{([
{ key: 'ssh' as const, label: i18n.t('主机发布'), icon: Server },
{ key: 'k8s' as const, label: i18n.t('主机发布'), icon: Network },
{ key: 'ssh' as const, label: i18n.t('deploy.target.hostDeploy'), icon: Server },
{ key: 'k8s' as const, label: i18n.t('deploy.target.hostDeploy'), icon: Network },
]).map((item) => {
const active = value.deployTarget === item.key;
const Icon = item.icon;
@@ -200,7 +200,7 @@ export function DeployTargetFields({ value, onChange, requireTarget = true }: De
<Input
value={serverSearch}
onChange={(e) => setServerSearch(e.target.value)}
placeholder={i18n.t('搜索主机名或 IP')}
placeholder={i18n.t('deploy.target.searchHost')}
className="pl-8 h-8 text-xs dg-input"
/>
</div>
@@ -296,7 +296,7 @@ export function DeployTargetFields({ value, onChange, requireTarget = true }: De
<Input
readOnly
value={value.k8sDeployment ? `${value.k8sNamespace}/${value.k8sDeployment}` : ''}
placeholder={i18n.t('从下方列表选择')}
placeholder={i18n.t('deploy.target.selectFromList')}
className="h-8 text-xs dg-input font-mono"
/>
</div>
@@ -308,7 +308,7 @@ export function DeployTargetFields({ value, onChange, requireTarget = true }: De
<Input
value={depSearch}
onChange={(e) => setDepSearch(e.target.value)}
placeholder={i18n.t('搜索 Deployment 或命名空间')}
placeholder={i18n.t('deploy.target.searchDeployment')}
className="pl-8 h-8 text-xs dg-input"
/>
</div>
@@ -353,7 +353,7 @@ export function DeployTargetFields({ value, onChange, requireTarget = true }: De
<Input
value={value.k8sContainerName}
onChange={(e) => onChange({ k8sContainerName: e.target.value })}
placeholder={i18n.t('搜索 Deployment 或命名空间')}
placeholder={i18n.t('deploy.target.searchDeployment')}
className="h-8 text-xs font-mono dg-input"
/>
</div>
@@ -10,12 +10,12 @@ import {
// 流水线阶段定义(固定顺序) // Logan.Li
export const STAGE_DEFS = [
{ key: 'checkout', label: i18n.t('pipeline.checkout'), icon: GitBranch, color: '#6366F1', order: 1, needs: ['repo'], desc: i18n.t('代码检出') },
{ key: 'build', label: i18n.t('pipeline.build'), icon: Box, color: '#F59E0B', order: 2, needs: [], desc: i18n.t('构建') },
{ key: 'test', label: i18n.t('pipeline.testTool'), icon: CheckCircle, color: '#10B981', order: 3, needs: [], desc: i18n.t('测试') },
{ key: 'image', label: i18n.t('pipeline.imageBuild'), icon: Container, color: '#06B6D4', order: 4, needs: ['registry'], desc: i18n.t('镜像打包') },
{ key: 'deploy', label: i18n.t('pipeline.deploy2'), icon: Rocket, color: '#EF4444', order: 5, needs: [], desc: i18n.t('部署') },
{ key: 'verify', label: i18n.t('system.credentialVerify'), icon: ShieldCheck, color: '#3B82F6', order: 6, needs: [], desc: i18n.t('验证') },
{ key: 'checkout', label: i18n.t('pipeline.checkout'), icon: GitBranch, color: '#6366F1', order: 1, needs: ['repo'], desc: i18n.t('deploy.stage.checkout') },
{ key: 'build', label: i18n.t('pipeline.build'), icon: Box, color: '#F59E0B', order: 2, needs: [], desc: i18n.t('deploy.stage.build') },
{ key: 'test', label: i18n.t('pipeline.testTool'), icon: CheckCircle, color: '#10B981', order: 3, needs: [], desc: i18n.t('deploy.stage.test') },
{ key: 'image', label: i18n.t('pipeline.imageBuild'), icon: Container, color: '#06B6D4', order: 4, needs: ['registry'], desc: i18n.t('deploy.stage.imageBuild') },
{ key: 'deploy', label: i18n.t('pipeline.deploy2'), icon: Rocket, color: '#EF4444', order: 5, needs: [], desc: i18n.t('deploy.stage.deploy') },
{ key: 'verify', label: i18n.t('system.credentialVerify'), icon: ShieldCheck, color: '#3B82F6', order: 6, needs: [], desc: i18n.t('deploy.stage.verify') },
].sort((a, b) => a.order - b.order);
// 运行日志条目 // Logan.Li