This is an automated email from the ASF dual-hosted git repository.

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git

commit 1e4fa1a8cf5ee297a5bd7a4a2804e1ee150f82e2
Author: zhaohai <[email protected]>
AuthorDate: Fri Jul 24 13:42:12 2026 +0800

    feat: enhance i18n support with labelKey-based translations (#495)
    
    Migrate hardcoded Chinese UI text to i18n labelKey pattern, add ~280 
translation entries (zh+en), fix duplicate key compilation errors.
---
 web/src/components/StatusBadge.tsx                |   4 +-
 web/src/components/__tests__/StatusBadge.test.tsx |  77 ++++++
 web/src/constants/__tests__/theme.test.ts         |  97 ++++++++
 web/src/constants/theme.ts                        |  50 ++--
 web/src/i18n/__tests__/LangContext.test.tsx       |   2 +-
 web/src/i18n/translations.ts                      | 287 +++++++++++++++++++++-
 web/src/pages/cluster/index.tsx                   |  57 ++---
 web/src/pages/home/dashboard.tsx                  |   2 +-
 web/src/pages/instance/consumer.tsx               |  13 +-
 web/src/pages/instance/topic.tsx                  |  14 +-
 10 files changed, 529 insertions(+), 74 deletions(-)

diff --git a/web/src/components/StatusBadge.tsx 
b/web/src/components/StatusBadge.tsx
index b2c02ec9..1a38781f 100644
--- a/web/src/components/StatusBadge.tsx
+++ b/web/src/components/StatusBadge.tsx
@@ -17,6 +17,7 @@
 
 import { Badge, Space, Typography } from 'antd';
 import { STATUS_MAP } from '../constants/theme';
+import { useLang } from '../i18n/LangContext';
 
 const { Text } = Typography;
 
@@ -27,8 +28,9 @@ interface StatusBadgeProps {
 }
 
 const StatusBadge = ({ status, text, showDot = true }: StatusBadgeProps) => {
+  const { t } = useLang();
   const config = STATUS_MAP[status] || STATUS_MAP.offline;
-  const label = text || config.label;
+  const label = text || t(config.labelKey);
   return (
     <Space size={4} role="status" aria-label={`状态:${label}`}>
       {showDot && <Badge color={config.dot} aria-hidden="true" />}
diff --git a/web/src/components/__tests__/StatusBadge.test.tsx 
b/web/src/components/__tests__/StatusBadge.test.tsx
new file mode 100644
index 00000000..07f08c7e
--- /dev/null
+++ b/web/src/components/__tests__/StatusBadge.test.tsx
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { describe, it, expect } from 'vitest';
+import { render, screen } from '@testing-library/react';
+import StatusBadge from '../StatusBadge';
+import { LangProvider } from '../../i18n/LangContext';
+
+const renderWithLang = (ui: React.ReactElement) => 
render(<LangProvider>{ui}</LangProvider>);
+
+describe('StatusBadge', () => {
+  it('renders the translated label for a known status in Chinese', () => {
+    renderWithLang(<StatusBadge status="healthy" />);
+    // theme.healthy in zh is '运行中'
+    expect(screen.getByText('运行中')).toBeInTheDocument();
+  });
+
+  it('renders the translated label for warning status', () => {
+    renderWithLang(<StatusBadge status="warning" />);
+    // theme.warning in zh is '告警'
+    expect(screen.getByText('告警')).toBeInTheDocument();
+  });
+
+  it('renders the translated label for error status', () => {
+    renderWithLang(<StatusBadge status="error" />);
+    // theme.error in zh is '异常'
+    expect(screen.getByText('异常')).toBeInTheDocument();
+  });
+
+  it('renders the translated label for offline status', () => {
+    renderWithLang(<StatusBadge status="offline" />);
+    expect(screen.getByText('离线')).toBeInTheDocument();
+  });
+
+  it('renders the translated label for connecting status', () => {
+    renderWithLang(<StatusBadge status="connecting" />);
+    expect(screen.getByText('连接中')).toBeInTheDocument();
+  });
+
+  it('falls back to offline config for unknown status', () => {
+    renderWithLang(<StatusBadge status="unknown_status" />);
+    // Should render offline label
+    expect(screen.getByText('离线')).toBeInTheDocument();
+  });
+
+  it('uses custom text when provided', () => {
+    renderWithLang(<StatusBadge status="healthy" text="自定义" />);
+    expect(screen.getByText('自定义')).toBeInTheDocument();
+  });
+
+  it('hides the dot when showDot is false', () => {
+    const { container } = renderWithLang(<StatusBadge status="healthy" 
showDot={false} />);
+    // No Badge component should be rendered
+    const badges = container.querySelectorAll('.ant-badge');
+    expect(badges.length).toBe(0);
+  });
+
+  it('has role=status for accessibility', () => {
+    const { container } = renderWithLang(<StatusBadge status="healthy" />);
+    const statusEl = container.querySelector('[role="status"]');
+    expect(statusEl).toBeInTheDocument();
+  });
+});
diff --git a/web/src/constants/__tests__/theme.test.ts 
b/web/src/constants/__tests__/theme.test.ts
new file mode 100644
index 00000000..c3945f0d
--- /dev/null
+++ b/web/src/constants/__tests__/theme.test.ts
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { describe, it, expect } from 'vitest';
+import { STATUS_MAP, CLUSTER_TYPE_MAP, TOPIC_TYPE_MAP, PROTOCOL_MAP, 
THEME_COLORS } from '../theme';
+
+describe('theme constants', () => {
+  describe('STATUS_MAP', () => {
+    it('has labelKey for all status entries', () => {
+      for (const [key, val] of Object.entries(STATUS_MAP)) {
+        expect(val.labelKey, `STATUS_MAP[${key}].labelKey should 
exist`).toBeDefined();
+        expect(val.labelKey, `STATUS_MAP[${key}].labelKey should start with 
theme.`).toMatch(
+          /^theme\./,
+        );
+      }
+    });
+
+    it('has color and dot for all status entries', () => {
+      for (const [key, val] of Object.entries(STATUS_MAP)) {
+        expect(val.color, `STATUS_MAP[${key}].color should 
exist`).toBeDefined();
+        expect(val.dot, `STATUS_MAP[${key}].dot should exist`).toBeDefined();
+      }
+    });
+
+    it('contains required status keys', () => {
+      expect(STATUS_MAP.healthy).toBeDefined();
+      expect(STATUS_MAP.warning).toBeDefined();
+      expect(STATUS_MAP.error).toBeDefined();
+      expect(STATUS_MAP.offline).toBeDefined();
+      expect(STATUS_MAP.connecting).toBeDefined();
+    });
+  });
+
+  describe('CLUSTER_TYPE_MAP', () => {
+    it('has labelKey for all cluster type entries', () => {
+      for (const [key, val] of Object.entries(CLUSTER_TYPE_MAP)) {
+        expect(val.labelKey, `CLUSTER_TYPE_MAP[${key}].labelKey should 
exist`).toBeDefined();
+        expect(val.color, `CLUSTER_TYPE_MAP[${key}].color should 
exist`).toBeDefined();
+      }
+    });
+
+    it('contains V4_DIRECT, V5_PROXY_LOCAL, V5_PROXY_CLUSTER', () => {
+      expect(CLUSTER_TYPE_MAP.V4_DIRECT).toBeDefined();
+      expect(CLUSTER_TYPE_MAP.V5_PROXY_LOCAL).toBeDefined();
+      expect(CLUSTER_TYPE_MAP.V5_PROXY_CLUSTER).toBeDefined();
+    });
+  });
+
+  describe('TOPIC_TYPE_MAP', () => {
+    it('has labelKey for all topic type entries', () => {
+      for (const [key, val] of Object.entries(TOPIC_TYPE_MAP)) {
+        expect(val.labelKey, `TOPIC_TYPE_MAP[${key}].labelKey should 
exist`).toBeDefined();
+        expect(val.color, `TOPIC_TYPE_MAP[${key}].color should 
exist`).toBeDefined();
+      }
+    });
+
+    it('contains NORMAL, FIFO, DELAY, TRANSACTION, LITE', () => {
+      expect(TOPIC_TYPE_MAP.NORMAL).toBeDefined();
+      expect(TOPIC_TYPE_MAP.FIFO).toBeDefined();
+      expect(TOPIC_TYPE_MAP.DELAY).toBeDefined();
+      expect(TOPIC_TYPE_MAP.TRANSACTION).toBeDefined();
+      expect(TOPIC_TYPE_MAP.LITE).toBeDefined();
+    });
+  });
+
+  describe('PROTOCOL_MAP', () => {
+    it('has labelKey for all protocol entries', () => {
+      for (const [key, val] of Object.entries(PROTOCOL_MAP)) {
+        expect(val.labelKey, `PROTOCOL_MAP[${key}].labelKey should 
exist`).toBeDefined();
+        expect(val.color, `PROTOCOL_MAP[${key}].color should 
exist`).toBeDefined();
+      }
+    });
+  });
+
+  describe('THEME_COLORS', () => {
+    it('contains primary colors', () => {
+      expect(THEME_COLORS.primary).toBeDefined();
+      expect(THEME_COLORS.success).toBeDefined();
+      expect(THEME_COLORS.warning).toBeDefined();
+      expect(THEME_COLORS.error).toBeDefined();
+    });
+  });
+});
diff --git a/web/src/constants/theme.ts b/web/src/constants/theme.ts
index 2cedb9bc..58ff5406 100644
--- a/web/src/constants/theme.ts
+++ b/web/src/constants/theme.ts
@@ -29,29 +29,41 @@ export const THEME_COLORS = {
   clusterV5Cluster: '#722ed1',
 } as const;
 
-export const CLUSTER_TYPE_MAP: Record<string, { label: string; color: 
TagProps['color'] }> = {
-  V4_DIRECT: { label: 'V4 直连', color: 'orange' },
-  V5_PROXY_LOCAL: { label: 'V5 Proxy 单节点', color: 'blue' },
-  V5_PROXY_CLUSTER: { label: 'V5 Proxy 集群', color: 'purple' },
+/**
+ * Cluster type map — labels are i18n keys, resolved at render time via t().
+ */
+export const CLUSTER_TYPE_MAP: Record<string, { labelKey: string; color: 
TagProps['color'] }> = {
+  V4_DIRECT: { labelKey: 'theme.clusterV4', color: 'orange' },
+  V5_PROXY_LOCAL: { labelKey: 'theme.clusterV5Local', color: 'blue' },
+  V5_PROXY_CLUSTER: { labelKey: 'theme.clusterV5Cluster', color: 'purple' },
 };
 
-export const STATUS_MAP: Record<string, { label: string; color: string; dot: 
string }> = {
-  healthy: { label: '运行中', color: '#52c41a', dot: '#52c41a' },
-  warning: { label: '告警', color: '#faad14', dot: '#faad14' },
-  error: { label: '异常', color: '#ff4d4f', dot: '#ff4d4f' },
-  offline: { label: '离线', color: '#d9d9d9', dot: '#d9d9d9' },
-  connecting: { label: '连接中', color: '#1677ff', dot: '#1677ff' },
+/**
+ * Status map — labels are i18n keys, resolved at render time via t().
+ */
+export const STATUS_MAP: Record<string, { labelKey: string; color: string; 
dot: string }> = {
+  healthy: { labelKey: 'theme.healthy', color: '#52c41a', dot: '#52c41a' },
+  warning: { labelKey: 'theme.warning', color: '#faad14', dot: '#faad14' },
+  error: { labelKey: 'theme.error', color: '#ff4d4f', dot: '#ff4d4f' },
+  offline: { labelKey: 'theme.offline', color: '#d9d9d9', dot: '#d9d9d9' },
+  connecting: { labelKey: 'theme.connecting', color: '#1677ff', dot: '#1677ff' 
},
 };
 
-export const TOPIC_TYPE_MAP: Record<string, { label: string; color: 
TagProps['color'] }> = {
-  NORMAL: { label: '普通', color: 'default' },
-  FIFO: { label: '顺序', color: 'blue' },
-  DELAY: { label: '延迟', color: 'orange' },
-  TRANSACTION: { label: '事务', color: 'purple' },
-  LITE: { label: 'LiteTopic', color: 'magenta' },
+/**
+ * Topic type map — labels are i18n keys, resolved at render time via t().
+ */
+export const TOPIC_TYPE_MAP: Record<string, { labelKey: string; color: 
TagProps['color'] }> = {
+  NORMAL: { labelKey: 'theme.topicNormal', color: 'default' },
+  FIFO: { labelKey: 'theme.topicFifo', color: 'blue' },
+  DELAY: { labelKey: 'theme.topicDelay', color: 'orange' },
+  TRANSACTION: { labelKey: 'theme.topicTransaction', color: 'purple' },
+  LITE: { labelKey: 'theme.topicLite', color: 'magenta' },
 };
 
-export const PROTOCOL_MAP: Record<string, { label: string; color: 
TagProps['color'] }> = {
-  REMOTING: { label: 'Remoting', color: 'geekblue' },
-  GRPC: { label: 'gRPC', color: 'green' },
+/**
+ * Protocol map — labels are i18n keys, resolved at render time via t().
+ */
+export const PROTOCOL_MAP: Record<string, { labelKey: string; color: 
TagProps['color'] }> = {
+  REMOTING: { labelKey: 'theme.protocolRemoting', color: 'geekblue' },
+  GRPC: { labelKey: 'theme.protocolGrpc', color: 'green' },
 };
diff --git a/web/src/i18n/__tests__/LangContext.test.tsx 
b/web/src/i18n/__tests__/LangContext.test.tsx
index 1e3ac571..5a81cab8 100644
--- a/web/src/i18n/__tests__/LangContext.test.tsx
+++ b/web/src/i18n/__tests__/LangContext.test.tsx
@@ -119,4 +119,4 @@ describe('LangContext', () => {
     );
     expect(screen.getByTestId('alias-lang')).toHaveTextContent('zh');
   });
-});
\ No newline at end of file
+});
diff --git a/web/src/i18n/translations.ts b/web/src/i18n/translations.ts
index dd619874..3b37ff10 100644
--- a/web/src/i18n/translations.ts
+++ b/web/src/i18n/translations.ts
@@ -87,6 +87,14 @@ const translations: Record<string, Record<Lang, string>> = {
   'dashboard.group': { zh: 'Group', en: 'Group' },
   'dashboard.trend': { zh: '趋势', en: 'Trend' },
   'dashboard.millionMessages': { zh: '约 {n}M 条消息', en: '~{n}M messages' },
+  'dashboard.tpsTrend': { zh: 'TPS 趋势', en: 'TPS Trend' },
+  'dashboard.tpsInLabel': { zh: 'TPS In', en: 'TPS In' },
+  'dashboard.tpsOutLabel': { zh: 'TPS Out', en: 'TPS Out' },
+  'dashboard.brokers': { zh: '{n} Broker', en: '{n} Brokers' },
+  'dashboard.proxies': { zh: '{n} Proxy', en: '{n} Proxies' },
+  'dashboard.consumerGroups': { zh: '{n} 消费组', en: '{n} Groups' },
+  'dashboard.healthy': { zh: '健康', en: 'Healthy' },
+  'dashboard.last12h': { zh: '近 12 小时', en: 'Last 12 hours' },
 
   // ─── Cluster Page ───
   'cluster.title': { zh: 'RocketMQ 集群', en: 'RocketMQ Cluster' },
@@ -332,10 +340,276 @@ const translations: Record<string, Record<Lang, string>> 
= {
   'acl.required': { zh: '请选择{field}', en: 'Please select {field}' },
   'acl.inputRequired': { zh: '请输入{field}', en: 'Please enter {field}' },
 
+  // ─── Topic Page (unique keys, duplicates merged into Topic section below) 
───
+  'topic.name': { zh: 'Topic 名称', en: 'Topic Name' },
+  'topic.action': { zh: '操作', en: 'Actions' },
+  'topic.detail': { zh: '详情', en: 'Detail' },
+  'topic.route': { zh: '路由', en: 'Route' },
+  'topic.send': { zh: '发送', en: 'Send' },
+  'topic.delete': { zh: '删除', en: 'Delete' },
+  'topic.deleteContent': {
+    zh: '确定要删除 Topic「{name}」吗?此操作不可撤销。',
+    en: 'Are you sure to delete Topic "{name}"? This cannot be undone.',
+  },
+  'topic.allTypes': { zh: '全部类型', en: 'All Types' },
+  'topic.normal': { zh: '普通', en: 'Normal' },
+  'topic.fifo': { zh: '顺序', en: 'FIFO' },
+  'topic.delay': { zh: '延迟', en: 'Delay' },
+  'topic.transaction': { zh: '事务', en: 'Transaction' },
+  'topic.lite': { zh: 'LiteTopic', en: 'LiteTopic' },
+  'topic.batchDelete': { zh: '批量删除', en: 'Batch Delete' },
+  'topic.bodyOrder': { zh: '订单事件', en: 'Order Event' },
+  'topic.bodyUserEvent': { zh: '用户行为', en: 'User Event' },
+  'topic.bodyPayment': { zh: '支付回调', en: 'Payment Callback' },
+  'topic.bodyInventory': { zh: '库存变更', en: 'Inventory Change' },
+  'topic.bodyNotification': { zh: '通知消息', en: 'Notification' },
+  'topic.bodyMetrics': { zh: '监控指标', en: 'Metrics' },
+  'topic.randomBody': { zh: '随机消息体', en: 'Random Body' },
+  'topic.sendTestMessage': { zh: '发送测试消息', en: 'Send Test Message' },
+  'topic.tag': { zh: 'Tag', en: 'Tag' },
+  'topic.key': { zh: 'Key', en: 'Key' },
+  'topic.messageBody': { zh: '消息体', en: 'Message Body' },
+  'topic.properties': { zh: '属性', en: 'Properties' },
+  'topic.topicConfig': { zh: 'Topic 配置', en: 'Topic Config' },
+  'topic.queueCount': { zh: '队列数', en: 'Queue Count' },
+
+  // ─── Consumer Page ───
+  'consumer.name': { zh: 'Group 名称', en: 'Group Name' },
+  'consumer.subType': { zh: '订阅组类型', en: 'Sub Type' },
+  'consumer.subMode': { zh: '订阅模式', en: 'Sub Mode' },
+  'consumer.onlineClients': { zh: '在线客户端', en: 'Online Clients' },
+  'consumer.totalLag': { zh: '总堆积量', en: 'Total Lag' },
+  'consumer.delay': { zh: '消费延迟', en: 'Consume Delay' },
+  'consumer.createdAt': { zh: '创建时间', en: 'Created' },
+  'consumer.updatedAt': { zh: '修改时间', en: 'Updated' },
+  'consumer.action': { zh: '操作', en: 'Actions' },
+  'consumer.detail': { zh: '详情', en: 'Detail' },
+  'consumer.resetOffset': { zh: '重置位点', en: 'Reset Offset' },
+  'consumer.confirmDelete': {
+    zh: '确认删除消费组 "{name}"?',
+    en: 'Delete consumer group "{name}"?',
+  },
+  'consumer.deleteWarning': {
+    zh: '删除后该消费组的所有配置和消费进度将被清除,此操作不可恢复。',
+    en: 'All configurations and progress will be removed. This cannot be 
undone.',
+  },
+  'consumer.deleted': { zh: '消费组 {name} 已删除', en: 'Consumer group {name} 
deleted' },
+  'consumer.searchPlaceholder': { zh: '搜索 Group 名称或 Topic', en: 'Search group 
name or topic' },
+  'consumer.allModes': { zh: '全部模式', en: 'All Modes' },
+  'consumer.push': { zh: 'Push', en: 'Push' },
+  'consumer.pull': { zh: 'Pull', en: 'Pull' },
+  'consumer.sortNameAsc': { zh: '名称升序', en: 'Name A-Z' },
+  'consumer.sortLagDesc': { zh: '堆积量降序', en: 'Lag Descending' },
+  'consumer.createGroup': { zh: '新建消费组', en: 'Create Group' },
+  'consumer.subTopic': { zh: 'Topic 主题', en: 'Topic' },
+  'consumer.consistency': { zh: '订阅一致性', en: 'Consistency' },
+  'consumer.consistent': { zh: '一致', en: 'Consistent' },
+  'consumer.inconsistent': { zh: '不一致', en: 'Inconsistent' },
+  'consumer.filterMode': { zh: '订阅模式', en: 'Filter Mode' },
+  'consumer.filterAll': { zh: '全量', en: 'Full' },
+  'consumer.filterTag': { zh: 'Tag 过滤', en: 'Tag Filter' },
+  'consumer.filterSql92': { zh: 'SQL92 过滤', en: 'SQL92 Filter' },
+  'consumer.expression': { zh: '订阅表达式', en: 'Expression' },
+  'consumer.viewDistribution': { zh: '查看分布', en: 'View Distribution' },
+  'consumer.resetToTime': { zh: '重置到指定时间', en: 'Reset to Time' },
+  'consumer.resetSuccess': { zh: '位点重置成功', en: 'Offset reset successfully' },
+
+  // ─── Home Page (additional) ───
+  'home.banner': {
+    zh: 'RocketMQ Studio — 跨集群 · 跨架构 · 跨云的统一管控平台',
+    en: 'RocketMQ Studio — Cross-cluster · Cross-arch · Cross-cloud unified 
management',
+  },
+  'home.placeholder': {
+    zh: '向 RocketMQ Bot 提问,全程加密、安全、可信',
+    en: 'Ask RocketMQ Bot, fully encrypted, secure, trusted',
+  },
+  'home.tools': { zh: '工具', en: 'Tools' },
+  'home.promptEnhance': { zh: 'Prompt 增强', en: 'Prompt Enhance' },
+  'home.docs': { zh: '文档中心', en: 'Documentation' },
+  'home.community': { zh: 'RocketMQ 社区', en: 'RocketMQ Community' },
+  'home.brand': { zh: 'RocketMQ Studio 出品', en: 'Powered by RocketMQ Studio' },
+
+  // ─── AI Page (additional) ───
+  'ai.recommended': { zh: '推荐', en: 'Rec.' },
+
+  // ─── Theme Constants ───
+  'theme.clusterV4': { zh: 'V4 直连', en: 'V4 Direct' },
+  'theme.clusterV5Local': { zh: 'V5 Proxy 单节点', en: 'V5 Proxy Local' },
+  'theme.clusterV5Cluster': { zh: 'V5 Proxy 集群', en: 'V5 Proxy Cluster' },
+  'theme.healthy': { zh: '运行中', en: 'Healthy' },
+  'theme.warning': { zh: '告警', en: 'Warning' },
+  'theme.error': { zh: '异常', en: 'Error' },
+  'theme.offline': { zh: '离线', en: 'Offline' },
+  'theme.connecting': { zh: '连接中', en: 'Connecting' },
+  'theme.topicNormal': { zh: '普通', en: 'Normal' },
+  'theme.topicFifo': { zh: '顺序', en: 'FIFO' },
+  'theme.topicDelay': { zh: '延迟', en: 'Delay' },
+  'theme.topicTransaction': { zh: '事务', en: 'Transaction' },
+  'theme.topicLite': { zh: 'LiteTopic', en: 'LiteTopic' },
+  'theme.protocolRemoting': { zh: 'Remoting', en: 'Remoting' },
+  'theme.protocolGrpc': { zh: 'gRPC', en: 'gRPC' },
+
   // ─── Settings ───
   'settings.title': { zh: '设置', en: 'Settings' },
   'settings.subtitle': { zh: '管理应用配置与数据源', en: 'Manage app settings and data 
sources' },
 
+  // ─── Certs ───
+  'cert.clusterName': { zh: 'K8s 集群名称', en: 'K8s Cluster Name' },
+  'cert.certName': { zh: '证书名称', en: 'Certificate Name' },
+  'cert.issuer': { zh: '签发者', en: 'Issuer' },
+  'cert.expiryTime': { zh: '到期时间', en: 'Expiry Time' },
+  'cert.daysRemaining': { zh: '剩余天数', en: 'Days Remaining' },
+  'cert.statusValid': { zh: '有效', en: 'Valid' },
+  'cert.statusExpiring': { zh: '即将过期', en: 'Expiring' },
+  'cert.statusExpired': { zh: '已过期', en: 'Expired' },
+  'cert.confirmDelete': { zh: '确认删除', en: 'Confirm Delete' },
+  'cert.deleteConfirm': {
+    zh: '确定要删除证书"{name}"吗?',
+    en: 'Are you sure to delete certificate "{name}"?',
+  },
+  'cert.deleted': { zh: '证书已删除: {name}', en: 'Certificate deleted: {name}' },
+  'cert.addCert': { zh: '添加证书', en: 'Add Certificate' },
+  'cert.searchPlaceholder': { zh: '搜索证书名称或集群', en: 'Search cert name or 
cluster' },
+  'cert.editCert': { zh: '编辑证书 — {name}', en: 'Edit Certificate — {name}' },
+  'cert.certUpdated': { zh: '证书「{name}」已更新', en: 'Certificate "{name}" 
updated' },
+  'cert.namespace': { zh: '命名空间', en: 'Namespace' },
+  'cert.issuerPlaceholder': { zh: '例:kubernetes-ca', en: 'e.g. kubernetes-ca' 
},
+  'cert.namespacePlaceholder': { zh: '例:kube-system', en: 'e.g. kube-system' },
+  'cert.featureWip': { zh: '添加证书功能开发中', en: 'Add certificate feature is in 
development' },
+  'cert.totalCount': { zh: '共 {count} 个证书', en: '{count} certificates total' },
+
+  // ─── Cluster ───
+  'cluster.brokerCount': { zh: 'Broker 数', en: 'Broker Count' },
+  'cluster.topicCount': { zh: 'Topic 数', en: 'Topic Count' },
+  'cluster.messageCount': { zh: '消息总量', en: 'Message Total' },
+  'cluster.totalTps': { zh: '总 TPS', en: 'Total TPS' },
+  'cluster.avgTps': { zh: '平均 TPS', en: 'Avg TPS' },
+  'cluster.consumerGroupCount': { zh: '消费者组数', en: 'Consumer Group Count' },
+  'cluster.createClusterWip': {
+    zh: '新建集群功能开发中',
+    en: 'Create cluster feature is in development',
+  },
+  'cluster.configTitle': { zh: '配置 - {name}', en: 'Config - {name}' },
+  'cluster.configUpdated': { zh: '配置已更新', en: 'Configuration updated' },
+  'cluster.flushDiskType': { zh: '刷盘方式', en: 'Flush Disk Type' },
+  'cluster.syncFlush': { zh: '同步刷盘', en: 'Sync Flush' },
+  'cluster.asyncFlush': { zh: '异步刷盘', en: 'Async Flush' },
+  'cluster.autoCreateTopic': { zh: '自动创建 Topic', en: 'Auto Create Topic' },
+  'cluster.autoCreateSubGroup': { zh: '自动创建订阅组', en: 'Auto Create Subscription 
Group' },
+  'cluster.maxMessageSize': { zh: '最大消息大小 (MB)', en: 'Max Message Size (MB)' },
+  'cluster.fileReservedTime': { zh: '文件保留时长 (小时)', en: 'File Retention Time 
(hours)' },
+  'cluster.writeQueues': { zh: '写队列数', en: 'Write Queues' },
+  'cluster.readQueues': { zh: '读队列数', en: 'Read Queues' },
+  'cluster.brokerPermission': { zh: 'Broker 权限', en: 'Broker Permission' },
+  'cluster.viewDetail': { zh: '查看详情: {addr}', en: 'View detail: {addr}' },
+  'cluster.restartProxyConfirm': {
+    zh: '确定要重启 Proxy "{addr}" 吗?',
+    en: 'Are you sure to restart Proxy "{addr}"?',
+  },
+  'cluster.restartProxySubmitted': {
+    zh: 'Proxy 重启已提交: {addr}',
+    en: 'Proxy restart submitted: {addr}',
+  },
+
+  // ─── Topic ───
+  'topic.type': { zh: '类型', en: 'Type' },
+  'topic.status': { zh: '状态', en: 'Status' },
+  'topic.serving': { zh: '服务中', en: 'Serving' },
+  'topic.createdAt': { zh: '创建时间', en: 'Created At' },
+  'topic.updatedAt': { zh: '修改时间', en: 'Updated At' },
+  'topic.topicName': { zh: 'Topic 名称', en: 'Topic Name' },
+  'topic.namespace': { zh: '命名空间', en: 'Namespace' },
+  'topic.cluster': { zh: '集群', en: 'Cluster' },
+  'topic.writeQueues': { zh: '写队列数', en: 'Write Queues' },
+  'topic.readQueues': { zh: '读队列数', en: 'Read Queues' },
+  'topic.perm': { zh: '权限', en: 'Permission' },
+  'topic.messageCount': { zh: '今日消息量', en: "Today's Messages" },
+  'topic.tps': { zh: 'TPS', en: 'TPS' },
+  'topic.consumerGroupCount': { zh: '消费者组数', en: 'Consumer Groups' },
+  'topic.quickFill': { zh: '快速填入:', en: 'Quick fill:' },
+  'topic.customProps': { zh: '自定义属性(可选)', en: 'Custom Properties (optional)' },
+  'topic.allNamespaces': { zh: '全部', en: 'All' },
+  'topic.remark': { zh: '备注', en: 'Remark' },
+  'topic.confirmDelete': { zh: '确认删除', en: 'Confirm Delete' },
+  'topic.deleteConfirm': {
+    zh: '确定要删除 Topic「{name}」吗?此操作不可撤销。',
+    en: 'Are you sure to delete Topic "{name}"? This action cannot be undone.',
+  },
+  'topic.deleted': { zh: 'Topic「{name}」已删除', en: 'Topic "{name}" deleted' },
+  'topic.permRW': { zh: '读写', en: 'Read/Write' },
+  'topic.permRO': { zh: '只读', en: 'Read-only' },
+  'topic.permWO': { zh: '只写', en: 'Write-only' },
+  'topic.brokerName': { zh: 'Broker 名称', en: 'Broker Name' },
+  'topic.brokerAddr': { zh: 'Broker 地址', en: 'Broker Address' },
+  'topic.writeQueue': { zh: '写队列', en: 'Write Queue' },
+  'topic.readQueue': { zh: '读队列', en: 'Read Queue' },
+  'topic.consumerGroup': { zh: '消费者组', en: 'Consumer Group' },
+  'topic.consumeMode': { zh: '消费模式', en: 'Consume Mode' },
+  'topic.consumeTps': { zh: '消费 TPS', en: 'Consume TPS' },
+  'topic.backlog': { zh: '堆积量', en: 'Backlog' },
+  'topic.broadcast': { zh: '广播消费', en: 'Broadcast' },
+  'topic.clustering': { zh: '集群消费', en: 'Clustering' },
+  'topic.basicInfo': { zh: '基本信息', en: 'Basic Info' },
+  'topic.routeInfo': { zh: '路由信息', en: 'Route Info' },
+  'topic.consumerInfo': { zh: '消费者', en: 'Consumers' },
+  'topic.createTopic': { zh: '创建 Topic', en: 'Create Topic' },
+  'topic.createSuccess': {
+    zh: 'Topic「{name}」创建成功',
+    en: 'Topic "{name}" created successfully',
+  },
+  'topic.topicNamePlaceholder': { zh: '请输入 Topic 名称', en: 'Enter topic name' },
+  'topic.topicNameRule': {
+    zh: '仅支持字母、数字、下划线、中划线、斜杠和星号',
+    en: 'Only letters, numbers, underscore, hyphen, slash and asterisk',
+  },
+  'topic.topicNameRequired': { zh: '请输入 Topic 名称', en: 'Please enter topic 
name' },
+  'topic.queueExtra': { zh: '每个 Broker 节点 8 个队列', en: '8 queues per Broker 
node' },
+  'topic.remarkPlaceholder': {
+    zh: '可选,描述 Topic 用途',
+    en: 'Optional, describe the topic purpose',
+  },
+  'topic.sendMsg': { zh: '发送消息到 {name}', en: 'Send message to {name}' },
+  'topic.sendSuccess': {
+    zh: '消息发送成功!MsgId: {id}',
+    en: 'Message sent successfully! MsgId: {id}',
+  },
+  'topic.tagPlaceholder': { zh: '可选,消息标签', en: 'Optional, message tag' },
+  'topic.keyPlaceholder': {
+    zh: '可选,消息 Key(用于查询)',
+    en: 'Optional, message key (for query)',
+  },
+  'topic.bodyLabel': { zh: '消息体 Body', en: 'Message Body' },
+  'topic.bodyRequired': { zh: '请输入消息体', en: 'Please enter message body' },
+  'topic.bodyPlaceholder': { zh: 'JSON 格式消息体', en: 'JSON format message body' 
},
+  'topic.propName': { zh: '属性名', en: 'Property Name' },
+  'topic.propValue': { zh: '属性值', en: 'Property Value' },
+  'topic.addProp': { zh: '添加属性', en: 'Add Property' },
+  'topic.confirmBatchDelete': { zh: '确认批量删除', en: 'Confirm Batch Delete' },
+  'topic.batchDeleteConfirm': {
+    zh: '确定要删除选中的 {count} 个 Topic 吗?此操作不可撤销。',
+    en: 'Are you sure to delete {count} selected topics? This action cannot be 
undone.',
+  },
+  'topic.batchDeleted': { zh: '已删除 {count} 个 Topic', en: '{count} topics 
deleted' },
+  'topic.importWip': { zh: '导入功能开发中', en: 'Import feature is in development' },
+  'topic.exported': { zh: '已导出 {count} 个 Topic', en: '{count} topics exported' 
},
+  'topic.totalCount': { zh: '共 {count} 个 Topic', en: '{count} topics total' },
+  'topic.searchPlaceholder': { zh: '搜索 Topic 名称', en: 'Search topic name' },
+  'topic.typeFilter': { zh: '类型筛选', en: 'Type filter' },
+  'topic.listView': { zh: '列表', en: 'List' },
+  'topic.cardView': { zh: '卡片', en: 'Card' },
+  'topic.showTotal': { zh: '共 {total} 条', en: '{total} total' },
+  'topic.orderEvent': { zh: '订单事件', en: 'Order Event' },
+  'topic.userEvent': { zh: '用户行为', en: 'User Event' },
+  'topic.paymentCallback': { zh: '支付回调', en: 'Payment Callback' },
+  'topic.inventoryChange': { zh: '库存变更', en: 'Inventory Change' },
+  'topic.notification': { zh: '通知消息', en: 'Notification' },
+  'topic.metrics': { zh: '监控指标', en: 'Metrics' },
+
+  // ─── Consumer ───
+  'consumer.subscriptionMode': { zh: '订阅模式', en: 'Subscription Mode' },
+  'consumer.subGroupType': { zh: '订阅组类型', en: 'Subscription Group Type' },
+  'consumer.maxRetry': { zh: '最大重试次数', en: 'Max Retries' },
+
   // ─── User Menu ───
   'user.profile': { zh: '个人中心', en: 'Profile' },
   'user.logout': { zh: '退出登录', en: 'Logout' },
@@ -412,18 +686,9 @@ const translations: Record<string, Record<Lang, string>> = 
{
   'topic.add': { zh: '添加主题', en: 'Add Topic' },
   'topic.config': { zh: '主题配置', en: 'Topic Config' },
   'topic.change': { zh: '修改主题', en: 'Modify Topic' },
-  'topic.name': { zh: '主题名', en: 'Topic Name' },
-  'topic.type': { zh: '消息类型', en: 'Message Type' },
-  'topic.normal': { zh: '普通消息', en: 'Normal' },
-  'topic.fifo': { zh: '顺序消息', en: 'FIFO' },
-  'topic.delay': { zh: '定时/延时消息', en: 'Delay' },
-  'topic.transaction': { zh: '事务消息', en: 'Transaction' },
   'topic.unspecified': { zh: '未指定', en: 'Unspecified' },
-  'topic.perm': { zh: '权限', en: 'Permission' },
   'topic.readQueueNums': { zh: '读队列数量', en: 'Read Queue Num' },
   'topic.writeQueueNums': { zh: '写队列数量', en: 'Write Queue Num' },
-  'topic.brokerName': { zh: 'Broker 名称', en: 'Broker Name' },
-  'topic.brokerAddr': { zh: 'Broker 地址', en: 'Broker Address' },
   'topic.clusterName': { zh: '集群名', en: 'Cluster Name' },
   'topic.selectCluster': { zh: '请选择集群', en: 'Select Cluster' },
   'topic.selectBroker': { zh: '请选择 Broker', en: 'Select Broker' },
@@ -431,15 +696,11 @@ const translations: Record<string, Record<Lang, string>> 
= {
   'topic.minOffset': { zh: '最小位点', en: 'Min Offset' },
   'topic.maxOffset': { zh: '最大位点', en: 'Max Offset' },
   'topic.lastUpdateTime': { zh: '上次更新时间', en: 'Last Update Time' },
-  'topic.sendMsg': { zh: '发送消息', en: 'Send Message' },
   'topic.resetOffset': { zh: '重置位点', en: 'Reset Offset' },
   'topic.skipAccumulate': { zh: '跳过堆积', en: 'Skip Accumulate' },
-  'topic.delete': { zh: '删除主题', en: 'Delete Topic' },
-  'topic.confirmDelete': { zh: '确认删除此主题?', en: 'Delete this topic?' },
   'topic.deleteWarning': { zh: '删除后无法恢复,请确认。', en: 'This cannot be undone.' },
   'topic.fetchFailed': { zh: '获取主题列表失败', en: 'Failed to fetch topic list' },
   'topic.operationSuccess': { zh: 'Topic 操作成功', en: 'Topic operation 
successful' },
-  'topic.searchPlaceholder': { zh: '搜索主题名称', en: 'Search topic name' },
   'topic.filterType': { zh: '消息类型', en: 'Message Type' },
   'topic.filterAll': { zh: '全部类型', en: 'All Types' },
 
diff --git a/web/src/pages/cluster/index.tsx b/web/src/pages/cluster/index.tsx
index f22ca74a..5fdcf63e 100644
--- a/web/src/pages/cluster/index.tsx
+++ b/web/src/pages/cluster/index.tsx
@@ -363,7 +363,7 @@ const ClusterPage = () => {
           <Button
             type="primary"
             icon={<PlusOutlined />}
-            onClick={() => message.info('新建集群功能开发中')}
+            onClick={() => message.info(t('cluster.createClusterWip'))}
           >
             {t('cluster.createCluster')}
           </Button>
@@ -380,51 +380,51 @@ const ClusterPage = () => {
 
         {selectedCluster && (
           <Modal
-            title={`配置 - ${selectedCluster.name}`}
+            title={t('cluster.configTitle', { name: selectedCluster.name })}
             open={configModalOpen}
             onCancel={() => setConfigModalOpen(false)}
             onOk={() => {
               configForm.validateFields().then(() => {
-                message.success('配置已更新');
+                message.success(t('cluster.configUpdated'));
                 setConfigModalOpen(false);
               });
             }}
             width={560}
           >
             <Form form={configForm} layout="vertical">
-              <Form.Item label="刷盘方式" name="flushDiskType">
+              <Form.Item label={t('cluster.flushDiskType')} 
name="flushDiskType">
                 <Radio.Group>
-                  <Radio value="SYNC_FLUSH">同步刷盘</Radio>
-                  <Radio value="ASYNC_FLUSH">异步刷盘</Radio>
+                  <Radio value="SYNC_FLUSH">{t('cluster.syncFlush')}</Radio>
+                  <Radio value="ASYNC_FLUSH">{t('cluster.asyncFlush')}</Radio>
                 </Radio.Group>
               </Form.Item>
               <Form.Item
-                label="自动创建 Topic"
+                label={t('cluster.autoCreateTopic')}
                 name="autoCreateTopicEnable"
                 valuePropName="checked"
               >
                 <Switch />
               </Form.Item>
               <Form.Item
-                label="自动创建订阅组"
+                label={t('cluster.autoCreateSubGroup')}
                 name="autoCreateSubscriptionGroup"
                 valuePropName="checked"
               >
                 <Switch />
               </Form.Item>
-              <Form.Item label="最大消息大小 (MB)" name="maxMessageSizeMB">
+              <Form.Item label={t('cluster.maxMessageSize')} 
name="maxMessageSizeMB">
                 <InputNumber min={1} max={128} style={{ width: '100%' }} />
               </Form.Item>
-              <Form.Item label="文件保留时长 (小时)" name="fileReservedTime">
+              <Form.Item label={t('cluster.fileReservedTime')} 
name="fileReservedTime">
                 <InputNumber min={1} max={720} style={{ width: '100%' }} />
               </Form.Item>
-              <Form.Item label="写队列数" name="writeQueueNums">
+              <Form.Item label={t('cluster.writeQueues')} 
name="writeQueueNums">
                 <InputNumber min={1} max={256} style={{ width: '100%' }} />
               </Form.Item>
-              <Form.Item label="读队列数" name="readQueueNums">
+              <Form.Item label={t('cluster.readQueues')} name="readQueueNums">
                 <InputNumber min={1} max={256} style={{ width: '100%' }} />
               </Form.Item>
-              <Form.Item label="Broker 权限" name="brokerPermission">
+              <Form.Item label={t('cluster.brokerPermission')} 
name="brokerPermission">
                 <InputNumber min={0} max={7} style={{ width: '100%' }} />
               </Form.Item>
             </Form>
@@ -468,9 +468,9 @@ const ClusterPage = () => {
         render: (status: string) => {
           const map: Record<string, { color: string; label: string }> = {
             healthy: { color: 'green', label: t('cluster.running') },
-            warning: { color: 'gold', label: '告警' },
-            error: { color: 'red', label: '异常' },
-            offline: { color: 'default', label: '离线' },
+            warning: { color: 'gold', label: t('cluster.warning') },
+            error: { color: 'red', label: t('cluster.error') },
+            offline: { color: 'default', label: t('cluster.offline') },
           };
           const cfg = map[status] ?? { color: 'default', label: status };
           return <Tag color={cfg.color}>{cfg.label}</Tag>;
@@ -530,9 +530,9 @@ const ClusterPage = () => {
         render: (status: string) => {
           const map: Record<string, { color: string; label: string }> = {
             healthy: { color: 'green', label: t('cluster.running') },
-            warning: { color: 'gold', label: '告警' },
-            error: { color: 'red', label: '异常' },
-            offline: { color: 'default', label: '离线' },
+            warning: { color: 'gold', label: t('cluster.warning') },
+            error: { color: 'red', label: t('cluster.error') },
+            offline: { color: 'default', label: t('cluster.offline') },
           };
           const cfg = map[status] ?? { color: 'default', label: status };
           return <Tag color={cfg.color}>{cfg.label}</Tag>;
@@ -661,9 +661,9 @@ const ClusterPage = () => {
         render: (status: string) => {
           const map: Record<string, { color: string; label: string }> = {
             healthy: { color: 'green', label: t('cluster.running') },
-            warning: { color: 'gold', label: '告警' },
-            error: { color: 'red', label: '异常' },
-            offline: { color: 'default', label: '离线' },
+            warning: { color: 'gold', label: t('cluster.warning') },
+            error: { color: 'red', label: t('cluster.error') },
+            offline: { color: 'default', label: t('cluster.offline') },
           };
           const cfg = map[status] ?? { color: 'default', label: status };
           return <Tag color={cfg.color}>{cfg.label}</Tag>;
@@ -704,7 +704,7 @@ const ClusterPage = () => {
               size="small"
               icon={<EyeOutlined />}
               style={{ borderColor: '#1677ff', color: '#1677ff' }}
-              onClick={() => message.info(`查看详情: ${record.addr}`)}
+              onClick={() => message.info(t('cluster.viewDetail', { addr: 
record.addr }))}
             >
               {t('common.detail')}
             </Button>
@@ -715,10 +715,11 @@ const ClusterPage = () => {
               onClick={() => {
                 Modal.confirm({
                   title: t('cluster.confirmRestart'),
-                  content: `确定要重启 Proxy "${record.addr}" 吗?`,
-                  okText: '确认',
-                  cancelText: '取消',
-                  onOk: () => message.success(`Proxy 重启已提交: ${record.addr}`),
+                  content: t('cluster.restartProxyConfirm', { addr: 
record.addr }),
+                  okText: t('common.confirm'),
+                  cancelText: t('common.cancel'),
+                  onOk: () =>
+                    message.success(t('cluster.restartProxySubmitted', { addr: 
record.addr })),
                 });
               }}
             >
@@ -744,7 +745,7 @@ const ClusterPage = () => {
           <Button
             type="primary"
             icon={<PlusOutlined />}
-            onClick={() => message.info('新建集群功能开发中')}
+            onClick={() => message.info(t('cluster.createClusterWip'))}
           >
             {t('cluster.createCluster')}
           </Button>
diff --git a/web/src/pages/home/dashboard.tsx b/web/src/pages/home/dashboard.tsx
index 349b7250..1242fca6 100644
--- a/web/src/pages/home/dashboard.tsx
+++ b/web/src/pages/home/dashboard.tsx
@@ -123,7 +123,7 @@ const DashboardPage = () => {
       key: 'type',
       render: (type: string) => {
         const info = CLUSTER_TYPE_MAP[type];
-        return info ? <Tag color={info.color}>{info.label}</Tag> : type;
+        return info ? <Tag color={info.color}>{t(info.labelKey)}</Tag> : type;
       },
     },
     {
diff --git a/web/src/pages/instance/consumer.tsx 
b/web/src/pages/instance/consumer.tsx
index 9e0ec7c3..d7cec904 100644
--- a/web/src/pages/instance/consumer.tsx
+++ b/web/src/pages/instance/consumer.tsx
@@ -177,8 +177,8 @@ const ConsumerPage = () => {
       width: 110,
       sorter: (a, b) => 
a.subscriptionDataType.localeCompare(b.subscriptionDataType),
       render: (type: string) => {
-        const config = TOPIC_TYPE_MAP[type] || { label: type, color: 'default' 
};
-        return <Tag color={config.color}>{config.label}</Tag>;
+        const config = TOPIC_TYPE_MAP[type] || { labelKey: type, color: 
'default' };
+        return <Tag color={config.color}>{t(config.labelKey)}</Tag>;
       },
     },
     {
@@ -374,8 +374,8 @@ const ConsumerPage = () => {
       key: 'protocol',
       width: 100,
       render: (protocol: string) => {
-        const config = PROTOCOL_MAP[protocol] || { label: protocol, color: 
'default' };
-        return <Tag color={config.color}>{config.label}</Tag>;
+        const config = PROTOCOL_MAP[protocol] || { labelKey: protocol, color: 
'default' };
+        return <Tag color={config.color}>{t(config.labelKey)}</Tag>;
       },
     },
     {
@@ -710,8 +710,9 @@ const ConsumerPage = () => {
                             
TOPIC_TYPE_MAP[selectedGroup.subscriptionDataType]?.color || 'default'
                           }
                         >
-                          
{TOPIC_TYPE_MAP[selectedGroup.subscriptionDataType]?.label ||
-                            selectedGroup.subscriptionDataType}
+                          {TOPIC_TYPE_MAP[selectedGroup.subscriptionDataType]
+                            ? 
t(TOPIC_TYPE_MAP[selectedGroup.subscriptionDataType].labelKey)
+                            : selectedGroup.subscriptionDataType}
                         </Tag>
                       </Descriptions.Item>
                       <Descriptions.Item label="消费延迟">
diff --git a/web/src/pages/instance/topic.tsx b/web/src/pages/instance/topic.tsx
index fc3af2eb..4569bfac 100644
--- a/web/src/pages/instance/topic.tsx
+++ b/web/src/pages/instance/topic.tsx
@@ -349,7 +349,7 @@ const TopicPage = () => {
       sorter: (a, b) => a.type.localeCompare(b.type),
       render: (type: string) => {
         const cfg = TOPIC_TYPE_MAP[type];
-        return cfg ? <Tag color={cfg.color}>{cfg.label}</Tag> : 
<Tag>{type}</Tag>;
+        return cfg ? <Tag color={cfg.color}>{t(cfg.labelKey)}</Tag> : 
<Tag>{type}</Tag>;
       },
     },
     {
@@ -458,7 +458,9 @@ const TopicPage = () => {
           {topic.name}
         </Descriptions.Item>
         <Descriptions.Item label="类型">
-          <Tag color={typeInfo?.color}>{typeInfo?.label}</Tag>
+          <Tag color={typeInfo?.color}>
+            {typeInfo?.labelKey ? t(typeInfo.labelKey) : topic.type}
+          </Tag>
         </Descriptions.Item>
         <Descriptions.Item label="命名空间">
           <Tag>{topic.namespace}</Tag>
@@ -466,7 +468,7 @@ const TopicPage = () => {
         <Descriptions.Item label="集群" span={2}>
           <Space>
             <Text>{topic.clusterId}</Text>
-            {clusterType && <Tag 
color={clusterType.color}>{clusterType.label}</Tag>}
+            {clusterType && <Tag 
color={clusterType.color}>{t(clusterType.labelKey)}</Tag>}
           </Space>
         </Descriptions.Item>
         <Descriptions.Item label="写队列数">{topic.writeQueues}</Descriptions.Item>
@@ -506,7 +508,9 @@ const TopicPage = () => {
                 <Text strong style={{ fontSize: 15 }}>
                   {topic.name}
                 </Text>
-                <Tag color={typeInfo?.color}>{typeInfo?.label}</Tag>
+                <Tag color={typeInfo?.color}>
+                  {typeInfo?.labelKey ? t(typeInfo.labelKey) : topic.type}
+                </Tag>
               </Flex>
 
               {/* Namespace + cluster tags */}
@@ -514,7 +518,7 @@ const TopicPage = () => {
                 <Tag style={{ fontSize: 11 }}>{topic.namespace}</Tag>
                 {clusterType && (
                   <Tag color={clusterType.color} style={{ fontSize: 11 }}>
-                    {clusterType.label}
+                    {t(clusterType.labelKey)}
                   </Tag>
                 )}
               </Space>

Reply via email to