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

@@ -6,6 +6,7 @@ import { CodeSnippetBlock } from '@/components/ui/code-snippet-block';
import { authApi } from '@/api';
import { getPublicApiBase } from '@/utils/request';
import { getSessionApiAccessKey } from '@/utils/storage';
import { useTranslation } from 'react-i18next';
export interface CICDWebhookContentProps {
clusterCode: string;
@@ -24,6 +25,7 @@ export function CICDWebhookContent({
image = '',
containerName = '',
}: CICDWebhookContentProps) {
const { t } = useTranslation();
const apiBase = getPublicApiBase();
const [keyLoading, setKeyLoading] = useState(true);
const [hasKey, setHasKey] = useState(false);
@@ -55,7 +57,7 @@ export function CICDWebhookContent({
const restartUrl = `${apiBase}/kubernetes/${clusterCode}/namespace/${namespace}/deployments/${deploymentName}/restart`;
const updateBody = useMemo(() => {
const body: Record<string, string> = { image: image || '<新镜像地址>' };
const body: Record<string, string> = { image: image || t('kubernetes.cicdwebhookcontent.sc4be9c') };
if (containerName) body.container_name = containerName;
return JSON.stringify(body);
}, [image, containerName]);
@@ -65,16 +67,12 @@ export function CICDWebhookContent({
-H 'X-AIOps-Access-Key: ${accessKey}' \\
-H 'Content-Type: application/json' \\
-d '${updateBody}'`
: `curl -X POST '${updateUrl}' \\
-H 'X-AIOps-Access-Key: <个人中心生成的访问密钥>' \\
-H 'Content-Type: application/json' \\
-d '${updateBody}'`;
: t('kubernetes.cicdwebhookcontent.s1976d2', { updateUrl: updateUrl, updateBody: updateBody });
const restartCurl = accessKey
? `curl -X POST '${restartUrl}' \\
-H 'X-AIOps-Access-Key: ${accessKey}'`
: `curl -X POST '${restartUrl}' \\
-H 'X-AIOps-Access-Key: <个人中心生成的访问密钥>'`;
: t('kubernetes.cicdwebhookcontent.s0e3668', { restartUrl: restartUrl });
const canCopy = !!accessKey && !keyExpired;
@@ -88,48 +86,48 @@ export function CICDWebhookContent({
) : keyExpired ? (
<>
访问凭证已过期,请前往
<Link to="/profile" className="text-[var(--dg-blue)] hover:underline mx-1">个人中心</Link>
<Link to="/profile" className="text-[var(--dg-blue)] hover:underline mx-1">{t('header.profile')}</Link>
调整有效期或轮换凭证后再复制 curl。
</>
) : accessKey ? (
<>已填入本机保存的访问凭证,复制 curl 后可直接在终端执行触发更新。</>
<>{t('kubernetes.cicdwebhookcontent.scd2b7d')}</>
) : hasKey ? (
<>
已检测到访问凭证,但本机未缓存明文。请前往
<Link to="/profile" className="text-[var(--dg-blue)] hover:underline mx-1">个人中心</Link>
<Link to="/profile" className="text-[var(--dg-blue)] hover:underline mx-1">{t('header.profile')}</Link>
轮换凭证一次,之后 curl 将自动填入。
</>
) : (
<>
请先在
<Link to="/profile" className="text-[var(--dg-blue)] hover:underline mx-1">个人中心</Link>
<Link to="/profile" className="text-[var(--dg-blue)] hover:underline mx-1">{t('header.profile')}</Link>
签发 CI/CD 访问凭证,curl 将自动填入。
</>
)}
</div>
<CodeSnippetBlock
label="更新镜像"
label=t('kubernetes.deployment.updateImage')
code={updateCurl}
copyLabel="curl 已复制,可直接粘贴到终端执行"
copyLabel=t('kubernetes.cicdwebhookcontent.s25a990')
copyDisabled={!canCopy}
copyDisabledTitle="请先在个人中心签发访问凭证"
copyDisabledTitle=t('kubernetes.cicdwebhookcontent.sc3bc41')
/>
<CodeSnippetBlock
label="滚动重启"
label=t('kubernetes.deployment.restart')
code={restartCurl}
copyLabel="curl 已复制,可直接粘贴到终端执行"
copyLabel=t('kubernetes.cicdwebhookcontent.s25a990')
copyDisabled={!canCopy}
copyDisabledTitle="请先在个人中心签发访问凭证"
copyDisabledTitle=t('kubernetes.cicdwebhookcontent.sc3bc41')
/>
<div className="text-xs text-[var(--dg-text-muted)] space-y-1">
<p>• 示例镜像:<code className="text-[var(--dg-text-secondary)]">{image || '—'}</code>
{containerName ? <> · 容器 <code className="text-[var(--dg-text-secondary)]">{containerName}</code></> : null}
<p>{t('kubernetes.cicdwebhookcontent.s1bdaf4')}<code className="text-[var(--dg-text-secondary)]">{image || '—'}</code>
{containerName ? <> {t('kubernetes.cicdwebhookcontent.s9b07e8')} <code className="text-[var(--dg-text-secondary)]">{containerName}</code></> : null}
</p>
<p>• 使用长期 API 密钥,不会随登录 JWT 过期</p>
<p>• 修改 <code className="text-[var(--dg-text-secondary)]">image</code> 字段后执行 curl 即可触发滚动更新</p>
<p>• 也可用 <code className="text-[var(--dg-text-secondary)]">Authorization: Bearer &lt;密钥&gt;</code></p>
<p>{t('kubernetes.cicdwebhookcontent.se067d1')}</p>
<p>{t('kubernetes.cicdwebhookcontent.s311998')} <code className="text-[var(--dg-text-secondary)]">image</code> {t('kubernetes.cicdwebhookcontent.s55260c')}</p>
<p>{t('kubernetes.cicdwebhookcontent.s4e20ba')} <code className="text-[var(--dg-text-secondary)]">{t('kubernetes.cicdwebhookcontent.s67cc60')}</code></p>
</div>
</div>
);
@@ -7,6 +7,7 @@ import { DeploymentEditContainers } from '../DeploymentEditContainers';
import { DeploymentEditStorage } from '../DeploymentEditStorage';
import { DeploymentEditAdvanced } from '../DeploymentEditAdvanced';
import { DeploymentEditService } from '../DeploymentEditService';
import { useTranslation } from 'react-i18next';
export interface EditModeContainerProps {
clusterCode: string;
@@ -20,6 +21,7 @@ export interface EditModeContainerProps {
}
export function EditModeContainer({ clusterCode, namespace, deploymentName, manifest, onSaved, onCancel, isCreate = false }: EditModeContainerProps) {
const { t } = useTranslation();
type EditTab = 'basic' | 'containers' | 'storage' | 'advanced' | 'service';
const [activeTab, setActiveTab] = useState<EditTab>('basic');
const [editManifest, setEditManifest] = useState<any>(manifest);
@@ -34,11 +36,11 @@ export function EditModeContainer({ clusterCode, namespace, deploymentName, mani
};
const tabs: Array<{ key: EditTab; label: string; icon: typeof Settings }> = [
{ key: 'basic', label: '基本信息', icon: Settings },
{ key: 'containers', label: '容器信息', icon: Container },
{ key: 'storage', label: '存储挂载', icon: Database },
{ key: 'advanced', label: '高级设置', icon: Settings },
{ key: 'service', label: '服务/应用路由', icon: Network },
{ key: 'basic', label: t('deploy.label.basicInfo'), icon: Settings },
{ key: 'containers', label: t('kubernetes.editmodecontainer.sd58c2e'), icon: Container },
{ key: 'storage', label: t('kubernetes.editmodecontainer.s9ffdac'), icon: Database },
{ key: 'advanced', label: t('kubernetes.editmodecontainer.se37075'), icon: Settings },
{ key: 'service', label: t('kubernetes.editmodecontainer.s8bdc2d'), icon: Network },
];
const commonProps = {
@@ -9,6 +9,7 @@ import { kubernetesApi } from '@/api';
import { useToast } from '@/hooks/use-toast';
import { useUserStore } from '@/store';
import type { JsonData } from '@/types/common';
import { useTranslation } from 'react-i18next';
export interface PodLogsModalProps {
clusterCode: string;
@@ -19,6 +20,7 @@ export interface PodLogsModalProps {
}
export function PodLogsModal({ clusterCode, namespace, podName, containers, onClose }: PodLogsModalProps) {
const { t } = useTranslation();
const { toast } = useToast();
const [logs, setLogs] = useState('');
const [tail, setTail] = useState(200);
@@ -30,9 +32,9 @@ export function PodLogsModal({ clusterCode, namespace, podName, containers, onCl
setLoading(true);
try {
const res: JsonData = await kubernetesApi.getPodLogs(clusterCode, namespace, podName, tail, container || undefined);
setLogs(res?.data?.logs || res?.logs || res || '无日志');
setLogs(res?.data?.logs || res?.logs || res || t('kubernetes.podlogsmodal.s4ff3b4'));
} catch {
setLogs('获取日志失败');
setLogs(t('kubernetes.podlogsmodal.sc93f33'));
} finally { setLoading(false); }
};
@@ -57,7 +59,7 @@ export function PodLogsModal({ clusterCode, namespace, podName, containers, onCl
setLogs(buf);
}
} catch {
toast({ title: '流式日志连接中断', variant: 'destructive' });
toast({ title: t('kubernetes.podlogsmodal.s6273c9'), variant: 'destructive' });
} finally { setStreaming(false); }
};
@@ -77,22 +79,22 @@ export function PodLogsModal({ clusterCode, namespace, podName, containers, onCl
{containers.map(c => <option key={c} value={c}>{c}</option>)}
</select>
)}
<span className="text-[11px] dg-text-muted">最近</span>
<span className="text-[11px] dg-text-muted">{t('k8s.deploymentDetail.recent')}</span>
<Input type="number" value={tail} onChange={e => setTail(Number(e.target.value) || 200)}
className="w-16 h-7 dg-input text-xs" />
<span className="text-[11px] dg-text-muted">行</span>
<span className="text-[11px] dg-text-muted">{t('k8s.deploymentDetail.lines')}</span>
<Button size="sm" variant="outline" className="h-7 border-[var(--dg-border)] text-[var(--dg-text-secondary)] text-xs" onClick={loadLogs} disabled={loading}>
{loading ? <Loader2 className="h-3 w-3 animate-spin" /> : <RefreshCw className="h-3 w-3" />}
</Button>
<Button size="sm" variant="outline" className="h-7 border-[var(--dg-border)] dg-text-green text-xs" onClick={startStream} disabled={streaming}>
{streaming ? '流中...' : '实时流'}
{streaming ? t('kubernetes.podlogsmodal.s2d4fb3') : t('monitor.logStreaming')}
</Button>
<Button variant="ghost" size="icon" className="h-7 w-7 dg-text-secondary" onClick={onClose}><X className="h-4 w-4" /></Button>
</div>
</div>
<div className="flex-1 overflow-auto p-3">
<pre className="bg-[var(--dg-bg-primary)] rounded p-3 text-xs text-[var(--dg-text-secondary)] font-mono whitespace-pre-wrap max-h-[60vh] overflow-auto">
{logs || '无日志内容'}
{logs || t('kubernetes.podlogsmodal.s99eac0')}
</pre>
</div>
</div>
@@ -6,6 +6,7 @@ import { Label } from '@/components/ui/label';
import { ArrowRight, Loader2 } from 'lucide-react';
import { joinImageRef, parseImageRef } from '../image-ref-utils';
import { ModalShell } from './ModalShell';
import { useTranslation } from 'react-i18next';
export interface UpdateImageModalProps {
deployment: any;
@@ -24,6 +25,7 @@ export function UpdateImageModal({
onClose,
onUpdateImage,
}: UpdateImageModalProps) {
const { t } = useTranslation();
const currentImage = deployment?.image || '';
const current = useMemo(() => parseImageRef(currentImage), [currentImage]);
@@ -46,14 +48,14 @@ export function UpdateImageModal({
};
return (
<ModalShell title="调整镜像版本" onClose={onClose} size="xl">
<ModalShell title=t('kubernetes.updateimagemodal.s22a2ed') onClose={onClose} size="xl">
<div className="space-y-5">
{/* 当前镜像:只读展示完整地址与 Tag // Logan.Li */}
<div className="rounded-lg border border-[var(--dg-border)]/80 bg-[var(--dg-bg-tertiary)]/40 p-4 space-y-3">
<p className="text-xs font-medium dg-text-secondary">当前镜像</p>
<p className="text-xs font-medium dg-text-secondary">{t('kubernetes.deployment.currentImage')}</p>
<div className="grid grid-cols-[1fr_180px] gap-3">
<div>
<Label className="dg-text-muted text-xs">镜像地址</Label>
<Label className="dg-text-muted text-xs">{t('kubernetes.updateimagemodal.sdbe6a6')}</Label>
<p
className="mt-1.5 text-sm text-[var(--dg-text-primary)] font-mono break-all leading-relaxed bg-[var(--dg-bg-secondary)]/60 rounded-md px-3 py-2.5 min-h-[42px]"
title={current.repository || '-'}
@@ -62,7 +64,7 @@ export function UpdateImageModal({
</p>
</div>
<div>
<Label className="dg-text-muted text-xs">当前 Tag</Label>
<Label className="dg-text-muted text-xs">{t('kubernetes.updateimagemodal.sf08c71')}</Label>
<p
className="mt-1.5 text-sm text-cyan-300 font-mono break-all leading-relaxed bg-[var(--dg-bg-secondary)]/60 rounded-md px-3 py-2.5 min-h-[42px]"
title={current.tag || '-'}
@@ -82,10 +84,10 @@ export function UpdateImageModal({
{/* 新镜像:地址与 Tag 分栏编辑 // Logan.Li */}
<div className="rounded-lg border border-blue-500/30 bg-blue-500/5 p-4 space-y-3">
<p className="text-xs font-medium text-blue-300/90">新镜像</p>
<p className="text-xs font-medium text-blue-300/90">{t('kubernetes.deployment.newImage')}</p>
<div className="grid grid-cols-[1fr_180px] gap-3">
<div>
<Label className="dg-text-secondary text-xs">镜像地址</Label>
<Label className="dg-text-secondary text-xs">{t('kubernetes.updateimagemodal.sdbe6a6')}</Label>
<Input
value={newRepo}
onChange={e => {
@@ -98,7 +100,7 @@ export function UpdateImageModal({
/>
</div>
<div>
<Label className="dg-text-secondary text-xs">新版本 Tag</Label>
<Label className="dg-text-secondary text-xs">{t('kubernetes.updateimagemodal.s6a38a8')}</Label>
<Input
value={newTag}
onChange={e => {
@@ -113,7 +115,7 @@ export function UpdateImageModal({
</div>
<div className="flex flex-wrap items-center gap-2 text-xs">
<span className="dg-text-muted">Tag 变更:</span>
<span className="dg-text-muted">{t('kubernetes.updateimagemodal.sec74e0')}</span>
<code className="px-2 py-0.5 rounded bg-[var(--dg-bg-tertiary)] dg-text-secondary font-mono">{current.tag}</code>
<ArrowRight className="h-3 w-3 dg-text-muted" />
<code
@@ -124,10 +126,10 @@ export function UpdateImageModal({
{newTag.trim() || '-'}
</code>
{!tagChanged && !repoChanged && (
<span className="dg-text-muted">(未修改)</span>
<span className="dg-text-muted">{t('kubernetes.updateimagemodal.s47c8cf')}</span>
)}
{repoChanged && !tagChanged && (
<span className="dg-text-yellow/80">地址已变更</span>
<span className="dg-text-yellow/80">{t('kubernetes.updateimagemodal.sdb3122')}</span>
)}
</div>
@@ -145,7 +147,7 @@ export function UpdateImageModal({
disabled={actionLoading === 'image' || !previewImage.trim() || !hasChanges}
onClick={onUpdateImage}
>
{actionLoading === 'image' ? <Loader2 className="h-4 w-4 animate-spin" /> : '更新镜像'}
{actionLoading === 'image' ? <Loader2 className="h-4 w-4 animate-spin" /> : i18n.t('kubernetes.deployment.updateImage')}
</Button>
</div>
</div>