feat(i18n): 剩余模块全量国际化 — 51% 覆盖率 (231 文件接入 t())

批量转换 163 个文件,3204 处中文替换为 t() 调用:
- Monitor: 日志中心/告警中心/数据源/大屏/站点拨测/SNMP/中间件 全部转换
- Kubernetes: ResourceVisualEditor/NamespaceResourcePage/拓扑/事件 全部转换
- Deploy: 流水线/发布/凭证/RBAC 全部转换
- Server/Cloud/Agent/Task/Layout/UI: 全部转换
- 翻译词典扩展至 6474 行 (22 namespaces, 5000+ keys)
- tsc 编译零错误
This commit is contained in:
attacker committed 2026-07-03 01:51:57 +08:00
1 parent 78c27037e3
commit ecec73989b
166 files changed
+6925 -2800

No files matched your search

@@ -22,11 +22,13 @@ import { PodFileBrowser } from './PodFileBrowser';
import { formatK8sTime, statusColor, STATUS_COLORS } from './k8s-yaml-utils';
import { PageShell } from '@/components/layout/PageShell';
import type { JsonData } from '@/types/common';
import { useTranslation } from 'react-i18next';
type WorkloadKind = 'StatefulSet' | 'DaemonSet';
type ViewMode = 'view' | 'yaml';
function StatCard({ label, value, sub, color }: { label: string; value: React.ReactNode; sub?: string; color?: string }) {
const { t } = useTranslation();
return (
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
<CardContent className="p-3">
@@ -40,8 +42,9 @@ function StatCard({ label, value, sub, color }: { label: string; value: React.Re
// Helper: render key-value pairs as badges
function LabelBadges({ labels }: { labels: Record<string, string> | undefined }) {
const { t } = useTranslation();
const entries = Object.entries(labels || {});
if (entries.length === 0) return <span className="text-xs text-[var(--dg-text-muted)]">无</span>;
if (entries.length === 0) return <span className="text-xs text-[var(--dg-text-muted)]">{t('common.none')}</span>;
return (
<div className="flex flex-wrap gap-1.5">
{entries.map(([k, v]) => (
@@ -54,6 +57,7 @@ function LabelBadges({ labels }: { labels: Record<string, string> | undefined })
}
export default function WorkloadViewPage() {
const { t } = useTranslation();
const params = useParams<{ clusterCode: string; namespace: string; name: string; mode?: string }>();
const navigate = useNavigate();
const location = useLocation();
@@ -119,9 +123,9 @@ export default function WorkloadViewPage() {
const handleRestart = async () => {
const ok = await confirm({
title: '滚动重启',
description: `确认重启 ${kind} ${name}?将触发滚动重启。`,
confirmText: '重启',
title: t('kubernetes.deployment.restart'),
description: t('kubernetes.workloadviewpage.s6139f5', { kind: kind, name: name }),
confirmText: t('common.reboot'),
});
if (!ok) return;
setActionLoading(true);
@@ -131,10 +135,10 @@ export default function WorkloadViewPage() {
} else {
await kubernetesApi.restartDaemonSet(clusterCode, namespace, name);
}
toast({ title: '重启指令已下发', description: `${kind} 滚动重启已触发` });
toast({ title: t('kubernetes.workloadviewpage.s68a700'), description: t('kubernetes.workloadviewpage.sb0614d', { kind: kind }) });
setTimeout(loadData, 1000);
} catch (e) {
toast({ title: '重启失败', description: e?.message || String(e), variant: 'destructive' });
toast({ title: t('kubernetes.deployment.restartFailed'), description: e?.message || String(e), variant: 'destructive' });
} finally { setActionLoading(false); }
};
@@ -142,11 +146,11 @@ export default function WorkloadViewPage() {
setActionLoading(true);
try {
await kubernetesApi.scaleStatefulSet(clusterCode, namespace, name, scaleValue);
toast({ title: '扩缩容成功', description: `StatefulSet 副本数已更新为 ${scaleValue}` });
toast({ title: t('kubernetes.workloadviewpage.s0b3ea1'), description: t('kubernetes.workloadviewpage.s27c3cf', { scaleValue: scaleValue }) });
setScaleModal(false);
setTimeout(loadData, 1000);
} catch (e) {
toast({ title: '扩缩容失败', description: e?.message || String(e), variant: 'destructive' });
toast({ title: t('kubernetes.workloadviewpage.s0b329f'), description: e?.message || String(e), variant: 'destructive' });
} finally { setActionLoading(false); }
};
@@ -190,7 +194,7 @@ export default function WorkloadViewPage() {
const modeTabs = (
<div className="flex items-center gap-1 bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)] rounded-lg p-0.5">
<button className={`px-3 py-1 text-xs rounded transition-colors ${mode === 'view' ? 'bg-[var(--dg-blue)]/20 text-[var(--dg-blue)]' : 'text-[var(--dg-text-secondary)] hover:text-[var(--dg-text-primary)]'}`} onClick={() => handleModeChange('view')}>查看</button>
<button className={`px-3 py-1 text-xs rounded transition-colors ${mode === 'view' ? 'bg-[var(--dg-blue)]/20 text-[var(--dg-blue)]' : 'text-[var(--dg-text-secondary)] hover:text-[var(--dg-text-primary)]'}`} onClick={() => handleModeChange('view')}>{t('common.view')}</button>
<button className={`px-3 py-1 text-xs rounded transition-colors ${mode === 'yaml' ? 'bg-[var(--dg-blue)]/20 text-[var(--dg-blue)]' : 'text-[var(--dg-text-secondary)] hover:text-[var(--dg-text-primary)]'}`} onClick={() => handleModeChange('yaml')}>YAML</button>
</div>
);
@@ -202,7 +206,7 @@ export default function WorkloadViewPage() {
subtitle={`${clusterCode} / ${namespace} · ${kind}`}
icon={<Boxes className="h-5 w-5" />}
loading={loading}
loadingLabel="加载工作负载..."
loadingLabel=t('kubernetes.workloadviewpage.s4eb429')
actions={(
<>
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={() => navigate(-1)}>
@@ -215,7 +219,7 @@ export default function WorkloadViewPage() {
</Button>
)}
<Button variant="outline" size="sm" className="dg-btn-secondary" disabled={actionLoading} onClick={handleRestart}>
<RotateCcw className="h-3.5 w-3.5 mr-1" />{actionLoading ? '重启中...' : '重启'}
<RotateCcw className="h-3.5 w-3.5 mr-1" />{actionLoading ? t('kubernetes.workloadviewpage.s9ac104') : t('common.reboot')}
</Button>
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={() => setYamlModal(true)}>
<FileCode className="h-3.5 w-3.5 mr-1" />YAML
@@ -227,8 +231,8 @@ export default function WorkloadViewPage() {
<Badge variant="outline" className="border-[var(--dg-border)] text-[var(--dg-text-secondary)]">{kind}</Badge>
<Badge variant="outline" className="border-[var(--dg-border)] text-[var(--dg-text-secondary)]">{namespace}</Badge>
{isHealthy
? <Badge className="bg-[var(--dg-green)]/20 text-[var(--dg-green)] border-[var(--dg-green)]/30">健康</Badge>
: <Badge className="bg-[var(--dg-orange)]/20 text-[var(--dg-orange)] border-[var(--dg-orange)]/30">更新中</Badge>}
? <Badge className="bg-[var(--dg-green)]/20 text-[var(--dg-green)] border-[var(--dg-green)]/30">{t('common.healthy')}</Badge>
: <Badge className="bg-[var(--dg-orange)]/20 text-[var(--dg-orange)] border-[var(--dg-orange)]/30">{t('kubernetes.workloadviewpage.sf339d4')}</Badge>}
</div>
{!loading && (mode === 'yaml' ? (
@@ -251,21 +255,21 @@ export default function WorkloadViewPage() {
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-3">
{isSts ? (
<>
<StatCard label="副本就绪" value={`${readyReplicas}/${replicas}`} color={isHealthy ? STATUS_COLORS.green : STATUS_COLORS.orange} sub={isHealthy ? '全部就绪' : '部分未就绪'} />
<StatCard label="当前副本" value={currentReplicas} color={STATUS_COLORS.blue} />
<StatCard label="已更新副本" value={updatedReplicas} color={STATUS_COLORS.green} />
<StatCard label="服务名" value={spec.serviceName || '-'} color="var(--dg-text-secondary)" />
<StatCard label="Pod 数量" value={pods.length} color="var(--dg-text-secondary)" />
<StatCard label="创建时间" value={<span className="text-xs">{formatK8sTime(manifest?.metadata?.creationTimestamp)}</span>} color="var(--dg-text-secondary)" />
<StatCard label=t('kubernetes.workloadviewpage.sea6396') value={`${readyReplicas}/${replicas}`} color={isHealthy ? STATUS_COLORS.green : STATUS_COLORS.orange} sub={isHealthy ? t('kubernetes.workloadviewpage.s23c944') : t('kubernetes.workloadviewpage.sac1002')} />
<StatCard label=t('kubernetes.hpa.currentReplicas') value={currentReplicas} color={STATUS_COLORS.blue} />
<StatCard label=t('kubernetes.deployment.updatedReplicas') value={updatedReplicas} color={STATUS_COLORS.green} />
<StatCard label=t('kubernetes.workloadviewpage.sf592ef') value={spec.serviceName || '-'} color="var(--dg-text-secondary)" />
<StatCard label=t('kubernetes.workloadviewpage.s55edf2') value={pods.length} color="var(--dg-text-secondary)" />
<StatCard label=t('common.createdAt') value={<span className="text-xs">{formatK8sTime(manifest?.metadata?.creationTimestamp)}</span>} color="var(--dg-text-secondary)" />
</>
) : (
<>
<StatCard label="期望调度" value={desiredScheduled} color={STATUS_COLORS.blue} />
<StatCard label="当前调度" value={currentScheduled} color="var(--dg-text-secondary)" />
<StatCard label="就绪数量" value={numberReady} color={isHealthy ? STATUS_COLORS.green : STATUS_COLORS.orange} />
<StatCard label="可用数量" value={numberAvailable} color={STATUS_COLORS.green} />
<StatCard label="已更新" value={numberUpdated} color="var(--dg-text-secondary)" />
<StatCard label="创建时间" value={<span className="text-xs">{formatK8sTime(manifest?.metadata?.creationTimestamp)}</span>} color="var(--dg-text-secondary)" />
<StatCard label=t('kubernetes.workloadviewpage.sd78bc8') value={desiredScheduled} color={STATUS_COLORS.blue} />
<StatCard label=t('kubernetes.workloadviewpage.sb76164') value={currentScheduled} color="var(--dg-text-secondary)" />
<StatCard label=t('kubernetes.daemonset.numberReady') value={numberReady} color={isHealthy ? STATUS_COLORS.green : STATUS_COLORS.orange} />
<StatCard label=t('kubernetes.daemonset.numberAvailable') value={numberAvailable} color={STATUS_COLORS.green} />
<StatCard label=t('deploy.toast.updated') value={numberUpdated} color="var(--dg-text-secondary)" />
<StatCard label=t('common.createdAt') value={<span className="text-xs">{formatK8sTime(manifest?.metadata?.creationTimestamp)}</span>} color="var(--dg-text-secondary)" />
</>
)}
</div>
@@ -280,18 +284,18 @@ export default function WorkloadViewPage() {
</CardHeader>
<CardContent className="pt-0 space-y-2 text-xs">
<div className="flex justify-between">
<span className="text-[var(--dg-text-muted)]">策略类型</span>
<span className="text-[var(--dg-text-muted)]">{t('kubernetes.workloadviewpage.s0511a2')}</span>
<Badge variant="outline" className="border-[var(--dg-border)] text-[var(--dg-text-secondary)]">{strategyType}</Badge>
</div>
{strategyType === 'RollingUpdate' && isSts && updateStrategy.rollingUpdate && (
<>
<div className="flex justify-between">
<span className="text-[var(--dg-text-muted)]">分区 (partition)</span>
<span className="text-[var(--dg-text-muted)]">{t('kubernetes.workloadviewpage.s45cb9d')}</span>
<span className="text-[var(--dg-text-secondary)] font-mono">{updateStrategy.rollingUpdate.partition ?? 0}</span>
</div>
{updateStrategy.rollingUpdate.maxUnavailable && (
<div className="flex justify-between">
<span className="text-[var(--dg-text-muted)]">最大不可用</span>
<span className="text-[var(--dg-text-muted)]">{t('kubernetes.daemonset.maxUnavailable')}</span>
<span className="text-[var(--dg-text-secondary)] font-mono">{typeof updateStrategy.rollingUpdate.maxUnavailable === 'object' ? JSON.stringify(updateStrategy.rollingUpdate.maxUnavailable) : updateStrategy.rollingUpdate.maxUnavailable}</span>
</div>
)}
@@ -301,13 +305,13 @@ export default function WorkloadViewPage() {
<>
{updateStrategy.rollingUpdate.maxUnavailable && (
<div className="flex justify-between">
<span className="text-[var(--dg-text-muted)]">最大不可用</span>
<span className="text-[var(--dg-text-muted)]">{t('kubernetes.daemonset.maxUnavailable')}</span>
<span className="text-[var(--dg-text-secondary)] font-mono">{typeof updateStrategy.rollingUpdate.maxUnavailable === 'object' ? JSON.stringify(updateStrategy.rollingUpdate.maxUnavailable) : updateStrategy.rollingUpdate.maxUnavailable}</span>
</div>
)}
{updateStrategy.rollingUpdate.maxSurge && (
<div className="flex justify-between">
<span className="text-[var(--dg-text-muted)]">最大 surge</span>
<span className="text-[var(--dg-text-muted)]">{t('kubernetes.workloadviewpage.sfed4ce')}</span>
<span className="text-[var(--dg-text-secondary)] font-mono">{typeof updateStrategy.rollingUpdate.maxSurge === 'object' ? JSON.stringify(updateStrategy.rollingUpdate.maxSurge) : updateStrategy.rollingUpdate.maxSurge}</span>
</div>
)}
@@ -315,12 +319,12 @@ export default function WorkloadViewPage() {
)}
{isSts && (
<div className="flex justify-between">
<span className="text-[var(--dg-text-muted)]">Pod 管理策略</span>
<span className="text-[var(--dg-text-muted)]">{t('kubernetes.statefulset.podManagementPolicy')}</span>
<span className="text-[var(--dg-text-secondary)] font-mono">{spec.podManagementPolicy || 'OrderedReady'}</span>
</div>
)}
<div className="flex justify-between">
<span className="text-[var(--dg-text-muted)]">修订历史限制</span>
<span className="text-[var(--dg-text-muted)]">{t('kubernetes.workloadviewpage.s450a4e')}</span>
<span className="text-[var(--dg-text-secondary)] font-mono">{spec.revisionHistoryLimit ?? 10}</span>
</div>
</CardContent>
@@ -369,10 +373,10 @@ export default function WorkloadViewPage() {
<Table>
<TableHeader>
<TableRow className="border-[var(--dg-border)] hover:bg-transparent">
<TableHead className="text-[var(--dg-text-secondary)]">名称</TableHead>
<TableHead className="text-[var(--dg-text-secondary)]">访问模式</TableHead>
<TableHead className="text-[var(--dg-text-secondary)]">存储类</TableHead>
<TableHead className="text-[var(--dg-text-secondary)]">大小</TableHead>
<TableHead className="text-[var(--dg-text-secondary)]">{t('common.name')}</TableHead>
<TableHead className="text-[var(--dg-text-secondary)]">{t('k8s.resource.accessMode')}</TableHead>
<TableHead className="text-[var(--dg-text-secondary)]">{t('k8s.resource.storageClass')}</TableHead>
<TableHead className="text-[var(--dg-text-secondary)]">{t('deploy.table.size')}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
@@ -414,7 +418,7 @@ export default function WorkloadViewPage() {
</div>
)}
{containers.length === 0 ? (
<p className="text-xs text-[var(--dg-text-muted)] py-3">暂无容器数据,请查看 YAML</p>
<p className="text-xs text-[var(--dg-text-muted)] py-3">{t('kubernetes.workloadviewpage.se4b610')}</p>
) : (
<div className="space-y-3">
<p className="text-[11px] text-[var(--dg-text-muted)] mb-1 uppercase tracking-wide">Containers</p>
@@ -437,17 +441,17 @@ export default function WorkloadViewPage() {
<Table>
<TableHeader>
<TableRow className="border-[var(--dg-border)] hover:bg-transparent">
<TableHead className="text-[var(--dg-text-secondary)]">Pod 名称</TableHead>
<TableHead className="text-[var(--dg-text-secondary)]">状态</TableHead>
<TableHead className="text-[var(--dg-text-secondary)]">所在节点</TableHead>
<TableHead className="text-[var(--dg-text-secondary)]">{t('kubernetes.pod.podName')}</TableHead>
<TableHead className="text-[var(--dg-text-secondary)]">{t('common.status')}</TableHead>
<TableHead className="text-[var(--dg-text-secondary)]">{t('kubernetes.pod.nodeName')}</TableHead>
<TableHead className="text-[var(--dg-text-secondary)]">Pod IP</TableHead>
<TableHead className="text-[var(--dg-text-secondary)]">重启</TableHead>
<TableHead className="text-[var(--dg-text-secondary)]">操作</TableHead>
<TableHead className="text-[var(--dg-text-secondary)]">{t('common.reboot')}</TableHead>
<TableHead className="text-[var(--dg-text-secondary)]">{t('common.actions')}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{pods.length === 0 ? (
<TableRow><TableCell colSpan={6} className="text-center text-[var(--dg-text-muted)] py-8">暂无 Pod</TableCell></TableRow>
<TableRow><TableCell colSpan={6} className="text-center text-[var(--dg-text-muted)] py-8">{t('k8s.deploymentDetail.noPod')}</TableCell></TableRow>
) : pods.map((pod: JsonData) => (
<TableRow key={pod.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
<TableCell className="font-medium text-[var(--dg-text-primary)]">{pod.name}</TableCell>
@@ -484,7 +488,7 @@ export default function WorkloadViewPage() {
{scaleModal && (
<div className="fixed inset-0 bg-black/60 z-50 flex items-center justify-center" onClick={() => setScaleModal(false)}>
<div className="bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)] rounded-lg p-6 w-80" onClick={e => e.stopPropagation()}>
<h3 className="text-sm font-semibold text-[var(--dg-text-primary)] mb-4">伸缩 StatefulSet</h3>
<h3 className="text-sm font-semibold text-[var(--dg-text-primary)] mb-4">{t('kubernetes.workloadviewpage.s7f541e')}</h3>
<div className="flex items-center justify-center gap-4 mb-4">
<Button variant="outline" size="icon" className="h-9 w-9" onClick={() => setScaleValue(Math.max(0, scaleValue - 1))}>
<Minus className="h-4 w-4" />
@@ -495,9 +499,9 @@ export default function WorkloadViewPage() {
</Button>
</div>
<div className="flex gap-2">
<Button variant="outline" size="sm" className="flex-1 border-[var(--dg-border)] text-[var(--dg-text-secondary)]" onClick={() => setScaleModal(false)}>取消</Button>
<Button variant="outline" size="sm" className="flex-1 border-[var(--dg-border)] text-[var(--dg-text-secondary)]" onClick={() => setScaleModal(false)}>{t('common.cancel')}</Button>
<Button size="sm" className="flex-1 bg-[var(--dg-blue)] hover:opacity-90" disabled={actionLoading} onClick={handleScale}>
{actionLoading ? '执行中...' : '确认'}
{actionLoading ? t('pipeline.executing') : t('common.confirm')}
</Button>
</div>
</div>
@@ -544,6 +548,7 @@ export default function WorkloadViewPage() {
// Container info sub-component
function ContainerInfo({ c }: { c: JsonData}) {
const { t } = useTranslation();
return (
<div className="border border-[var(--dg-border)] rounded-lg p-3 bg-[var(--dg-bg-primary)]/30">
<div className="flex items-center justify-between mb-2">