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


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new 14da4b95a fix(studio): localize settings management tabs (#2565)
14da4b95a is described below

commit 14da4b95a3eeedd86bdec544c8f40baeff92004f
Author: aias00 <[email protected]>
AuthorDate: Tue Aug 25 18:08:09 2026 +0800

    fix(studio): localize settings management tabs (#2565)
---
 web/src/i18n/LangContext.tsx                       |   2 +-
 web/src/i18n/translations.ts                       | 218 +++++++++++++++++++++
 web/src/pages/settings/AiAssistantTab.tsx          | 110 ++++++-----
 web/src/pages/settings/CloudCredentialTab.tsx      | 113 ++++++-----
 web/src/pages/settings/DataSourceTab.tsx           | 105 +++++-----
 web/src/pages/settings/GeneralSettingsTab.tsx      |  74 +++----
 .../settings/__tests__/AiAssistantTab.test.tsx     |  10 +
 .../settings/__tests__/CloudCredentialTab.test.tsx |   9 +
 .../settings/__tests__/DataSourceTab.test.tsx      |  15 ++
 .../settings/__tests__/GeneralSettingsTab.test.tsx |  18 +-
 web/src/pages/settings/index.tsx                   |  16 +-
 11 files changed, 496 insertions(+), 194 deletions(-)

diff --git a/web/src/i18n/LangContext.tsx b/web/src/i18n/LangContext.tsx
index 120baef20..d4a27545e 100644
--- a/web/src/i18n/LangContext.tsx
+++ b/web/src/i18n/LangContext.tsx
@@ -28,7 +28,7 @@ interface LangContextType {
 const LangContext = createContext<LangContextType>({
   lang: 'zh',
   setLang: () => {},
-  t: (key) => key,
+  t: (key) => translations[key]?.zh ?? key,
 });
 
 export const LangProvider = ({ children }: { children: ReactNode }) => {
diff --git a/web/src/i18n/translations.ts b/web/src/i18n/translations.ts
index 49dd7263c..41ff6fb9d 100644
--- a/web/src/i18n/translations.ts
+++ b/web/src/i18n/translations.ts
@@ -629,7 +629,225 @@ const translations: Record<string, Record<Lang, string>> 
= {
   // ─── Settings ───
   'settings.title': { zh: '设置', en: 'Settings' },
   'settings.subtitle': { zh: '管理应用配置与数据源', en: 'Manage app settings and data 
sources' },
+  'settings.tabGeneral': { zh: '通用设置', en: 'General' },
+  'settings.tabAi': { zh: 'AI 助手', en: 'AI Assistant' },
+  'settings.tabCredential': { zh: '云凭据管理', en: 'Cloud Credentials' },
+  'settings.tabDatasource': { zh: '数据源管理', en: 'Data Sources' },
+  'settings.tabAbout': { zh: '关于', en: 'About' },
   'settings.dataSourceNotTested': { zh: '未检测', en: 'Not tested' },
+  'settings.loadFailed': {
+    zh: '设置加载失败,请稍后重试',
+    en: 'Failed to load settings. Please try again later.',
+  },
+  'settings.saveSuccess': { zh: '设置已保存', en: 'Settings saved' },
+  'settings.saveFailed': {
+    zh: '设置保存失败,请稍后重试',
+    en: 'Failed to save settings. Please try again later.',
+  },
+  'settings.saveSettings': { zh: '保存设置', en: 'Save settings' },
+  'settings.testConnection': { zh: '测试连接', en: 'Test connection' },
+  'settings.confirmAction': { zh: '确定', en: 'Confirm' },
+  'settings.appearance': { zh: '界面偏好', en: 'Appearance preferences' },
+  'settings.themeMode': { zh: '主题模式', en: 'Theme mode' },
+  'settings.themeHelp': {
+    zh: '作用于前端展示,跟随系统时随操作系统外观变化',
+    en: 'Changes the UI appearance and follows your operating system when set 
to System.',
+  },
+  'settings.lightTheme': { zh: '浅色', en: 'Light' },
+  'settings.darkTheme': { zh: '深色', en: 'Dark' },
+  'settings.systemTheme': { zh: '跟随系统', en: 'System' },
+  'settings.compactMode': { zh: '紧凑模式', en: 'Compact mode' },
+  'settings.compactHelp': {
+    zh: '减小组件间距,单屏展示更多内容',
+    en: 'Reduces component spacing to show more content on one screen.',
+  },
+  'settings.securitySession': { zh: '安全与会话', en: 'Security and sessions' },
+  'settings.sessionTimeout': { zh: '会话超时', en: 'Session timeout' },
+  'settings.sessionTimeoutHelp': {
+    zh: '应用于新创建的会话,已登录用户保持原到期时间',
+    en: 'Applies to new sessions; existing sessions keep their current 
expiration.',
+  },
+  'settings.sessionTimeoutRequired': { zh: '请输入会话超时时长', en: 'Enter a session 
timeout.' },
+  'settings.minutes': { zh: '分钟', en: 'minutes' },
+  'settings.sessionTimeoutUnit': { zh: '会话超时单位', en: 'Session timeout unit' },
+  'settings.loginProtectionHelp': {
+    zh: '登录保护由服务端 STUDIO_AUTH_LOGIN_REQUIRED 配置决定,修改后重启服务生效。',
+    en: 'Login protection is controlled by the server-side 
STUDIO_AUTH_LOGIN_REQUIRED setting and takes effect after restart.',
+  },
+  'settings.notification': { zh: '通知设置', en: 'Notification settings' },
+  'settings.dingtalkWebhook': { zh: '钉钉机器人 Webhook', en: 'DingTalk bot 
webhook' },
+  'settings.dingtalkWebhookHelp': {
+    zh: '告警规则选择钉钉渠道时推送到该机器人',
+    en: 'Alert rules using DingTalk are sent to this bot.',
+  },
+  'settings.emailRecipients': { zh: '邮件收件人', en: 'Email recipients' },
+  'settings.emailRecipientsHelp': {
+    zh: '多个地址用英文逗号分隔;邮件发送通道后续接入',
+    en: 'Separate addresses with commas; email delivery will be added later.',
+  },
+  'settings.smsWebhook': { zh: '短信网关 Webhook', en: 'SMS gateway webhook' },
+  'settings.smsWebhookHelp': {
+    zh: '占位配置,短信发送通道后续接入',
+    en: 'Placeholder configuration; SMS delivery will be added later.',
+  },
+  'settings.dataSourceLoadFailed': {
+    zh: '数据源加载失败,请稍后重试',
+    en: 'Failed to load data sources. Please try again later.',
+  },
+  'settings.dataSourceAuthTestHint': {
+    zh: '认证数据源请编辑后输入凭据再测试连接',
+    en: 'Edit this authenticated data source and enter credentials before 
testing.',
+  },
+  'settings.connectionTestFailed': {
+    zh: '连接测试失败,请稍后重试',
+    en: 'Connection test failed. Please try again later.',
+  },
+  'settings.dataSourceUpdated': { zh: '数据源已更新', en: 'Data source updated' },
+  'settings.dataSourceAdded': { zh: '数据源已添加', en: 'Data source added' },
+  'settings.dataSourceSaveFailed': {
+    zh: '保存数据源失败,请稍后重试',
+    en: 'Failed to save data source. Please try again later.',
+  },
+  'settings.dataSourceDeleted': { zh: '数据源已删除', en: 'Data source deleted' },
+  'settings.dataSourceDeleteFailed': {
+    zh: '删除数据源失败,请稍后重试',
+    en: 'Failed to delete data source. Please try again later.',
+  },
+  'settings.instances': { zh: '适用实例', en: 'Applicable instances' },
+  'settings.global': { zh: '全局', en: 'Global' },
+  'settings.authentication': { zh: '认证方式', en: 'Authentication' },
+  'settings.deleteDataSourceConfirm': {
+    zh: '确定要删除该数据源吗?',
+    en: 'Delete this data source?',
+  },
+  'settings.searchDataSources': { zh: '搜索数据源名称', en: 'Search data source 
names' },
+  'settings.allTypes': { zh: '全部类型', en: 'All types' },
+  'settings.addDataSource': { zh: '添加数据源', en: 'Add data source' },
+  'settings.editDataSource': { zh: '编辑数据源', en: 'Edit data source' },
+  'settings.dataSourceNameRequired': { zh: '请输入数据源名称', en: 'Enter a data 
source name.' },
+  'settings.dataSourceNameExample': {
+    zh: '例如:Prometheus 生产监控',
+    en: 'Example: Production Prometheus',
+  },
+  'settings.selectType': { zh: '请选择类型', en: 'Select a type' },
+  'settings.dataSourceUrlRequired': { zh: '请输入数据源 URL', en: 'Enter a data 
source URL.' },
+  'settings.instancesHelp': {
+    zh: '留空时可用于所有实例',
+    en: 'Leave empty to use this data source for all instances.',
+  },
+  'settings.selectDataSourceInstances': {
+    zh: '选择此数据源对应的实例',
+    en: 'Select instances for this data source',
+  },
+  'settings.username': { zh: '用户名', en: 'Username' },
+  'settings.usernameRequired': { zh: '请输入用户名', en: 'Enter a username.' },
+  'settings.password': { zh: '密码', en: 'Password' },
+  'settings.passwordRequired': { zh: '请输入密码', en: 'Enter a password.' },
+  'settings.bearerTokenRequired': { zh: '请输入 Bearer Token', en: 'Enter a 
bearer token.' },
+  'settings.enterToken': { zh: '请输入 Token', en: 'Enter a token' },
+  'settings.totalRecords': { zh: '共 {total} 条', en: '{total} records' },
+  'settings.credentialLoadFailed': {
+    zh: '云凭据加载失败,请稍后重试',
+    en: 'Failed to load cloud credentials. Please try again later.',
+  },
+  'settings.credentialUpdated': { zh: '云凭据已更新', en: 'Cloud credential updated' 
},
+  'settings.credentialAdded': { zh: '云凭据已添加', en: 'Cloud credential added' },
+  'settings.credentialSaveFailed': {
+    zh: '保存云凭据失败,请稍后重试',
+    en: 'Failed to save cloud credential. Please try again later.',
+  },
+  'settings.credentialDeleted': { zh: '云凭据已删除', en: 'Cloud credential deleted' 
},
+  'settings.credentialDeleteFailed': {
+    zh: '删除云凭据失败(可能仍被实例引用),请稍后重试',
+    en: 'Failed to delete cloud credential. It may still be used by an 
instance.',
+  },
+  'settings.cloudVendor': { zh: '云厂商', en: 'Cloud vendor' },
+  'settings.remark': { zh: '备注', en: 'Remark' },
+  'settings.createdAt': { zh: '创建时间', en: 'Created at' },
+  'settings.deleteCredentialConfirm': {
+    zh: '确定要删除该云凭据吗?',
+    en: 'Delete this cloud credential?',
+  },
+  'settings.searchCredentials': { zh: '搜索凭据名称', en: 'Search credential names' 
},
+  'settings.allCloudVendors': { zh: '全部云厂商', en: 'All cloud vendors' },
+  'settings.addCredential': { zh: '添加云凭据', en: 'Add credential' },
+  'settings.editCredential': { zh: '编辑云凭据', en: 'Edit credential' },
+  'settings.credentialNameRequired': { zh: '请输入凭据名称', en: 'Enter a credential 
name.' },
+  'settings.credentialNameExample': {
+    zh: '例如:阿里云测试账号',
+    en: 'Example: Aliyun test account',
+  },
+  'settings.selectCloudVendor': { zh: '请选择云厂商', en: 'Select a cloud vendor.' },
+  'settings.accessKeyRequired': { zh: '请输入 AccessKey', en: 'Enter an 
AccessKey.' },
+  'settings.secretKeyRequired': { zh: '请输入 SecretKey', en: 'Enter a 
SecretKey.' },
+  'settings.keepSecretKey': {
+    zh: '留空表示保持原 SecretKey 不变',
+    en: 'Leave empty to keep the existing SecretKey.',
+  },
+  'settings.enterSecretKey': { zh: '请输入 SecretKey', en: 'Enter a SecretKey' },
+  'settings.optional': { zh: '可选', en: 'Optional' },
+  'settings.aliyun': { zh: '阿里云', en: 'Aliyun' },
+  'settings.tencent': { zh: '腾讯云', en: 'Tencent Cloud' },
+  'settings.aiEngineModel': { zh: '执行引擎与模型', en: 'Execution engine and model' 
},
+  'settings.aiEngine': { zh: '执行引擎', en: 'Execution engine' },
+  'settings.aiEngineHelp': {
+    zh: 'Claude Code / Qoder 引擎在服务器上以 CLI 子进程方式运行,凭据经环境变量注入;HTTP 引擎直连 OpenAI 
兼容接口',
+    en: 'Claude Code and Qoder run as CLI subprocesses on the server with 
credentials supplied through environment variables; HTTP connects directly to 
an OpenAI-compatible API.',
+  },
+  'settings.modelProvider': { zh: '模型服务商', en: 'Model provider' },
+  'settings.modelProviderRequired': { zh: '请选择模型服务商', en: 'Select a model 
provider.' },
+  'settings.model': { zh: '模型', en: 'Model' },
+  'settings.modelRequired': { zh: '请选择或输入模型', en: 'Select or enter a model.' },
+  'settings.modelHelp': {
+    zh: '可从下拉选择,也可直接输入自定义模型 ID',
+    en: 'Select from the list or enter a custom model ID.',
+  },
+  'settings.selectOrEnterModel': { zh: '选择或输入模型', en: 'Select or enter a 
model' },
+  'settings.credentialsEndpoint': { zh: '凭据与接入地址', en: 'Credentials and 
endpoint' },
+  'settings.apiKeyConfiguredHelp': {
+    zh: '已配置(可能来自环境变量 RMQ_LLM_TOKEN);留空将保留现有密钥',
+    en: 'Configured (possibly from RMQ_LLM_TOKEN); leave empty to keep the 
existing key.',
+  },
+  'settings.enterApiKey': { zh: '请输入 API Key', en: 'Enter an API key' },
+  'settings.apiKeyConfiguredPlaceholder': {
+    zh: '••••••••(已配置,留空保留)',
+    en: '•••••••• (configured; leave empty to keep)',
+  },
+  'settings.apiKeyConfigured': { zh: '密钥已配置', en: 'API key configured' },
+  'settings.apiBaseRequired': { zh: '请输入 API Base URL', en: 'Enter an API base 
URL.' },
+  'settings.apiBaseInvalid': { zh: '需为 http/https 地址', en: 'Use an http or 
https URL.' },
+  'settings.generationParameters': { zh: '生成参数', en: 'Generation parameters' },
+  'settings.connectionSucceeded': { zh: '连接成功', en: 'Connection succeeded' },
+  'settings.connectionTestFailedShort': { zh: '连接测试失败', en: 'Connection test 
failed' },
+  'settings.connectionTestRequestFailed': {
+    zh: '连接测试请求失败,请稍后重试',
+    en: 'Connection test request failed. Please try again later.',
+  },
+  'settings.saveSucceeded': { zh: '保存成功', en: 'Saved' },
+  'settings.saveFailedShort': { zh: '保存失败', en: 'Save failed' },
+  'settings.saveRequestFailed': {
+    zh: '保存请求失败,请稍后重试',
+    en: 'Save request failed. Please try again later.',
+  },
+  'settings.tongyi': { zh: '通义千问(DashScope)', en: 'Tongyi Qianwen (DashScope)' 
},
+  'settings.ollamaLocal': { zh: 'Ollama(本地)', en: 'Ollama (local)' },
+  'settings.claudeCodeDefault': { zh: 'Claude Code(默认)', en: 'Claude Code 
(default)' },
+  'settings.httpOpenAiCompatible': { zh: 'HTTP(OpenAI 兼容)', en: 'HTTP 
(OpenAI-compatible)' },
+  'settings.dashscopeStandard': {
+    zh: '百炼 DashScope 标准(compatible-mode)',
+    en: 'DashScope standard (compatible-mode)',
+  },
+  'settings.tokenPlanOpenAi': {
+    zh: 'Token Plan 网关(OpenAI 兼容)',
+    en: 'Token Plan gateway (OpenAI-compatible)',
+  },
+  'settings.tokenPlanAnthropic': {
+    zh: 'Token Plan 网关(Anthropic 兼容)',
+    en: 'Token Plan gateway (Anthropic-compatible)',
+  },
+  'settings.openaiOfficial': { zh: 'OpenAI 官方', en: 'OpenAI official' },
+  'settings.anthropicOfficial': { zh: 'Anthropic 官方', en: 'Anthropic official' 
},
+  'settings.deepseekOfficial': { zh: 'DeepSeek 官方', en: 'DeepSeek official' },
 
   // ─── Certs ───
   'cert.clusterName': { zh: 'K8s 集群名称', en: 'K8s Cluster Name' },
diff --git a/web/src/pages/settings/AiAssistantTab.tsx 
b/web/src/pages/settings/AiAssistantTab.tsx
index e920ec3bd..d0d80d901 100644
--- a/web/src/pages/settings/AiAssistantTab.tsx
+++ b/web/src/pages/settings/AiAssistantTab.tsx
@@ -45,56 +45,58 @@ import {
 } from '../../api/llm';
 import { fallbackModelOptions } from '../studio/llmModelOptions';
 
-const PROVIDER_OPTIONS = [
-  { value: 'tongyi', label: '通义千问(DashScope)' },
+const DEFAULT_BASE_URL: Record<string, string> = {
+  tongyi: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
+  openai: 'https://api.openai.com/v1',
+  anthropic: 'https://api.anthropic.com',
+  deepseek: 'https://api.deepseek.com/v1',
+  ollama: 'http://localhost:11434/v1',
+};
+
+const providerOptions = (t: (key: string) => string) => [
+  { value: 'tongyi', label: t('settings.tongyi') },
   { value: 'openai', label: 'OpenAI' },
-  { value: 'anthropic', label: 'Anthropic(Claude)' },
+  { value: 'anthropic', label: 'Anthropic (Claude)' },
   { value: 'azure', label: 'Azure OpenAI' },
   { value: 'deepseek', label: 'DeepSeek' },
-  { value: 'ollama', label: 'Ollama(本地)' },
+  { value: 'ollama', label: t('settings.ollamaLocal') },
   { value: 'bedrock', label: 'AWS Bedrock' },
 ];
 
-const ENGINE_OPTIONS = [
-  { value: 'claude-code', label: 'Claude Code(默认)' },
+const engineOptions = (t: (key: string) => string) => [
+  { value: 'claude-code', label: t('settings.claudeCodeDefault') },
   { value: 'qoder', label: 'Qoder CLI' },
-  { value: 'http', label: 'HTTP(OpenAI 兼容)' },
+  { value: 'http', label: t('settings.httpOpenAiCompatible') },
 ];
 
-const DEFAULT_BASE_URL: Record<string, string> = {
-  tongyi: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
-  openai: 'https://api.openai.com/v1',
-  anthropic: 'https://api.anthropic.com',
-  deepseek: 'https://api.deepseek.com/v1',
-  ollama: 'http://localhost:11434/v1',
-};
-
-const BASE_URL_PRESETS: Record<string, { value: string; label: string }[]> = {
+const baseUrlPresets = (
+  t: (key: string) => string,
+): Record<string, { value: string; label: string }[]> => ({
   tongyi: [
     {
       value: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
-      label: '百炼 DashScope 标准(compatible-mode)',
+      label: t('settings.dashscopeStandard'),
     },
     {
       value: 
'https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1',
-      label: 'Token Plan 网关(OpenAI 兼容)',
+      label: t('settings.tokenPlanOpenAi'),
     },
     {
       value: 'https://token-plan.cn-beijing.maas.aliyuncs.com/apps/anthropic',
-      label: 'Token Plan 网关(Anthropic 兼容)',
+      label: t('settings.tokenPlanAnthropic'),
     },
   ],
-  openai: [{ value: 'https://api.openai.com/v1', label: 'OpenAI 官方' }],
+  openai: [{ value: 'https://api.openai.com/v1', label: 
t('settings.openaiOfficial') }],
   anthropic: [
-    { value: 'https://api.anthropic.com', label: 'Anthropic 官方' },
+    { value: 'https://api.anthropic.com', label: 
t('settings.anthropicOfficial') },
     {
       value: 'https://token-plan.cn-beijing.maas.aliyuncs.com/apps/anthropic',
-      label: 'Token Plan 网关(Anthropic 兼容)',
+      label: t('settings.tokenPlanAnthropic'),
     },
   ],
-  deepseek: [{ value: 'https://api.deepseek.com/v1', label: 'DeepSeek 官方' }],
-  ollama: [{ value: 'http://localhost:11434/v1', label: 'Ollama 本地' }],
-};
+  deepseek: [{ value: 'https://api.deepseek.com/v1', label: 
t('settings.deepseekOfficial') }],
+  ollama: [{ value: 'http://localhost:11434/v1', label: 
t('settings.ollamaLocal') }],
+});
 
 interface TestState {
   success: boolean;
@@ -222,11 +224,11 @@ export const AiAssistantTab = () => {
 
   const applyTestResult = (result: LlmTestResult) => {
     if (result.status === 0) {
-      setTestResult({ success: true, msg: result.msg || '连接成功' });
+      setTestResult({ success: true, msg: result.msg || 
t('settings.connectionSucceeded') });
     } else {
       setTestResult({
         success: false,
-        msg: result.errMsg || '连接测试失败',
+        msg: result.errMsg || t('settings.connectionTestFailedShort'),
         hint: result.hint,
       });
     }
@@ -250,7 +252,7 @@ export const AiAssistantTab = () => {
       }
     } catch {
       if (testRequestIdRef.current === requestId) {
-        setTestResult({ success: false, msg: '连接测试请求失败,请稍后重试' });
+        setTestResult({ success: false, msg: 
t('settings.connectionTestRequestFailed') });
       }
     } finally {
       if (testRequestIdRef.current === requestId) {
@@ -266,7 +268,7 @@ export const AiAssistantTab = () => {
     try {
       const result = await saveLlmConfig(payload);
       if (result.status === 0) {
-        message.success('保存成功');
+        message.success(t('settings.saveSucceeded'));
         if (payload.apiKey) {
           setApiKeyConfigured(true);
           form.setFieldValue('apiKey', undefined);
@@ -279,10 +281,10 @@ export const AiAssistantTab = () => {
           message.warning(t('ai.modelsRefreshFailedAfterSave'));
         }
       } else {
-        message.error(result.errMsg || '保存失败');
+        message.error(result.errMsg || t('settings.saveFailedShort'));
       }
     } catch {
-      message.error('保存请求失败,请稍后重试');
+      message.error(t('settings.saveRequestFailed'));
     } finally {
       setSaving(false);
     }
@@ -302,40 +304,40 @@ export const AiAssistantTab = () => {
             title={
               <Space>
                 <RobotOutlined />
-                执行引擎与模型
+                {t('settings.aiEngineModel')}
               </Space>
             }
           >
             <Row gutter={16}>
               <Col span={12}>
                 <Form.Item
-                  label="执行引擎"
+                  label={t('settings.aiEngine')}
                   name="engine"
-                  extra="Claude Code / Qoder 引擎在服务器上以 CLI 
子进程方式运行,凭据经环境变量注入;HTTP 引擎直连 OpenAI 兼容接口"
+                  extra={t('settings.aiEngineHelp')}
                 >
-                  <Select options={ENGINE_OPTIONS} />
+                  <Select options={engineOptions(t)} />
                 </Form.Item>
               </Col>
               <Col span={12}>
                 <Form.Item
-                  label="模型服务商"
+                  label={t('settings.modelProvider')}
                   name="provider"
-                  rules={[{ required: true, message: '请选择模型服务商' }]}
+                  rules={[{ required: true, message: 
t('settings.modelProviderRequired') }]}
                 >
-                  <Select options={PROVIDER_OPTIONS} 
onChange={handleProviderChange} />
+                  <Select options={providerOptions(t)} 
onChange={handleProviderChange} />
                 </Form.Item>
               </Col>
             </Row>
             <Form.Item
-              label="模型"
+              label={t('settings.model')}
               name="model"
-              rules={[{ required: true, message: '请选择或输入模型' }]}
-              extra="可从下拉选择,也可直接输入自定义模型 ID"
+              rules={[{ required: true, message: t('settings.modelRequired') 
}]}
+              extra={t('settings.modelHelp')}
               style={{ marginBottom: 0 }}
             >
               <AutoComplete
                 options={modelOptions}
-                placeholder="选择或输入模型"
+                placeholder={t('settings.selectOrEnterModel')}
                 filterOption={(input, option) =>
                   String(option?.value ?? '')
                     .toLowerCase()
@@ -351,7 +353,7 @@ export const AiAssistantTab = () => {
             title={
               <Space>
                 <KeyOutlined />
-                凭据与接入地址
+                {t('settings.credentialsEndpoint')}
               </Space>
             }
           >
@@ -359,19 +361,19 @@ export const AiAssistantTab = () => {
               label="API Key"
               name="apiKey"
               extra={
-                apiKeyConfigured
-                  ? '已配置(可能来自环境变量 RMQ_LLM_TOKEN);留空将保留现有密钥'
-                  : '请输入 API Key'
+                apiKeyConfigured ? t('settings.apiKeyConfiguredHelp') : 
t('settings.enterApiKey')
               }
             >
               <Input.Password
-                placeholder={apiKeyConfigured ? '••••••••(已配置,留空保留)' : 
'sk-...'}
+                placeholder={
+                  apiKeyConfigured ? t('settings.apiKeyConfiguredPlaceholder') 
: 'sk-...'
+                }
                 autoComplete="new-password"
               />
             </Form.Item>
             {apiKeyConfigured && (
               <div style={{ marginTop: -16, marginBottom: 16 }}>
-                <Tag color="green">密钥已配置</Tag>
+                <Tag color="green">{t('settings.apiKeyConfigured')}</Tag>
               </div>
             )}
 
@@ -379,10 +381,10 @@ export const AiAssistantTab = () => {
               label="API Base URL"
               name="apiBase"
               rules={[
-                { required: true, message: '请输入 API Base URL' },
+                { required: true, message: t('settings.apiBaseRequired') },
                 {
                   pattern: /^https?:\/\/.+/,
-                  message: '需为 http/https 地址',
+                  message: t('settings.apiBaseInvalid'),
                 },
               ]}
               style={{
@@ -391,7 +393,7 @@ export const AiAssistantTab = () => {
               }}
             >
               <AutoComplete
-                options={BASE_URL_PRESETS[selectedProvider ?? 'tongyi'] ?? []}
+                options={baseUrlPresets(t)[selectedProvider ?? 'tongyi'] ?? []}
                 placeholder="https://dashscope.aliyuncs.com/compatible-mode/v1";
                 suffixIcon={<CaretDownOutlined style={{ color: '#9CA3AF' }} />}
                 filterOption={(input, option) =>
@@ -447,7 +449,7 @@ export const AiAssistantTab = () => {
             title={
               <Space>
                 <ControlOutlined />
-                生成参数
+                {t('settings.generationParameters')}
               </Space>
             }
           >
@@ -481,10 +483,10 @@ export const AiAssistantTab = () => {
               disabled={loading}
               onClick={() => void handleSave()}
             >
-              保存
+              {t('common.save')}
             </Button>
             <Button loading={testing} disabled={loading} onClick={() => void 
handleTest()}>
-              测试连接
+              {t('settings.testConnection')}
             </Button>
           </Space>
         </Flex>
diff --git a/web/src/pages/settings/CloudCredentialTab.tsx 
b/web/src/pages/settings/CloudCredentialTab.tsx
index 5846636bd..077e08355 100644
--- a/web/src/pages/settings/CloudCredentialTab.tsx
+++ b/web/src/pages/settings/CloudCredentialTab.tsx
@@ -33,6 +33,7 @@ import {
 import { DeleteOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons';
 import type { ColumnsType } from 'antd/es/table';
 import { MagnifyingGlass } from '@phosphor-icons/react';
+import { useLang } from '../../i18n/LangContext';
 
 import {
   createCloudCredential,
@@ -43,21 +44,11 @@ import {
 import type { CloudCredential } from '../../api/cloudCredential';
 import type { InstanceVendor } from '../../api/instance';
 
-const vendorLabel: Record<string, string> = {
-  ALIYUN: '阿里云',
-  TENCENT: '腾讯云',
-};
-
 const vendorTagColor: Record<string, string> = {
   ALIYUN: 'orange',
   TENCENT: 'blue',
 };
 
-const VENDOR_OPTIONS = [
-  { value: 'ALIYUN', label: '阿里云' },
-  { value: 'TENCENT', label: '腾讯云' },
-];
-
 const PAGE_SIZE_OPTIONS = [20, 50, 100];
 
 interface CredentialFormValues {
@@ -69,6 +60,7 @@ interface CredentialFormValues {
 }
 
 export const CloudCredentialTab = () => {
+  const { t } = useLang();
   const [credentials, setCredentials] = useState<CloudCredential[]>([]);
   const [total, setTotal] = useState(0);
   const [page, setPage] = useState(1);
@@ -110,7 +102,7 @@ export const CloudCredentialTab = () => {
         setTotal(result.total);
       } catch {
         if (requestId === requestSeqRef.current) {
-          message.error('云凭据加载失败,请稍后重试');
+          message.error(t('settings.credentialLoadFailed'));
         }
       } finally {
         if (requestId === requestSeqRef.current) {
@@ -118,7 +110,7 @@ export const CloudCredentialTab = () => {
         }
       }
     })();
-  }, [debouncedSearch, page, pageSize, vendorFilter]);
+  }, [debouncedSearch, page, pageSize, t, vendorFilter]);
 
   useEffect(() => {
     void loadCredentials();
@@ -175,7 +167,7 @@ export const CloudCredentialTab = () => {
           remark: values.remark,
         });
         setCredentials((previous) => previous.map((item) => (item.id === 
saved.id ? saved : item)));
-        message.success('云凭据已更新');
+        message.success(t('settings.credentialUpdated'));
       } else {
         await createCloudCredential({
           name: values.name,
@@ -185,7 +177,7 @@ export const CloudCredentialTab = () => {
           remark: values.remark,
         });
         setPage(1);
-        message.success('云凭据已添加');
+        message.success(t('settings.credentialAdded'));
       }
       await loadCredentials();
       closeModal();
@@ -193,7 +185,7 @@ export const CloudCredentialTab = () => {
       if (error && typeof error === 'object' && 'errorFields' in error) {
         return; // validation failure; antd already shows field-level errors
       }
-      message.error('保存云凭据失败,请稍后重试');
+      message.error(t('settings.credentialSaveFailed'));
     } finally {
       setSubmitting(false);
     }
@@ -208,27 +200,33 @@ export const CloudCredentialTab = () => {
       } else {
         await loadCredentials();
       }
-      message.success('云凭据已删除');
+      message.success(t('settings.credentialDeleted'));
     } catch {
-      message.error('删除云凭据失败(可能仍被实例引用),请稍后重试');
+      message.error(t('settings.credentialDeleteFailed'));
     }
   };
 
   const columns: ColumnsType<CloudCredential> = [
-    { title: '名称', dataIndex: 'name', key: 'name' },
+    { title: t('common.name'), dataIndex: 'name', key: 'name' },
     {
-      title: '云厂商',
+      title: t('settings.cloudVendor'),
       dataIndex: 'vendor',
       key: 'vendor',
       render: (vendor: string) => (
-        <Tag color={vendorTagColor[vendor]}>{vendorLabel[vendor] ?? 
vendor}</Tag>
+        <Tag color={vendorTagColor[vendor]}>
+          {vendor === 'ALIYUN'
+            ? t('settings.aliyun')
+            : vendor === 'TENCENT'
+              ? t('settings.tencent')
+              : vendor}
+        </Tag>
       ),
     },
     { title: 'AccessKey', dataIndex: 'accessKey', key: 'accessKey' },
-    { title: '备注', dataIndex: 'remark', key: 'remark' },
-    { title: '创建时间', dataIndex: 'gmtCreate', key: 'gmtCreate' },
+    { title: t('settings.remark'), dataIndex: 'remark', key: 'remark' },
+    { title: t('settings.createdAt'), dataIndex: 'gmtCreate', key: 'gmtCreate' 
},
     {
-      title: '操作',
+      title: t('common.actions'),
       key: 'action',
       render: (_: unknown, record: CloudCredential) => (
         <Space size="small">
@@ -238,16 +236,16 @@ export const CloudCredentialTab = () => {
             icon={<EditOutlined />}
             onClick={() => openEditModal(record)}
           >
-            编辑
+            {t('common.edit')}
           </Button>
           <Popconfirm
-            title="确定要删除该云凭据吗?"
+            title={t('settings.deleteCredentialConfirm')}
             onConfirm={() => void handleDelete(record)}
-            okText="确定"
-            cancelText="取消"
+            okText={t('settings.confirmAction')}
+            cancelText={t('common.cancel')}
           >
             <Button type="link" size="small" danger icon={<DeleteOutlined />}>
-              删除
+              {t('common.delete')}
             </Button>
           </Popconfirm>
         </Space>
@@ -262,22 +260,25 @@ export const CloudCredentialTab = () => {
           <Input
             allowClear
             prefix={<MagnifyingGlass size={14} color="#9CA3AF" />}
-            placeholder="搜索凭据名称"
+            placeholder={t('settings.searchCredentials')}
             style={{ width: 240 }}
             value={search}
             onChange={(event) => changeSearch(event.target.value)}
           />
           <Select<InstanceVendor>
             allowClear
-            placeholder="全部云厂商"
+            placeholder={t('settings.allCloudVendors')}
             style={{ width: 160 }}
             value={vendorFilter}
             onChange={changeVendorFilter}
-            options={VENDOR_OPTIONS}
+            options={[
+              { value: 'ALIYUN', label: t('settings.aliyun') },
+              { value: 'TENCENT', label: t('settings.tencent') },
+            ]}
           />
         </Flex>
         <Button type="primary" icon={<PlusOutlined />} 
onClick={openCreateModal} disabled={loading}>
-          添加云凭据
+          {t('settings.addCredential')}
         </Button>
       </Flex>
 
@@ -292,7 +293,7 @@ export const CloudCredentialTab = () => {
           total,
           showSizeChanger: true,
           pageSizeOptions: PAGE_SIZE_OPTIONS.map(String),
-          showTotal: (count) => `共 ${count} 条`,
+          showTotal: (count) => t('settings.totalRecords', { total: count }),
           onChange: (nextPage, nextPageSize) => {
             if (nextPageSize !== pageSize) {
               setPage(1);
@@ -306,7 +307,7 @@ export const CloudCredentialTab = () => {
       />
 
       <Modal
-        title={editingCredential ? '编辑云凭据' : '添加云凭据'}
+        title={t(editingCredential ? 'settings.editCredential' : 
'settings.addCredential')}
         open={modalOpen}
         onCancel={closeModal}
         onOk={() => void handleSubmit()}
@@ -321,8 +322,13 @@ export const CloudCredentialTab = () => {
             items={[
               {
                 key: 'vendor',
-                label: '云厂商',
-                children: vendorLabel[editingCredential.vendor] ?? 
editingCredential.vendor,
+                label: t('settings.cloudVendor'),
+                children:
+                  editingCredential.vendor === 'ALIYUN'
+                    ? t('settings.aliyun')
+                    : editingCredential.vendor === 'TENCENT'
+                      ? t('settings.tencent')
+                      : editingCredential.vendor,
               },
               { key: 'accessKey', label: 'AccessKey', children: 
editingCredential.accessKey },
             ]}
@@ -330,27 +336,34 @@ export const CloudCredentialTab = () => {
         )}
         <Form form={form} layout="vertical" preserve={false}>
           <Form.Item
-            label="名称"
+            label={t('common.name')}
             name="name"
-            rules={[{ required: true, message: '请输入凭据名称' }]}
+            rules={[{ required: true, message: 
t('settings.credentialNameRequired') }]}
           >
-            <Input placeholder="例如:阿里云测试账号" />
+            <Input placeholder={t('settings.credentialNameExample')} />
           </Form.Item>
 
           {!editingCredential && (
             <>
               <Form.Item
-                label="云厂商"
+                label={t('settings.cloudVendor')}
                 name="vendor"
-                rules={[{ required: true, message: '请选择云厂商' }]}
+                rules={[{ required: true, message: 
t('settings.selectCloudVendor') }]}
               >
-                <Select placeholder="请选择" virtual={false} 
options={VENDOR_OPTIONS} />
+                <Select
+                  placeholder={t('settings.selectCloudVendor')}
+                  virtual={false}
+                  options={[
+                    { value: 'ALIYUN', label: t('settings.aliyun') },
+                    { value: 'TENCENT', label: t('settings.tencent') },
+                  ]}
+                />
               </Form.Item>
 
               <Form.Item
                 label="AccessKey"
                 name="accessKey"
-                rules={[{ required: true, message: '请输入 AccessKey' }]}
+                rules={[{ required: true, message: 
t('settings.accessKeyRequired') }]}
               >
                 <Input autoComplete="off" placeholder="LTAI..." />
               </Form.Item>
@@ -360,14 +373,18 @@ export const CloudCredentialTab = () => {
           <Form.Item
             label="SecretKey"
             name="secretKey"
-            rules={editingCredential ? [] : [{ required: true, message: '请输入 
SecretKey' }]}
-            extra={editingCredential ? '留空表示保持原 SecretKey 不变' : undefined}
+            rules={
+              editingCredential
+                ? []
+                : [{ required: true, message: t('settings.secretKeyRequired') 
}]
+            }
+            extra={editingCredential ? t('settings.keepSecretKey') : undefined}
           >
-            <Input.Password autoComplete="off" placeholder="请输入 SecretKey" />
+            <Input.Password autoComplete="off" 
placeholder={t('settings.enterSecretKey')} />
           </Form.Item>
 
-          <Form.Item label="备注" name="remark">
-            <Input.TextArea rows={2} placeholder="可选" />
+          <Form.Item label={t('settings.remark')} name="remark">
+            <Input.TextArea rows={2} placeholder={t('settings.optional')} />
           </Form.Item>
         </Form>
       </Modal>
diff --git a/web/src/pages/settings/DataSourceTab.tsx 
b/web/src/pages/settings/DataSourceTab.tsx
index ccd2d5306..d0e7729ed 100644
--- a/web/src/pages/settings/DataSourceTab.tsx
+++ b/web/src/pages/settings/DataSourceTab.tsx
@@ -142,7 +142,7 @@ export const DataSourceTab = () => {
         setTotal(result.total);
       } catch {
         if (requestId === requestSeqRef.current) {
-          message.error('数据源加载失败,请稍后重试');
+          message.error(t('settings.dataSourceLoadFailed'));
         }
       } finally {
         if (requestId === requestSeqRef.current) {
@@ -150,7 +150,7 @@ export const DataSourceTab = () => {
         }
       }
     })();
-  }, [debouncedSearch, page, pageSize, typeFilter]);
+  }, [debouncedSearch, page, pageSize, t, typeFilter]);
 
   useEffect(() => {
     void loadDataSources();
@@ -182,7 +182,7 @@ export const DataSourceTab = () => {
     key: string,
   ) => {
     if (key !== 'modal' && authNeedsSecret(data.auth)) {
-      message.warning('认证数据源请编辑后输入凭据再测试连接');
+      message.warning(t('settings.dataSourceAuthTestHint'));
       return;
     }
     setTestingKeys((previous) => new Set(previous).add(key));
@@ -191,7 +191,7 @@ export const DataSourceTab = () => {
       if (result.success) message.success(result.message);
       else message.error(result.message);
     } catch {
-      message.error('连接测试失败,请稍后重试');
+      message.error(t('settings.connectionTestFailed'));
     } finally {
       setTestingKeys((previous) => {
         const next = new Set(previous);
@@ -230,14 +230,16 @@ export const DataSourceTab = () => {
         setPage(1);
       }
       await loadDataSources();
-      message.success(editingDataSource ? '数据源已更新' : '数据源已添加');
+      message.success(
+        t(editingDataSource ? 'settings.dataSourceUpdated' : 
'settings.dataSourceAdded'),
+      );
       setModalOpen(false);
       dsForm.resetFields();
     } catch (error) {
       if (error && typeof error === 'object' && 'errorFields' in error) {
         return; // validation failure; antd already shows field-level errors
       }
-      message.error('保存数据源失败,请稍后重试');
+      message.error(t('settings.dataSourceSaveFailed'));
     } finally {
       setSubmitting(false);
     }
@@ -251,27 +253,27 @@ export const DataSourceTab = () => {
       } else {
         await loadDataSources();
       }
-      message.success('数据源已删除');
+      message.success(t('settings.dataSourceDeleted'));
     } catch {
-      message.error('删除数据源失败,请稍后重试');
+      message.error(t('settings.dataSourceDeleteFailed'));
     }
   };
 
   const columns: ColumnsType<DataSource> = [
-    { title: '名称', dataIndex: 'name', key: 'name' },
+    { title: t('common.name'), dataIndex: 'name', key: 'name' },
     {
-      title: '类型',
+      title: t('common.type'),
       dataIndex: 'type',
       key: 'type',
       render: (t: string) => <Tag color={typeTagColor[t]}>{t}</Tag>,
     },
     { title: 'URL', dataIndex: 'url', key: 'url' },
     {
-      title: '适用实例',
+      title: t('settings.instances'),
       dataIndex: 'instanceIds',
       key: 'instanceIds',
       render: (instanceIds: string[] | undefined) => {
-        if (!instanceIds?.length) return '全局';
+        if (!instanceIds?.length) return t('settings.global');
         return instanceIds
           .map(
             (instanceId) =>
@@ -282,9 +284,9 @@ export const DataSourceTab = () => {
           .join('、');
       },
     },
-    { title: '认证方式', dataIndex: 'auth', key: 'auth' },
+    { title: t('settings.authentication'), dataIndex: 'auth', key: 'auth' },
     {
-      title: '状态',
+      title: t('common.status'),
       dataIndex: 'status',
       key: 'status',
       render: (status: DataSource['status']) =>
@@ -295,7 +297,7 @@ export const DataSourceTab = () => {
         ),
     },
     {
-      title: '操作',
+      title: t('common.actions'),
       key: 'action',
       render: (_: unknown, record: DataSource) => (
         <Space size="small">
@@ -305,12 +307,10 @@ export const DataSourceTab = () => {
             icon={<ApiOutlined />}
             loading={testingKeys.has(record.key)}
             disabled={authNeedsSecret(record.auth)}
-            title={
-              authNeedsSecret(record.auth) ? '认证数据源请编辑后输入凭据再测试连接' : undefined
-            }
+            title={authNeedsSecret(record.auth) ? 
t('settings.dataSourceAuthTestHint') : undefined}
             onClick={() => void handleTestConnection(record, record.key)}
           >
-            测试连接
+            {t('settings.testConnection')}
           </Button>
           <Button
             type="link"
@@ -318,16 +318,16 @@ export const DataSourceTab = () => {
             icon={<EditOutlined />}
             onClick={() => openEditModal(record)}
           >
-            编辑
+            {t('common.edit')}
           </Button>
           <Popconfirm
-            title="确定要删除该数据源吗?"
+            title={t('settings.deleteDataSourceConfirm')}
             onConfirm={() => void handleDelete(record)}
-            okText="确定"
-            cancelText="取消"
+            okText={t('settings.confirmAction')}
+            cancelText={t('common.cancel')}
           >
             <Button type="link" size="small" danger icon={<DeleteOutlined />}>
-              删除
+              {t('common.delete')}
             </Button>
           </Popconfirm>
         </Space>
@@ -342,7 +342,7 @@ export const DataSourceTab = () => {
           <Input
             allowClear
             prefix={<MagnifyingGlass size={14} color="#9CA3AF" />}
-            placeholder="搜索数据源名称"
+            placeholder={t('settings.searchDataSources')}
             style={{ width: 240 }}
             value={search}
             onChange={(event) => {
@@ -352,7 +352,7 @@ export const DataSourceTab = () => {
           />
           <Select
             allowClear
-            placeholder="全部类型"
+            placeholder={t('settings.allTypes')}
             style={{ width: 160 }}
             value={typeFilter}
             onChange={(value) => {
@@ -363,7 +363,7 @@ export const DataSourceTab = () => {
           />
         </Flex>
         <Button type="primary" icon={<PlusOutlined />} 
onClick={openCreateModal} disabled={loading}>
-          添加数据源
+          {t('settings.addDataSource')}
         </Button>
       </Flex>
 
@@ -378,7 +378,7 @@ export const DataSourceTab = () => {
           total,
           showSizeChanger: true,
           pageSizeOptions: PAGE_SIZE_OPTIONS.map(String),
-          showTotal: (count) => `共 ${count} 条`,
+          showTotal: (count) => t('settings.totalRecords', { total: count }),
           onChange: (nextPage, nextPageSize) => {
             if (nextPageSize !== pageSize) {
               setPage(1);
@@ -392,7 +392,7 @@ export const DataSourceTab = () => {
       />
 
       <Modal
-        title={editingDataSource ? '编辑数据源' : '添加数据源'}
+        title={t(editingDataSource ? 'settings.editDataSource' : 
'settings.addDataSource')}
         open={modalOpen}
         onCancel={() => {
           setModalOpen(false);
@@ -405,34 +405,42 @@ export const DataSourceTab = () => {
       >
         <Form form={dsForm} layout="vertical" preserve={false}>
           <Form.Item
-            label="名称"
+            label={t('common.name')}
             name="name"
-            rules={[{ required: true, message: '请输入数据源名称' }]}
+            rules={[{ required: true, message: 
t('settings.dataSourceNameRequired') }]}
           >
-            <Input placeholder="例如:Prometheus 生产监控" />
+            <Input placeholder={t('settings.dataSourceNameExample')} />
           </Form.Item>
 
           <Form.Item
-            label="类型"
+            label={t('common.type')}
             name="type"
-            rules={[{ required: true, message: '请选择数据源类型' }]}
+            rules={[{ required: true, message: t('settings.selectType') }]}
           >
-            <Select placeholder="请选择" virtual={false} 
options={DATA_SOURCE_TYPE_OPTIONS} />
+            <Select
+              placeholder={t('settings.selectType')}
+              virtual={false}
+              options={DATA_SOURCE_TYPE_OPTIONS}
+            />
           </Form.Item>
 
           <Form.Item
             label="URL"
             name="url"
-            rules={[{ required: true, message: '请输入数据源 URL' }]}
+            rules={[{ required: true, message: 
t('settings.dataSourceUrlRequired') }]}
           >
             <Input placeholder="http://localhost:9090"; />
           </Form.Item>
 
-          <Form.Item label="适用实例" name="instanceIds" extra="留空时可用于所有实例">
+          <Form.Item
+            label={t('settings.instances')}
+            name="instanceIds"
+            extra={t('settings.instancesHelp')}
+          >
             <Select
               mode="multiple"
               allowClear
-              placeholder="选择此数据源对应的实例"
+              placeholder={t('settings.selectDataSourceInstances')}
               options={instances.map((instance) => ({
                 value: instance.name,
                 label: instance.name,
@@ -440,7 +448,7 @@ export const DataSourceTab = () => {
             />
           </Form.Item>
 
-          <Form.Item label="认证方式" name="auth" initialValue="None">
+          <Form.Item label={t('settings.authentication')} name="auth" 
initialValue="None">
             <Select
               virtual={false}
               onChange={() => {
@@ -461,18 +469,21 @@ export const DataSourceTab = () => {
           {authValue === 'Basic Auth' && (
             <>
               <Form.Item
-                label="用户名"
+                label={t('settings.username')}
                 name="username"
-                rules={[{ required: true, message: '请输入用户名' }]}
+                rules={[{ required: true, message: 
t('settings.usernameRequired') }]}
               >
                 <Input autoComplete="username" placeholder="prometheus" />
               </Form.Item>
               <Form.Item
-                label="密码"
+                label={t('settings.password')}
                 name="password"
-                rules={[{ required: true, message: '请输入密码' }]}
+                rules={[{ required: true, message: 
t('settings.passwordRequired') }]}
               >
-                <Input.Password autoComplete="current-password" 
placeholder="请输入密码" />
+                <Input.Password
+                  autoComplete="current-password"
+                  placeholder={t('settings.passwordRequired')}
+                />
               </Form.Item>
             </>
           )}
@@ -481,9 +492,9 @@ export const DataSourceTab = () => {
             <Form.Item
               label="Bearer Token"
               name="bearerToken"
-              rules={[{ required: true, message: '请输入 Bearer Token' }]}
+              rules={[{ required: true, message: 
t('settings.bearerTokenRequired') }]}
             >
-              <Input.Password autoComplete="off" placeholder="请输入 Token" />
+              <Input.Password autoComplete="off" 
placeholder={t('settings.enterToken')} />
             </Form.Item>
           )}
 
@@ -498,7 +509,7 @@ export const DataSourceTab = () => {
             }}
             style={{ marginTop: 8 }}
           >
-            测试连接
+            {t('settings.testConnection')}
           </Button>
         </Form>
       </Modal>
diff --git a/web/src/pages/settings/GeneralSettingsTab.tsx 
b/web/src/pages/settings/GeneralSettingsTab.tsx
index 445ef5b0f..67ad953e4 100644
--- a/web/src/pages/settings/GeneralSettingsTab.tsx
+++ b/web/src/pages/settings/GeneralSettingsTab.tsx
@@ -34,15 +34,10 @@ import { getGeneralSettings, saveGeneralSettings } from 
'../../api/settings';
 import type { GeneralSettings, GeneralSettingsUpdate } from 
'../../api/settings';
 import { useTheme } from '../../theme/useTheme';
 import type { ThemeMode } from '../../theme/themePreference';
+import { useLang } from '../../i18n/LangContext';
 
 const { Text } = Typography;
 
-const THEME_OPTIONS = [
-  { value: 'light', label: '浅色' },
-  { value: 'dark', label: '深色' },
-  { value: 'system', label: '跟随系统' },
-];
-
 const toThemeMode = (value?: string): ThemeMode =>
   value === 'light' || value === 'dark' ? value : 'system';
 
@@ -62,6 +57,7 @@ const buildPayload = (settings: GeneralSettings): 
GeneralSettingsUpdate => ({
 });
 
 export const GeneralSettingsTab = () => {
+  const { t } = useLang();
   const { message } = App.useApp();
   const { setThemeMode, setCompact } = useTheme();
   const [settings, setSettings] = useState<GeneralSettings | null>(null);
@@ -88,7 +84,7 @@ export const GeneralSettingsTab = () => {
         });
       })
       .catch(() => {
-        if (!cancelled) message.error('通用设置加载失败,请稍后重试');
+        if (!cancelled) message.error(t('settings.loadFailed'));
       })
       .finally(() => {
         if (!cancelled) setLoading(false);
@@ -97,8 +93,7 @@ export const GeneralSettingsTab = () => {
     return () => {
       cancelled = true;
     };
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, []);
+  }, [message, notifyForm, securityForm, t]);
 
   const persistPreference = async (patch: Partial<GeneralSettings>) => {
     if (!settings) return;
@@ -107,9 +102,9 @@ export const GeneralSettingsTab = () => {
     setSavingPreference(true);
     try {
       await saveGeneralSettings(buildPayload(next));
-      message.success('设置已保存');
+      message.success(t('settings.saveSuccess'));
     } catch {
-      message.error('设置保存失败,请稍后重试');
+      message.error(t('settings.saveFailed'));
     } finally {
       setSavingPreference(false);
     }
@@ -122,7 +117,7 @@ export const GeneralSettingsTab = () => {
       setSettings({ ...settings, ...patch });
       return true;
     } catch {
-      message.error('设置保存失败,请稍后重试');
+      message.error(t('settings.saveFailed'));
       return false;
     }
   };
@@ -133,7 +128,7 @@ export const GeneralSettingsTab = () => {
     setSavingSecurity(true);
     try {
       if (await mergeAndSave({ sessionTimeout: values.sessionTimeout })) {
-        message.success('设置已保存');
+        message.success(t('settings.saveSuccess'));
       }
     } finally {
       securityInFlightRef.current = false;
@@ -151,7 +146,7 @@ export const GeneralSettingsTab = () => {
     setSavingNotification(true);
     try {
       if (await mergeAndSave(values)) {
-        message.success('设置已保存');
+        message.success(t('settings.saveSuccess'));
       }
     } finally {
       notifyInFlightRef.current = false;
@@ -165,7 +160,7 @@ export const GeneralSettingsTab = () => {
         title={
           <Space>
             <SkinOutlined />
-            界面偏好
+            {t('settings.appearance')}
           </Space>
         }
         loading={loading}
@@ -173,14 +168,18 @@ export const GeneralSettingsTab = () => {
         <Flex vertical gap={20}>
           <Flex justify="space-between" align="center" gap={16}>
             <div>
-              <div>主题模式</div>
+              <div>{t('settings.themeMode')}</div>
               <Text type="secondary" style={{ fontSize: 14 }}>
-                作用于前端展示,跟随系统时随操作系统外观变化
+                {t('settings.themeHelp')}
               </Text>
             </div>
             <Segmented
               value={toThemeMode(settings?.theme)}
-              options={THEME_OPTIONS}
+              options={[
+                { value: 'light', label: t('settings.lightTheme') },
+                { value: 'dark', label: t('settings.darkTheme') },
+                { value: 'system', label: t('settings.systemTheme') },
+              ]}
               disabled={savingPreference}
               onChange={(value) => {
                 const mode = value as ThemeMode;
@@ -191,9 +190,9 @@ export const GeneralSettingsTab = () => {
           </Flex>
           <Flex justify="space-between" align="center" gap={16}>
             <div>
-              <div>紧凑模式</div>
+              <div>{t('settings.compactMode')}</div>
               <Text type="secondary" style={{ fontSize: 14 }}>
-                减小组件间距,单屏展示更多内容
+                {t('settings.compactHelp')}
               </Text>
             </div>
             <Switch
@@ -212,37 +211,42 @@ export const GeneralSettingsTab = () => {
         title={
           <Space>
             <SafetyOutlined />
-            安全与会话
+            {t('settings.securitySession')}
           </Space>
         }
         loading={loading}
       >
         <Form form={securityForm} layout="vertical" 
onFinish={handleSecurityFinish}>
           <Form.Item
-            label="会话超时"
-            extra="应用于新创建的会话,已登录用户保持原到期时间"
+            label={t('settings.sessionTimeout')}
+            extra={t('settings.sessionTimeoutHelp')}
             style={{ marginBottom: 16 }}
           >
             <Space.Compact>
               <Form.Item
                 name="sessionTimeout"
                 noStyle
-                rules={[{ required: true, message: '请输入会话超时时长' }]}
+                rules={[{ required: true, message: 
t('settings.sessionTimeoutRequired') }]}
               >
                 <InputNumber min={5} max={1440} style={{ width: 120 }} />
               </Form.Item>
-              <Input aria-label="会话超时单位" readOnly value="分钟" style={{ width: 
64 }} />
+              <Input
+                aria-label={t('settings.sessionTimeoutUnit')}
+                readOnly
+                value={t('settings.minutes')}
+                style={{ width: 64 }}
+              />
             </Space.Compact>
           </Form.Item>
 
           <Form.Item style={{ marginBottom: 16 }}>
             <Button type="primary" htmlType="submit" loading={savingSecurity} 
disabled={loading}>
-              保存设置
+              {t('settings.saveSettings')}
             </Button>
           </Form.Item>
 
           <Text type="secondary" style={{ fontSize: 14 }}>
-            登录保护由服务端 STUDIO_AUTH_LOGIN_REQUIRED 配置决定,修改后重启服务生效。
+            {t('settings.loginProtectionHelp')}
           </Text>
         </Form>
       </Card>
@@ -251,32 +255,32 @@ export const GeneralSettingsTab = () => {
         title={
           <Space>
             <BellOutlined />
-            通知设置
+            {t('settings.notification')}
           </Space>
         }
         loading={loading}
       >
         <Form form={notifyForm} layout="vertical" 
onFinish={handleNotifyFinish}>
           <Form.Item
-            label="钉钉机器人 Webhook"
+            label={t('settings.dingtalkWebhook')}
             name="dingtalkWebhook"
-            extra="告警规则选择钉钉渠道时推送到该机器人"
+            extra={t('settings.dingtalkWebhookHelp')}
           >
             <Input 
placeholder="https://oapi.dingtalk.com/robot/send?access_token=..."; />
           </Form.Item>
 
           <Form.Item
-            label="邮件收件人"
+            label={t('settings.emailRecipients')}
             name="emailRecipients"
-            extra="多个地址用英文逗号分隔;邮件发送通道后续接入"
+            extra={t('settings.emailRecipientsHelp')}
           >
             <Input.TextArea rows={2} placeholder="[email protected], 
[email protected]" />
           </Form.Item>
 
           <Form.Item
-            label="短信网关 Webhook"
+            label={t('settings.smsWebhook')}
             name="smsWebhook"
-            extra="占位配置,短信发送通道后续接入"
+            extra={t('settings.smsWebhookHelp')}
           >
             <Input placeholder="https://sms-gateway.example.com/notify"; />
           </Form.Item>
@@ -288,7 +292,7 @@ export const GeneralSettingsTab = () => {
               loading={savingNotification}
               disabled={loading}
             >
-              保存设置
+              {t('settings.saveSettings')}
             </Button>
           </Form.Item>
         </Form>
diff --git a/web/src/pages/settings/__tests__/AiAssistantTab.test.tsx 
b/web/src/pages/settings/__tests__/AiAssistantTab.test.tsx
index bf2050050..7bb6aad54 100644
--- a/web/src/pages/settings/__tests__/AiAssistantTab.test.tsx
+++ b/web/src/pages/settings/__tests__/AiAssistantTab.test.tsx
@@ -20,6 +20,7 @@ import { act, render, screen, waitFor } from 
'@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
 import { LangProvider } from '../../../i18n/LangContext';
+import { LANGUAGE_STORAGE_KEY } from '../../../i18n/languagePreference';
 import { AiAssistantTab } from '../AiAssistantTab';
 
 const llmApiMocks = vi.hoisted(() => ({
@@ -59,6 +60,7 @@ const renderPage = () =>
 describe('AiAssistantTab', () => {
   beforeEach(() => {
     vi.clearAllMocks();
+    localStorage.removeItem(LANGUAGE_STORAGE_KEY);
     llmApiMocks.getLlmConfig.mockResolvedValue({
       provider: 'tongyi',
       apiBase: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
@@ -83,6 +85,14 @@ describe('AiAssistantTab', () => {
     expect(screen.getByDisplayValue('qwen3.8-max')).toBeInTheDocument();
   });
 
+  it('renders assistant configuration labels in English when English is 
selected', async () => {
+    localStorage.setItem(LANGUAGE_STORAGE_KEY, 'en');
+    renderPage();
+
+    expect(await screen.findByText('Execution engine and 
model')).toBeInTheDocument();
+    expect(screen.getByRole('button', { name: 'Test connection' 
})).toBeInTheDocument();
+  });
+
   it('saves without sending an apiKey when the input stays empty', async () => 
{
     const user = userEvent.setup();
     renderPage();
diff --git a/web/src/pages/settings/__tests__/CloudCredentialTab.test.tsx 
b/web/src/pages/settings/__tests__/CloudCredentialTab.test.tsx
index bcb4dde62..f205e049d 100644
--- a/web/src/pages/settings/__tests__/CloudCredentialTab.test.tsx
+++ b/web/src/pages/settings/__tests__/CloudCredentialTab.test.tsx
@@ -27,6 +27,7 @@ import {
   updateCloudCredential,
 } from '../../../api/cloudCredential';
 import { LangProvider } from '../../../i18n/LangContext';
+import { LANGUAGE_STORAGE_KEY } from '../../../i18n/languagePreference';
 import { CloudCredentialTab } from '../CloudCredentialTab';
 
 vi.mock('../../../api/cloudCredential', () => ({
@@ -88,6 +89,7 @@ beforeAll(() => {
 describe('CloudCredentialTab', () => {
   beforeEach(() => {
     vi.clearAllMocks();
+    localStorage.removeItem(LANGUAGE_STORAGE_KEY);
     vi.mocked(listCloudCredentials).mockResolvedValue(credentials);
   });
 
@@ -105,6 +107,13 @@ describe('CloudCredentialTab', () => {
     await waitFor(() => 
expect(listCloudCredentials).toHaveBeenCalledWith(undefined, '', 1, 20));
   });
 
+  it('renders management controls in English when English is selected', async 
() => {
+    localStorage.setItem(LANGUAGE_STORAGE_KEY, 'en');
+    renderTab();
+
+    expect(await screen.findByPlaceholderText('Search credential 
names')).toBeInTheDocument();
+  });
+
   it('resets to the first page and queries filters after they change', async 
() => {
     const user = userEvent.setup({ pointerEventsCheck: 0 });
     renderTab();
diff --git a/web/src/pages/settings/__tests__/DataSourceTab.test.tsx 
b/web/src/pages/settings/__tests__/DataSourceTab.test.tsx
index a050acf80..b7d886e73 100644
--- a/web/src/pages/settings/__tests__/DataSourceTab.test.tsx
+++ b/web/src/pages/settings/__tests__/DataSourceTab.test.tsx
@@ -22,6 +22,7 @@ import { App } from 'antd';
 import type { DataSource, DataSourcePage } from '../../../api/settings';
 import { createDataSource, listDataSourcesPage, testDataSource } from 
'../../../api/settings';
 import { LangProvider } from '../../../i18n/LangContext';
+import { LANGUAGE_STORAGE_KEY } from '../../../i18n/languagePreference';
 import { DataSourceTab } from '../DataSourceTab';
 
 vi.mock('../../../api/settings', () => ({
@@ -88,6 +89,7 @@ beforeAll(() => {
 describe('DataSourceTab', () => {
   beforeEach(() => {
     vi.clearAllMocks();
+    localStorage.removeItem(LANGUAGE_STORAGE_KEY);
     vi.mocked(listDataSourcesPage).mockResolvedValue(sourcePage);
   });
 
@@ -128,6 +130,19 @@ describe('DataSourceTab', () => {
     expect(screen.queryByText('离线')).not.toBeInTheDocument();
   });
 
+  it('renders management controls in English when English is selected', async 
() => {
+    localStorage.setItem(LANGUAGE_STORAGE_KEY, 'en');
+    render(
+      <LangProvider>
+        <App>
+          <DataSourceTab />
+        </App>
+      </LangProvider>,
+    );
+
+    expect(await screen.findByPlaceholderText('Search data source 
names')).toBeInTheDocument();
+  });
+
   it('shows connection test loading only on the clicked row', async () => {
     let resolveTest: (value: { success: boolean; message: string }) => void = 
() => undefined;
     vi.mocked(testDataSource).mockReturnValue(
diff --git a/web/src/pages/settings/__tests__/GeneralSettingsTab.test.tsx 
b/web/src/pages/settings/__tests__/GeneralSettingsTab.test.tsx
index ba0f78b0c..f7ee93cfe 100644
--- a/web/src/pages/settings/__tests__/GeneralSettingsTab.test.tsx
+++ b/web/src/pages/settings/__tests__/GeneralSettingsTab.test.tsx
@@ -21,6 +21,8 @@ import userEvent from '@testing-library/user-event';
 import { App } from 'antd';
 import { getGeneralSettings, saveGeneralSettings } from 
'../../../api/settings';
 import { ThemeProvider } from '../../../theme/ThemeProvider';
+import { LangProvider } from '../../../i18n/LangContext';
+import { LANGUAGE_STORAGE_KEY } from '../../../i18n/languagePreference';
 import { GeneralSettingsTab } from '../GeneralSettingsTab';
 
 beforeAll(() => {
@@ -52,9 +54,11 @@ vi.mock('../../../api/settings', () => ({
 const renderTab = () =>
   render(
     <App>
-      <ThemeProvider>
-        <GeneralSettingsTab />
-      </ThemeProvider>
+      <LangProvider>
+        <ThemeProvider>
+          <GeneralSettingsTab />
+        </ThemeProvider>
+      </LangProvider>
     </App>,
   );
 
@@ -123,6 +127,14 @@ describe('GeneralSettingsTab', () => {
     );
   });
 
+  it('renders section labels in English when English is selected', async () => 
{
+    localStorage.setItem(LANGUAGE_STORAGE_KEY, 'en');
+    renderTab();
+
+    expect(await screen.findByText('Appearance 
preferences')).toBeInTheDocument();
+    expect(screen.getByText('Session timeout')).toBeInTheDocument();
+  });
+
   it('saves immediately when the theme mode preference changes', async () => {
     vi.mocked(saveGeneralSettings).mockResolvedValue();
     const user = userEvent.setup();
diff --git a/web/src/pages/settings/index.tsx b/web/src/pages/settings/index.tsx
index 5400e098c..22c138ea6 100644
--- a/web/src/pages/settings/index.tsx
+++ b/web/src/pages/settings/index.tsx
@@ -39,17 +39,21 @@ const SettingsPage = () => {
 
   return (
     <div style={{ padding: 24 }}>
-      <PageHeader title={t('settings.title')} subtitle="管理应用配置与数据源" />
+      <PageHeader title={t('settings.title')} 
subtitle={t('settings.subtitle')} />
 
       <Tabs
         activeKey={activeKey}
         onChange={(key) => setSearchParams({ tab: key })}
         items={[
-          { key: 'general', label: '通用设置', children: <GeneralSettingsTab /> },
-          { key: 'ai', label: 'AI 助手', children: <AiAssistantTab /> },
-          { key: 'credential', label: '云凭据管理', children: <CloudCredentialTab 
/> },
-          { key: 'datasource', label: '数据源管理', children: <DataSourceTab /> },
-          { key: 'about', label: '关于', children: <AboutTab /> },
+          { key: 'general', label: t('settings.tabGeneral'), children: 
<GeneralSettingsTab /> },
+          { key: 'ai', label: t('settings.tabAi'), children: <AiAssistantTab 
/> },
+          {
+            key: 'credential',
+            label: t('settings.tabCredential'),
+            children: <CloudCredentialTab />,
+          },
+          { key: 'datasource', label: t('settings.tabDatasource'), children: 
<DataSourceTab /> },
+          { key: 'about', label: t('settings.tabAbout'), children: <AboutTab 
/> },
         ]}
       />
     </div>

Reply via email to