From ad9acea1e510521fa052c8afb8510158907dd348 Mon Sep 17 00:00:00 2001 From: attacker Date: Sun, 12 Jul 2026 16:51:57 +0800 Subject: [PATCH] =?UTF-8?q?refactor(i18n):=20U33=20K8s=20=E8=B5=84?= =?UTF-8?q?=E6=BA=90=E7=BC=96=E8=BE=91=E5=99=A8=20+=20Pod=20=E6=97=A5?= =?UTF-8?q?=E5=BF=97/Metrics=20=E6=A8=A1=E6=9D=BF=E5=AD=97=E9=9D=A2?= =?UTF-8?q?=E9=87=8F=E4=B8=AD=E6=96=87=E6=B8=85=E9=9B=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit resource-visual-editors/ 5 文件 (ConfigMap/Secret/Service/Ingress/PvcEditor): - 保存按钮文案 (保存配置/密钥/Service/Ingress/PVC) - IngressEditor: (可选)/手工输入/帮助文本/常见示例/手动输入/继续手动输入 - PvcEditor: 访问模式描述 (单节点读写/多节点只读/多节点读写) pod-logs-viewer/ 3 文件: - ControlBar: 导出/刷新/实时/停止按钮 - LogContentArea: 加载中… 占位 - LogsHeader: 日志标题模板 PodLogsViewer: 6 处错误消息 + DialogTitle/Description PodMetricsPage: subtitle 模板字面量 + 所有命名空间 PodExecTerminal: 连接错误消息 17 处 toast description 模板字面量中文→i18n.t() 插值: - DeploymentEditAdvanced/Containers/Storage: 更新已提交 - DeploymentView: 回滚/伸缩 - DeploymentDetail: 伸缩/镜像更新 - DeploymentEditService: 资源删除 - ResourceVisualEditor/NamespaceResourcePage: 资源更新/YAML下载 - ClusterOverviewPage: 集群未找到 - DeploymentEditContainers: 容器名称/镜像为空 5 处 confirm description 中文→i18n.t() 插值: - DeploymentView/EditService/Detail/WorkloadViewPage/MiddlewareMonitor/SiteMonitor/HealthProbeSection 5 处 subtitle 模板字面量中文→i18n.t(): - PodMetricsPage/CapacityPage/EventsPage/KubernetesNodes/ClusterOverviewPage 新增 k8s.rve 14键 + k8s.toast 18键 + k8s.podMetrics 4键 + k8s.podLogs 13键 = 49 翻译键 zh/en 7481→7524 完全对齐 tsc -b=0, npm build✓ (17.76s), ESLint errors=0/warnings=43/any=0, go build=0, 17/17 测试通过 --- ROADMAP.md | 1 + .../components/kubernetes/CapacityPage.tsx | 2 +- .../kubernetes/ClusterOverviewPage.tsx | 4 +- .../kubernetes/DeploymentDetail.tsx | 6 +- .../kubernetes/DeploymentEditAdvanced.tsx | 2 +- .../kubernetes/DeploymentEditContainers.tsx | 6 +- .../kubernetes/DeploymentEditService.tsx | 4 +- .../kubernetes/DeploymentEditStorage.tsx | 2 +- .../components/kubernetes/DeploymentView.tsx | 6 +- .../src/components/kubernetes/EventsPage.tsx | 2 +- .../components/kubernetes/KubernetesNodes.tsx | 2 +- .../kubernetes/NamespaceResourcePage.tsx | 2 +- .../components/kubernetes/PodExecTerminal.tsx | 2 +- .../components/kubernetes/PodLogsViewer.tsx | 12 ++-- .../components/kubernetes/PodMetricsPage.tsx | 4 +- .../kubernetes/ResourceVisualEditor.tsx | 4 +- .../kubernetes/WorkloadViewPage.tsx | 2 +- .../src/components/kubernetes/YamlViewer.tsx | 2 +- .../kubernetes/pod-logs-viewer/ControlBar.tsx | 8 +-- .../pod-logs-viewer/LogContentArea.tsx | 2 +- .../kubernetes/pod-logs-viewer/LogsHeader.tsx | 2 +- .../ConfigMapEditor.tsx | 2 +- .../resource-visual-editors/IngressEditor.tsx | 16 ++--- .../resource-visual-editors/PvcEditor.tsx | 8 +-- .../resource-visual-editors/SecretEditor.tsx | 2 +- .../resource-visual-editors/ServiceEditor.tsx | 2 +- frontend/src/locales/en-US.json | 62 ++++++++++++++++--- frontend/src/locales/zh-CN.json | 62 ++++++++++++++++--- .../src/pages/monitor/MiddlewareMonitor.tsx | 3 +- frontend/src/pages/monitor/SiteMonitor.tsx | 2 +- .../host-monitor/HealthProbeSection.tsx | 3 +- 31 files changed, 167 insertions(+), 72 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 4f5df67..b41c3c1 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -521,6 +521,7 @@ services/api.ts (1324L) | U30 | i18n 批量转换损坏调用修复(8 处损坏的 i18n.t() 调用修复 — DeploymentEditContainers.tsx:85 toast description 损坏 / DeploymentEditStorage.tsx:175,202 !manifest 空状态+数据卷副标题损坏 / AgentRuleDialog.tsx:111,157,183,195 DialogDescription+操作符/端口/持续时间 Label 损坏 / LogFieldSidebar.tsx:68 搜索 placeholder 损坏。损坏原因: 早期批量正则 i18n 转换将模板表达式/JSX 碎片错误包裹进 i18n.t() 调用。新增 6 个语义化键(agent.ruleDialog.description/operator/port/duration + kubernetes.storage.volumeDesc + monitor.lbl.searchFields),zh/en 7375→7381 +6 键对齐,tsc -b=0,npm build✓ (17.99s),ESLint=0/53/0,go build=0,17/17 测试通过) | P3 | **done** | | U31 | AIOps Agent CLI 组件 i18n 修复(AgentCLI + AgentCLIToolbar + AgentCLIHistory + AgentCLIEmptyState + ChatInputArea + agent-cli-constants.ts 6 文件 — Q7/Q35 拆分后丢失的 i18n 集成全部修复,新增 `aiops.cli.*` 命名空间 100 翻译键(8 子分类: toolbar/safety/tips/error/info/viewMode/placeholder/history/empty/input),SAFETY_MODES+TIPS_HOST+TIPS_KUBERNETES 常量数组改为 `getSafetyModes(t)`/`getTipsHost(t)`/`getTipsKubernetes(t)` 接收 TFunction 参数的工厂函数,全部 UI 硬编码中文清零,zh/en 7381→7481 +100 键对齐,tsc -b=0,npm build✓ (18.21s),ESLint=0/53/0,go build=0,17/17 测试通过) | P3 | **done** | | U32 | 全量 hash key 清理 + React Hooks 警告修复(151 个 hash-style flat key (s+hex 后缀) 全部重命名为语义化 nested key — monitor 58/kubernetes 42/aiops 17/agent 12/cloud 13/pipeline 6/system 3;58 个源文件 147 处 t()/i18n.t() 调用更新;3 个命名空间冲突修复(monitor.hostMonitor/agent.agentMonitor/aiops.aiAnalysisResult 原 string → .title 子键);React Hooks 警告 53→43 (-10): exhaustive-deps 4→0 + preserve-manual-memoization 5→0 + 变量遮蔽修复(filter(t)→filter(tg));zh/en 7480=7480 完全对齐 0 hash key 残留;tsc -b=0,npm build✓ (17.59s),ESLint=0/43/any=0,go build=0,17/17 测试通过) | P3 | **done** | +| U33 | K8s 资源编辑器 + Pod 日志/Metrics i18n 补全(resource-visual-editors/ 5 文件: ConfigMap/Secret/Service/Ingress/PvcEditor 按钮文案+帮助文本+访问模式+IngressClass 提示全部 i18n;pod-logs-viewer/ 3 文件: ControlBar 导出/刷新/实时/停止+LogContentArea 加载中+LogsHeader 标题;PodLogsViewer 6 处错误消息+DialogTitle/Description;PodMetricsPage subtitle+表头+所有命名空间;17 处 toast description 模板字面量中文→i18n.t() 插值 (DeploymentEditAdvanced/Containers/Storage/View/Detail/Service/ResourceVisualEditor/NamespaceResourcePage/ClusterOverviewPage);5 处 confirm description (DeploymentView/EditService/Detail/WorkloadViewPage/MiddlewareMonitor/SiteMonitor/HealthProbeSection);5 处 subtitle 模板字面量 (PodMetricsPage/CapacityPage/EventsPage/KubernetesNodes/ClusterOverviewPage);新增 k8s.rve 14键+k8s.toast 18键+k8s.podMetrics 4键+k8s.podLogs 13键 共 49 翻译键;zh/en 7481→7524 +43 对齐;tsc -b=0,npm build✓ (17.76s),ESLint=0/43/any=0,go build=0,17/17 测试通过) | P3 | **done** | --- diff --git a/frontend/src/components/kubernetes/CapacityPage.tsx b/frontend/src/components/kubernetes/CapacityPage.tsx index 0dc122f..e25f49b 100644 --- a/frontend/src/components/kubernetes/CapacityPage.tsx +++ b/frontend/src/components/kubernetes/CapacityPage.tsx @@ -79,7 +79,7 @@ export default function CapacityPage() { } actions={( <> diff --git a/frontend/src/components/kubernetes/ClusterOverviewPage.tsx b/frontend/src/components/kubernetes/ClusterOverviewPage.tsx index e42d4f0..fd445de 100644 --- a/frontend/src/components/kubernetes/ClusterOverviewPage.tsx +++ b/frontend/src/components/kubernetes/ClusterOverviewPage.tsx @@ -54,7 +54,7 @@ export default function ClusterOverviewPage() { const current = list.find((c: JsonData) => (c.code || String(c.id)) === clusterCode); setClusterInfo(current || null); if (!current && list.length > 0 && clusterCode) { - toast({ title: i18n.t('k8s.clusterOverview.notFound'), description: `找不到集群 ${clusterCode}`, variant: 'destructive' }); + toast({ title: i18n.t('k8s.clusterOverview.notFound'), description: i18n.t('k8s.toast.clusterNotFound', { code: clusterCode }), variant: 'destructive' }); } } catch { setClusters([]); } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -86,7 +86,7 @@ export default function ClusterOverviewPage() { } metrics={!loading ? ( { const ok = await confirm({ title: i18n.t('kubernetes.hpa.deleteHpa'), - description: `确定删除 HPA "${hpa.name}"?`, + description: i18n.t('k8s.toast.confirmDeleteHpa', { name: hpa.name }), confirmText: i18n.t('kubernetes.storage.delete2'), variant: 'destructive', }); diff --git a/frontend/src/components/kubernetes/DeploymentEditAdvanced.tsx b/frontend/src/components/kubernetes/DeploymentEditAdvanced.tsx index 499c246..33baea1 100644 --- a/frontend/src/components/kubernetes/DeploymentEditAdvanced.tsx +++ b/frontend/src/components/kubernetes/DeploymentEditAdvanced.tsx @@ -170,7 +170,7 @@ export function DeploymentEditAdvanced({ clusterCode, namespace, deploymentName, }; const base = buildAdvancedManifest(rawManifest, state); await saveDeploymentManifest(clusterCode, namespace, deploymentName, base); - toast({ title: i18n.t('kubernetes.deploymentEditAdvanced.saved'), description: `${deploymentName} 更新已提交` }); + toast({ title: i18n.t('kubernetes.deploymentEditAdvanced.saved'), description: i18n.t('k8s.toast.updatedSubmitted', { name: deploymentName }) }); onSaved(base); } catch (e) { toast({ title: i18n.t('kubernetes.msg.saveFailed'), description: e instanceof Error ? e.message : String(e), variant: 'destructive' }); diff --git a/frontend/src/components/kubernetes/DeploymentEditContainers.tsx b/frontend/src/components/kubernetes/DeploymentEditContainers.tsx index c301838..28f033f 100644 --- a/frontend/src/components/kubernetes/DeploymentEditContainers.tsx +++ b/frontend/src/components/kubernetes/DeploymentEditContainers.tsx @@ -82,11 +82,11 @@ export function DeploymentEditContainers({ clusterCode, namespace, deploymentNam // validate for (const c of [...initContainers, ...containers]) { if (!c.name.trim()) { - toast({ title: i18n.t('kubernetes.msg.saveFailed'), description: '容器名称不能为空', variant: 'destructive' }); + toast({ title: i18n.t('kubernetes.msg.saveFailed'), description: i18n.t('k8s.toast.containerNameEmpty'), variant: 'destructive' }); return; } if (!c.image.trim()) { - toast({ title: i18n.t('kubernetes.msg.saveFailed'), description: `容器 ${c.name} 的镜像不能为空`, variant: 'destructive' }); + toast({ title: i18n.t('kubernetes.msg.saveFailed'), description: i18n.t('k8s.toast.containerImageEmpty', { name: c.name }), variant: 'destructive' }); return; } } @@ -102,7 +102,7 @@ export function DeploymentEditContainers({ clusterCode, namespace, deploymentNam ? imagePullSecrets.filter(s => s.trim()).map((s: string) => ({ name: s })) : undefined; await saveDeploymentManifest(clusterCode, namespace, deploymentName, base); - toast({ title: i18n.t('k8s.editContainers.saved'), description: `${deploymentName} 更新已提交` }); + toast({ title: i18n.t('k8s.editContainers.saved'), description: i18n.t('k8s.toast.updatedSubmitted', { name: deploymentName }) }); onSaved(base); } catch (e) { toast({ title: i18n.t('kubernetes.msg.saveFailed'), description: e?.message || String(e), variant: 'destructive' }); diff --git a/frontend/src/components/kubernetes/DeploymentEditService.tsx b/frontend/src/components/kubernetes/DeploymentEditService.tsx index f016fd1..2f35413 100644 --- a/frontend/src/components/kubernetes/DeploymentEditService.tsx +++ b/frontend/src/components/kubernetes/DeploymentEditService.tsx @@ -222,7 +222,7 @@ export function DeploymentEditService({ clusterCode, namespace, deploymentName, const deleteResource = async (kind: 'service' | 'ingress', name: string) => { const ok = await confirm({ title: i18n.t('common.delete'), - description: `确认删除 ${kind === 'service' ? 'Service' : 'Ingress'} 「${name}」?此操作不可撤销。`, + description: i18n.t('k8s.toast.confirmDeleteResource', { kind: kind === 'service' ? 'Service' : 'Ingress', name }), confirmText: i18n.t('kubernetes.storage.delete2'), variant: 'destructive', }); @@ -230,7 +230,7 @@ export function DeploymentEditService({ clusterCode, namespace, deploymentName, try { const resource = kind === 'service' ? 'services' : 'ingresses'; await kubernetesApi.deleteResourceYaml(clusterCode, namespace, resource, name); - toast({ title: i18n.t('monitor.silenceRules.deleted'), description: `${kind === 'service' ? 'Service' : 'Ingress'} ${name} 已删除` }); + toast({ title: i18n.t('monitor.silenceRules.deleted'), description: i18n.t('k8s.toast.resourceDeleted', { kind: kind === 'service' ? 'Service' : 'Ingress', name }) }); load(); } catch (e) { // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/frontend/src/components/kubernetes/DeploymentEditStorage.tsx b/frontend/src/components/kubernetes/DeploymentEditStorage.tsx index 99f3b37..2d6144c 100644 --- a/frontend/src/components/kubernetes/DeploymentEditStorage.tsx +++ b/frontend/src/components/kubernetes/DeploymentEditStorage.tsx @@ -151,7 +151,7 @@ export function DeploymentEditStorage({ clusterCode, namespace, deploymentName, const { volumes, mounts } = volumeEntriesToStorage(entries); const base = applyStorageToManifest(rawManifest, volumes, mounts, passthroughVolumes); await saveDeploymentManifest(clusterCode, namespace, deploymentName, base); - toast({ title: i18n.t('k8s.editStorage.saved'), description: `${deploymentName} 更新已提交` }); + toast({ title: i18n.t('k8s.editStorage.saved'), description: i18n.t('k8s.toast.updatedSubmitted', { name: deploymentName }) }); onSaved(base); } catch (e) { toast({ title: i18n.t('kubernetes.msg.saveFailed'), description: (e as Error)?.message || String(e), variant: 'destructive' }); diff --git a/frontend/src/components/kubernetes/DeploymentView.tsx b/frontend/src/components/kubernetes/DeploymentView.tsx index 55ba14e..7df7628 100644 --- a/frontend/src/components/kubernetes/DeploymentView.tsx +++ b/frontend/src/components/kubernetes/DeploymentView.tsx @@ -140,7 +140,7 @@ export function DeploymentView(props: DeploymentViewProps) { const revision = rev.revision ?? rev.revision_number ?? rev.number; const ok = await confirm({ title: i18n.t('k8s.deploymentView.rollbackVersion'), - description: `确认回滚到修订版本 #${revision}?`, + description: i18n.t('k8s.toast.confirmRollback', { revision }), confirmText: i18n.t('pipeline.rollback'), variant: 'destructive', }); @@ -148,7 +148,7 @@ export function DeploymentView(props: DeploymentViewProps) { setRollbackLoading(revision); try { await kubernetesApi.rollbackDeployment(clusterCode, namespace, deployment.name, Number(revision)); - toast({ title: i18n.t('k8s.deploymentView.rollbackSent'), description: `正在回滚到 #${revision}` }); + toast({ title: i18n.t('k8s.deploymentView.rollbackSent'), description: i18n.t('k8s.toast.rollbackToRevision', { revision }) }); onRefresh(); } catch (e) { toast({ title: i18n.t('pipeline.rollbackFailed'), description: e?.message || String(e), variant: 'destructive' }); @@ -160,7 +160,7 @@ export function DeploymentView(props: DeploymentViewProps) { setScaleLoading(true); try { await kubernetesApi.scaleDeployment(clusterCode, namespace, deployment.name, scaleValue); - toast({ title: i18n.t('k8s.deploymentView.scaleSent'), description: `副本数 → ${scaleValue}` }); + toast({ title: i18n.t('k8s.deploymentView.scaleSent'), description: i18n.t('k8s.toast.scaleTo', { count: scaleValue }) }); onRefresh(); } catch (e) { toast({ title: i18n.t('kubernetes.msg.scaleFailed'), description: e?.message || String(e), variant: 'destructive' }); diff --git a/frontend/src/components/kubernetes/EventsPage.tsx b/frontend/src/components/kubernetes/EventsPage.tsx index 849b2b0..e0fdd4e 100644 --- a/frontend/src/components/kubernetes/EventsPage.tsx +++ b/frontend/src/components/kubernetes/EventsPage.tsx @@ -180,7 +180,7 @@ export default function EventsPage() { } metrics={!loading ? ( } actions={( <> diff --git a/frontend/src/components/kubernetes/NamespaceResourcePage.tsx b/frontend/src/components/kubernetes/NamespaceResourcePage.tsx index a236279..3ba886a 100644 --- a/frontend/src/components/kubernetes/NamespaceResourcePage.tsx +++ b/frontend/src/components/kubernetes/NamespaceResourcePage.tsx @@ -144,7 +144,7 @@ export default function NamespaceResourcePage() { if (!clusterCode || !namespace) return; try { await kubernetesApi.exportNamespaceResources(clusterCode, namespace); - toast({ title: i18n.t('common.exportComplete'), description: `${namespace} 资源 YAML 已开始下载` }); + toast({ title: i18n.t('common.exportComplete'), description: i18n.t('k8s.toast.yamlDownloadStarted', { namespace }) }); } catch (e) { toast({ title: i18n.t('kubernetes.msg.exportFailed'), description: (e as Error)?.message || String(e), variant: 'destructive' }); } diff --git a/frontend/src/components/kubernetes/PodExecTerminal.tsx b/frontend/src/components/kubernetes/PodExecTerminal.tsx index 36850c9..2cf2be3 100644 --- a/frontend/src/components/kubernetes/PodExecTerminal.tsx +++ b/frontend/src/components/kubernetes/PodExecTerminal.tsx @@ -151,7 +151,7 @@ export function PodExecTerminal({ console.error('[PodExec:connect] WS error event fired, readyState=', ws.readyState, 'type=', ev.type); setConnState('error'); setHasOutput(true); - term.write('\r\n\x1b[31m[连接发生错误]\x1b[0m\r\n'); + term.write(`\r\n\x1b[31m${i18n.t('k8s.podLogs.connError')}\x1b[0m\r\n`); }; }, [clusterId, namespace, podName, container, shell]); diff --git a/frontend/src/components/kubernetes/PodLogsViewer.tsx b/frontend/src/components/kubernetes/PodLogsViewer.tsx index ef27f22..0f1cfe4 100644 --- a/frontend/src/components/kubernetes/PodLogsViewer.tsx +++ b/frontend/src/components/kubernetes/PodLogsViewer.tsx @@ -125,7 +125,7 @@ export function PodLogsViewer({ appendLines(arr); } catch (e) { const msg = e instanceof Error ? e.message : String(e); - appendLines([`\x1b[31m[获取日志失败: ${msg}]\x1b[0m`]); + appendLines([i18n.t('k8s.podLogs.fetchFailed', { msg })]); toast({ title: i18n.t('k8s.podLogs.loadFailed'), description: msg, variant: 'destructive' }); } finally { setLoading(false); @@ -183,14 +183,14 @@ export function PodLogsViewer({ } } flush(); - appendLines(['\x1b[33m— 流式结束 —\x1b[0m']); + appendLines([`\x1b[33m${i18n.t('k8s.podLogs.streamEnded')}\x1b[0m`]); } catch (e) { const errName = (e as { name?: string })?.name; if (errName === 'AbortError') { - appendLines(['\x1b[33m— 已停止 —\x1b[0m']); + appendLines([`\x1b[33m${i18n.t('k8s.podLogs.streamStopped')}\x1b[0m`]); } else { const msg = e instanceof Error ? e.message : String(e); - appendLines([`\x1b[31m[流式连接中断: ${msg}]\x1b[0m`]); + appendLines([`\x1b[31m${i18n.t('k8s.podLogs.streamInterrupted', { msg })}\x1b[0m`]); } } finally { setStreaming(false); @@ -261,8 +261,8 @@ export function PodLogsViewer({ )} style={fullscreen ? { maxWidth: '100vw', width: '100vw', height: '100vh' } : { maxWidth: '1100px', width: '94vw' }} > - 日志 — {podName} - Pod 容器日志查看 + {i18n.t('k8s.podLogs.logTitle', { name: podName })} + {i18n.t('k8s.podLogs.logDesc')} } metrics={!loading ? ( - 所有命名空间 + {i18n.t('k8s.podMetrics.allNamespaces')} {namespaces.map(n => {n})} diff --git a/frontend/src/components/kubernetes/ResourceVisualEditor.tsx b/frontend/src/components/kubernetes/ResourceVisualEditor.tsx index e4b971d..9d953ce 100644 --- a/frontend/src/components/kubernetes/ResourceVisualEditor.tsx +++ b/frontend/src/components/kubernetes/ResourceVisualEditor.tsx @@ -92,7 +92,7 @@ export function ResourceVisualEditor({ clusterCode, namespace, resource, name, o toast({ title: i18n.t('monitor.dashboard.custom.toastCreateSuccess'), description: `${resource}/${resName}` }); } else { await kubernetesApi.updateResourceYaml(clusterCode, namespace, resource, name, yamlStr); - toast({ title: i18n.t('kubernetes.msg.saveSuccess'), description: `${resource}/${name} 已更新` }); + toast({ title: i18n.t('kubernetes.msg.saveSuccess'), description: i18n.t('k8s.toast.resourceUpdated', { resource, name }) }); } onSaved?.(); onClose(); @@ -110,7 +110,7 @@ export function ResourceVisualEditor({ clusterCode, namespace, resource, name, o toast({ title: i18n.t('monitor.dashboard.custom.toastCreateSuccess'), description: i18n.t('k8s.rve.saveFailed') }); } else { await kubernetesApi.updateResourceYaml(clusterCode, namespace, resource, name, yamlStr); - toast({ title: i18n.t('kubernetes.msg.saveSuccess'), description: `${resource}/${name} 已更新` }); + toast({ title: i18n.t('kubernetes.msg.saveSuccess'), description: i18n.t('k8s.toast.resourceUpdated', { resource, name }) }); } setYamlEditing(false); setDisplayYaml(yamlStr); diff --git a/frontend/src/components/kubernetes/WorkloadViewPage.tsx b/frontend/src/components/kubernetes/WorkloadViewPage.tsx index 23149a3..7204a19 100644 --- a/frontend/src/components/kubernetes/WorkloadViewPage.tsx +++ b/frontend/src/components/kubernetes/WorkloadViewPage.tsx @@ -96,7 +96,7 @@ export default function WorkloadViewPage() { const handleRestart = async () => { const ok = await confirm({ title: i18n.t('kubernetes.deployment.restart'), - description: `确认重启 ${kind} ${name}?将触发滚动重启。`, + description: i18n.t('k8s.toast.confirmRestart', { kind, name }), confirmText: i18n.t('agent.agentServiceRestart'), }); if (!ok) return; diff --git a/frontend/src/components/kubernetes/YamlViewer.tsx b/frontend/src/components/kubernetes/YamlViewer.tsx index f434c37..eaca55f 100644 --- a/frontend/src/components/kubernetes/YamlViewer.tsx +++ b/frontend/src/components/kubernetes/YamlViewer.tsx @@ -104,7 +104,7 @@ export function YamlViewer({ } else { await kubernetesApi.updateResourceYaml(clusterCode, namespace, resource, name, yamlStr); } - toast({ title: i18n.t('kubernetes.msg.saveSuccess'), description: saveYamlFn ? i18n.t('k8s.rve.saveFailed') : `${resource}/${name} 已更新` }); + toast({ title: i18n.t('kubernetes.msg.saveSuccess'), description: i18n.t('k8s.toast.resourceUpdated', { resource, name }),}); setEditing(false); setRawYaml(yamlStr); setDisplayYaml(yamlStr); diff --git a/frontend/src/components/kubernetes/pod-logs-viewer/ControlBar.tsx b/frontend/src/components/kubernetes/pod-logs-viewer/ControlBar.tsx index 9575723..af4ee99 100644 --- a/frontend/src/components/kubernetes/pod-logs-viewer/ControlBar.tsx +++ b/frontend/src/components/kubernetes/pod-logs-viewer/ControlBar.tsx @@ -127,7 +127,7 @@ export function ControlBar(props: ControlBarProps) { title={i18n.t('k8s.podLogs.previousLogs')} disabled={filteredLineCount === 0} > - 导出 + {i18n.t('k8s.podLogs.exportBtn')} {/* 刷新(一次性) */} @@ -138,7 +138,7 @@ export function ControlBar(props: ControlBarProps) { disabled={loading} title={i18n.t('k8s.podLogs.previousLogs')} > - 刷新 + {i18n.t('k8s.podLogs.refreshBtn')} {/* 实时跟踪 / 暂停 / 继续 */} @@ -150,7 +150,7 @@ export function ControlBar(props: ControlBarProps) { disabled={loading} title={i18n.t('k8s.podLogs.previousLogs')} > - 实时 + {i18n.t('k8s.podLogs.liveBtn')} ) : ( <> @@ -169,7 +169,7 @@ export function ControlBar(props: ControlBarProps) { onClick={onStopStream} title={i18n.t('agent.agentServiceStop')} > - 停止 + {i18n.t('k8s.podLogs.stopBtn')} )} diff --git a/frontend/src/components/kubernetes/pod-logs-viewer/LogContentArea.tsx b/frontend/src/components/kubernetes/pod-logs-viewer/LogContentArea.tsx index 0bc7e14..bcdfd58 100644 --- a/frontend/src/components/kubernetes/pod-logs-viewer/LogContentArea.tsx +++ b/frontend/src/components/kubernetes/pod-logs-viewer/LogContentArea.tsx @@ -51,7 +51,7 @@ export function LogContentArea({ )} {loading && lineCount === 0 && (
- 加载中… + {i18n.t('k8s.podLogs.loadingDots')}
)} {filteredLines.map((line) => ( diff --git a/frontend/src/components/kubernetes/pod-logs-viewer/LogsHeader.tsx b/frontend/src/components/kubernetes/pod-logs-viewer/LogsHeader.tsx index c1f6b08..c346349 100644 --- a/frontend/src/components/kubernetes/pod-logs-viewer/LogsHeader.tsx +++ b/frontend/src/components/kubernetes/pod-logs-viewer/LogsHeader.tsx @@ -37,7 +37,7 @@ export function LogsHeader({ streaming ? (paused ? 'bg-amber-400' : 'bg-emerald-400 animate-pulse') : 'bg-[var(--dg-bg-active)]' )} /> - 日志 — {podName} + {i18n.t('k8s.podLogs.logTitle', { name: podName })} {loading && } {streaming ? (paused ? i18n.t('kubernetes.deployment.paused') : i18n.t('kubernetes.podLogsViewer.liveTail')) : i18n.t('agent.agentServiceStopped')} · {matchedCount}{keyword ? `/${lineCount}` : ''} 行 diff --git a/frontend/src/components/kubernetes/resource-visual-editors/ConfigMapEditor.tsx b/frontend/src/components/kubernetes/resource-visual-editors/ConfigMapEditor.tsx index deb7dfd..c09671f 100644 --- a/frontend/src/components/kubernetes/resource-visual-editors/ConfigMapEditor.tsx +++ b/frontend/src/components/kubernetes/resource-visual-editors/ConfigMapEditor.tsx @@ -87,7 +87,7 @@ export function ConfigMapEditor({ manifest, onSave, saving, isCreate }: ConfigMa
diff --git a/frontend/src/components/kubernetes/resource-visual-editors/IngressEditor.tsx b/frontend/src/components/kubernetes/resource-visual-editors/IngressEditor.tsx index 47e55ad..4726347 100644 --- a/frontend/src/components/kubernetes/resource-visual-editors/IngressEditor.tsx +++ b/frontend/src/components/kubernetes/resource-visual-editors/IngressEditor.tsx @@ -108,7 +108,7 @@ export function IngressEditor({ manifest, clusterCode, onSave, saving, isCreate
{/* IngressClass 下拉 + 手工输入支持 // Logan.Li */}
@@ -135,15 +135,15 @@ export function IngressEditor({ manifest, clusterCode, onSave, saving, isCreate )} {/* 当前自定义值作为选项,确保选中态 */} {ingressClass && !ingressClasses.some((ic) => ic.name === ingressClass) && ( - {ingressClass} (手动输入) + {ingressClass} {i18n.t('k8s.rve.manualEntry')} )} - —— 继续手动输入 —— + {i18n.t('k8s.rve.continueManual')} {/* 总是显示输入框支持手工输入或修改 */}
- 手工输入: + {i18n.t('k8s.rve.manualInput')} setIngressClass(e.target.value)} @@ -154,13 +154,13 @@ export function IngressEditor({ manifest, clusterCode, onSave, saving, isCreate {/* 帮助提示 */}
- 您的集群中可能有多个 IngressController,您可在此指定当前 Ingress 对应哪个 IngressController。
- 从 IngressClass 列表选择,或手工输入名称。 + {i18n.t('k8s.rve.ingressHelp1')}
+ {i18n.t('k8s.rve.ingressHelp2')}
{/* 常见示例 */} {(ingressClasses.length === 0 || ingressClass) && (
- 常见:alb → ingress.k8s.alibabacloud/alb    nginx → k8s.io/ingress-nginx + {i18n.t('k8s.rve.commonExamples')}alb → ingress.k8s.alibabacloud/alb    nginx → k8s.io/ingress-nginx
)}
@@ -207,7 +207,7 @@ export function IngressEditor({ manifest, clusterCode, onSave, saving, isCreate
diff --git a/frontend/src/components/kubernetes/resource-visual-editors/PvcEditor.tsx b/frontend/src/components/kubernetes/resource-visual-editors/PvcEditor.tsx index 69870aa..1a9d093 100644 --- a/frontend/src/components/kubernetes/resource-visual-editors/PvcEditor.tsx +++ b/frontend/src/components/kubernetes/resource-visual-editors/PvcEditor.tsx @@ -60,9 +60,9 @@ export function PvcEditor({ manifest, onSave, saving, isCreate }: PvcEditorProps - ReadWriteOnce (单节点读写) - ReadOnlyMany (多节点只读) - ReadWriteMany (多节点读写) + {i18n.t('k8s.rve.accessModeRWO')} + {i18n.t('k8s.rve.accessModeROX')} + {i18n.t('k8s.rve.accessModeRWX')}
@@ -82,7 +82,7 @@ export function PvcEditor({ manifest, onSave, saving, isCreate }: PvcEditorProps
diff --git a/frontend/src/components/kubernetes/resource-visual-editors/SecretEditor.tsx b/frontend/src/components/kubernetes/resource-visual-editors/SecretEditor.tsx index 4ac541d..ee70fc0 100644 --- a/frontend/src/components/kubernetes/resource-visual-editors/SecretEditor.tsx +++ b/frontend/src/components/kubernetes/resource-visual-editors/SecretEditor.tsx @@ -106,7 +106,7 @@ export function SecretEditor({ manifest, onSave, saving, isCreate }: SecretEdito
diff --git a/frontend/src/components/kubernetes/resource-visual-editors/ServiceEditor.tsx b/frontend/src/components/kubernetes/resource-visual-editors/ServiceEditor.tsx index 95d4aed..e1630d8 100644 --- a/frontend/src/components/kubernetes/resource-visual-editors/ServiceEditor.tsx +++ b/frontend/src/components/kubernetes/resource-visual-editors/ServiceEditor.tsx @@ -147,7 +147,7 @@ export function ServiceEditor({ manifest, onSave, saving, isCreate }: ServiceEdi
diff --git a/frontend/src/locales/en-US.json b/frontend/src/locales/en-US.json index 76c35ce..c1816d4 100644 --- a/frontend/src/locales/en-US.json +++ b/frontend/src/locales/en-US.json @@ -2775,11 +2775,11 @@ "podLogs": { "noLogs": "No logs", "loadFailed": "Failed to load logs", - "streamInterrupted": "Stream connection interrupted", + "streamInterrupted": "[Stream connection interrupted: {{msg}}]", "streaming": "Streaming...", "liveStream": "Live Stream", "noLogContent": "No log content", - "logTitle": "Logs", + "logTitle": "Logs — {{name}}", "logDesc": "Pod container log viewer", "export": "Export", "refreshLogs": "Refresh", @@ -2791,13 +2791,20 @@ "bufferFull": "Buffer full", "matched": "Matched", "loading": "Loading…", - "fetchFailed": "Failed to fetch logs", - "streamEnded": "Stream ended", - "streamStopped": "Stream stopped", + "fetchFailed": "[Failed to fetch logs: {{msg}}]", + "streamEnded": "— Stream ended —", + "streamStopped": "— Stopped —", "download": "Download Logs", "searchPlaceholder": "Search logs...", "previousLogs": "View previous (crashed) container logs", - "keywordFilter": "Keyword filter…" + "keywordFilter": "Keyword filter…", + "liveTail": "Live Tail", + "exportBtn": "Export", + "refreshBtn": "Refresh", + "liveBtn": "Live", + "stopBtn": "Stop", + "loadingDots": "Loading…", + "connError": "[Connection error]" }, "editMode": { "basic": "Basic Info", @@ -3182,7 +3189,11 @@ "memory": "总内存", "namespace": "所有命名空间", "cpuUsage": "CPU Usage", - "memUsage": "Memory Usage" + "memUsage": "Memory Usage", + "realtimeMetrics": "Container CPU/Memory real-time metrics", + "allNamespaces": "All Namespaces", + "colNamespace": "Namespace", + "colContainer": "Container" }, "resourceEditor": { "create": "Create", @@ -3366,7 +3377,22 @@ "keyPlaceholder": "Key name (e.g. LOG_LEVEL)", "parseYamlFailed": "Unable to parse YAML", "checkSyntax": "Please check syntax and continue in YAML mode", - "fallbackToYaml": "Unable to parse resource structure, switched to YAML edit mode" + "fallbackToYaml": "Unable to parse resource structure, switched to YAML edit mode", + "saveConfigMap": "Save ConfigMap", + "saveSecret": "Save Secret", + "saveService": "Save Service", + "saveIngress": "Save Ingress", + "savePvc": "Save PVC", + "optional": "(Optional)", + "manualInput": "Manual input:", + "manualEntry": "(Manual)", + "continueManual": "—— Continue manual input ——", + "ingressHelp1": "Your cluster may have multiple IngressControllers. You can specify which IngressController this Ingress corresponds to.", + "ingressHelp2": "Select from the IngressClass list, or enter a name manually.", + "commonExamples": "Common:", + "accessModeRWO": "ReadWriteOnce (Single node read-write)", + "accessModeROX": "ReadOnlyMany (Multi-node read-only)", + "accessModeRWX": "ReadWriteMany (Multi-node read-write)" }, "workloadView": { "restartSent": "Restart command sent", @@ -3467,6 +3493,26 @@ }, "podFile": { "loadFailed": "Failed to load file list" + }, + "toast": { + "updatedSubmitted": "{{name}} update submitted", + "yamlDownloadStarted": "{{namespace}} resource YAML download started", + "rollbackToRevision": "Rolling back to #{{revision}}", + "scaleTo": "Replicas → {{count}}", + "scaleSet": "Replicas set to {{count}}", + "imageUpdating": "Rolling update to {{image}}", + "resourceUpdated": "{{resource}}/{{name}} updated", + "resourceDeleted": "{{kind}} {{name}} deleted", + "containerNameEmpty": "Container name cannot be empty", + "containerImageEmpty": "Container {{name}} image cannot be empty", + "clusterNotFound": "Cluster {{code}} not found", + "confirmRollback": "Confirm rollback to revision #{{revision}}?", + "confirmDeleteResource": "Confirm delete {{kind}} \"{{name}}\"? This cannot be undone.", + "confirmDeleteHpa": "Delete HPA \"{{name}}\"?", + "confirmRestart": "Confirm restart {{kind}} {{name}}? This will trigger a rolling restart.", + "confirmDeleteProbe": "Delete probe \"{{name}}\"?", + "confirmDeleteMonitor": "Confirm delete \"{{name}}\"?", + "confirmDeleteTask": "Delete \"{{name}}\"?" } }, "monitor": { diff --git a/frontend/src/locales/zh-CN.json b/frontend/src/locales/zh-CN.json index d2f4af8..ab0f181 100644 --- a/frontend/src/locales/zh-CN.json +++ b/frontend/src/locales/zh-CN.json @@ -2775,11 +2775,11 @@ "podLogs": { "noLogs": "无日志", "loadFailed": "获取日志失败", - "streamInterrupted": "流式连接中断", + "streamInterrupted": "[流式连接中断: {{msg}}]", "streaming": "流中...", "liveStream": "实时流", "noLogContent": "无日志内容", - "logTitle": "日志", + "logTitle": "日志 — {{name}}", "logDesc": "Pod 容器日志查看", "export": "导出", "refreshLogs": "刷新", @@ -2791,13 +2791,20 @@ "bufferFull": "缓冲已满", "matched": "匹配", "loading": "加载中…", - "fetchFailed": "获取日志失败", - "streamEnded": "流式传输结束", - "streamStopped": "流式传输已停止", + "fetchFailed": "[获取日志失败: {{msg}}]", + "streamEnded": "— 流式结束 —", + "streamStopped": "— 已停止 —", "download": "下载日志", "searchPlaceholder": "搜索日志...", "previousLogs": "查看上一次(崩溃)容器的日志", - "keywordFilter": "关键字过滤…" + "keywordFilter": "关键字过滤…", + "liveTail": "实时跟踪", + "exportBtn": "导出", + "refreshBtn": "刷新", + "liveBtn": "实时", + "stopBtn": "停止", + "loadingDots": "加载中…", + "connError": "[连接发生错误]" }, "editMode": { "basic": "基本信息", @@ -3182,7 +3189,11 @@ "memory": "总内存", "namespace": "所有命名空间", "cpuUsage": "CPU 使用", - "memUsage": "内存使用" + "memUsage": "内存使用", + "realtimeMetrics": "容器 CPU/内存实时指标", + "allNamespaces": "所有命名空间", + "colNamespace": "命名空间", + "colContainer": "容器" }, "resourceEditor": { "create": "新建", @@ -3366,7 +3377,22 @@ "keyPlaceholder": "键名 (如 LOG_LEVEL)", "parseYamlFailed": "无法解析 YAML", "checkSyntax": "请检查语法后继续用 YAML 模式", - "fallbackToYaml": "无法解析资源结构,已切换到 YAML 编辑模式" + "fallbackToYaml": "无法解析资源结构,已切换到 YAML 编辑模式", + "saveConfigMap": "保存配置", + "saveSecret": "保存密钥", + "saveService": "保存 Service", + "saveIngress": "保存 Ingress", + "savePvc": "保存 PVC", + "optional": "(可选)", + "manualInput": "手工输入:", + "manualEntry": "(手动输入)", + "continueManual": "—— 继续手动输入 ——", + "ingressHelp1": "您的集群中可能有多个 IngressController,您可在此指定当前 Ingress 对应哪个 IngressController。", + "ingressHelp2": "从 IngressClass 列表选择,或手工输入名称。", + "commonExamples": "常见:", + "accessModeRWO": "ReadWriteOnce (单节点读写)", + "accessModeROX": "ReadOnlyMany (多节点只读)", + "accessModeRWX": "ReadWriteMany (多节点读写)" }, "workloadView": { "restartSent": "重启指令已下发", @@ -3467,6 +3493,26 @@ }, "podFile": { "loadFailed": "加载文件列表失败" + }, + "toast": { + "updatedSubmitted": "{{name}} 更新已提交", + "yamlDownloadStarted": "{{namespace}} 资源 YAML 已开始下载", + "rollbackToRevision": "正在回滚到 #{{revision}}", + "scaleTo": "副本数 → {{count}}", + "scaleSet": "副本数已设置为 {{count}}", + "imageUpdating": "正在滚动更新为 {{image}}", + "resourceUpdated": "{{resource}}/{{name}} 已更新", + "resourceDeleted": "{{kind}} {{name}} 已删除", + "containerNameEmpty": "容器名称不能为空", + "containerImageEmpty": "容器 {{name}} 的镜像不能为空", + "clusterNotFound": "找不到集群 {{code}}", + "confirmRollback": "确认回滚到修订版本 #{{revision}}?", + "confirmDeleteResource": "确认删除 {{kind}} 「{{name}}」?此操作不可撤销。", + "confirmDeleteHpa": "确定删除 HPA \"{{name}}\"?", + "confirmRestart": "确认重启 {{kind}} {{name}}?将触发滚动重启。", + "confirmDeleteProbe": "确定删除探针「{{name}}」?", + "confirmDeleteMonitor": "确认删除 \"{{name}}\"?", + "confirmDeleteTask": "确定删除「{{name}}」吗?" } }, "monitor": { diff --git a/frontend/src/pages/monitor/MiddlewareMonitor.tsx b/frontend/src/pages/monitor/MiddlewareMonitor.tsx index 5219f98..102d3b1 100644 --- a/frontend/src/pages/monitor/MiddlewareMonitor.tsx +++ b/frontend/src/pages/monitor/MiddlewareMonitor.tsx @@ -13,6 +13,7 @@ import { MiddlewareDialog } from './middleware-monitor/MiddlewareDialog'; import type { MwTarget } from './middleware-monitor/helpers'; import { REDIS_CHARTS, MYSQL_CHARTS, evalMwHealth, worstHealthLevel } from './middleware-monitor/helpers'; import { useTranslation } from 'react-i18next'; +import i18n from '@/i18n'; type TabKey = 'redis' | 'mysql'; const REFRESH_INTERVAL_MS = 30_000; @@ -87,7 +88,7 @@ export function MiddlewareMonitor() { const warnCount = targets.filter((t) => worstHealthLevel(evalMwHealth(t)) !== 'ok').length; const handleDelete = async (target: MwTarget) => { - const ok = await confirm({ title: t('monitor.lbl.deleteMonitor'), description: `确认删除 "${target.name}"?`, confirmText: t('kubernetes.storage.delete2') }); + const ok = await confirm({ title: t('monitor.lbl.deleteMonitor'), description: i18n.t('k8s.toast.confirmDeleteMonitor', { name: target.name }), confirmText: t('kubernetes.storage.delete2') }); if (!ok) return; try { await middlewareMonitorApi.delete(target.id); diff --git a/frontend/src/pages/monitor/SiteMonitor.tsx b/frontend/src/pages/monitor/SiteMonitor.tsx index 04192d6..a7760f2 100644 --- a/frontend/src/pages/monitor/SiteMonitor.tsx +++ b/frontend/src/pages/monitor/SiteMonitor.tsx @@ -158,7 +158,7 @@ export function SiteMonitor() { }; const handleDelete = async (task: SiteTask) => { - const ok = await _confirm({ title: t('monitor.deleteProbe'), description: `确定删除「${task.name}」吗?`, variant: 'destructive' }); + const ok = await _confirm({ title: t('monitor.deleteProbe'), description: i18n.t('k8s.toast.confirmDeleteTask', { name: task.name }), variant: 'destructive' }); if (!ok) return; try { await siteApi.deleteTask(task.id); diff --git a/frontend/src/pages/monitor/host-monitor/HealthProbeSection.tsx b/frontend/src/pages/monitor/host-monitor/HealthProbeSection.tsx index 0156688..f209571 100644 --- a/frontend/src/pages/monitor/host-monitor/HealthProbeSection.tsx +++ b/frontend/src/pages/monitor/host-monitor/HealthProbeSection.tsx @@ -19,6 +19,7 @@ import { DEFAULT_PROBE_FORM, type ProbeForm } from './types'; import { useConfirm } from '@/components/ui/confirm-dialog'; import { toast } from 'sonner'; import { useTranslation } from 'react-i18next'; +import i18n from '@/i18n'; interface HealthProbeSectionProps { selectedHost: string; @@ -77,7 +78,7 @@ export function HealthProbeSection({ selectedHost }: HealthProbeSectionProps) { const handleDeleteProbe = async (p: Record) => { const ok = await confirm({ title: t('monitor.lbl.deleteProbe'), - description: `确定删除探针「${p.name}」?`, + description: i18n.t('k8s.toast.confirmDeleteProbe', { name: p.name }), confirmText: t('kubernetes.storage.delete2'), variant: 'destructive', });