From 0dbb85f5b81ea5dc51a3340ed79d0c3c5f60cf9e Mon Sep 17 00:00:00 2001 From: attacker Date: Sun, 12 Jul 2026 21:29:58 +0800 Subject: [PATCH] =?UTF-8?q?refactor(i18n):=20U34=20System=20&=20User=20?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=20i18n=20=E6=B8=85=E7=90=86=20=E2=80=94=20Pr?= =?UTF-8?q?ofilePage=20+=20ResetPasswordDialog=20+=20SystemRolesTab=20+=20?= =?UTF-8?q?LdapManagementSection=204=20=E6=96=87=E4=BB=B6=20UI=20=E7=A1=AC?= =?UTF-8?q?=E7=BC=96=E7=A0=81=E4=B8=AD=E6=96=87=E6=B8=85=E9=9B=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ProfilePage.tsx: 从零接入 useTranslation,基本信息/安全设置/密码修改/用户名邮箱手机/账号来源 15 处中文→t() - ResetPasswordDialog.tsx: 从零接入 useTranslation,重置密码标题/描述/LDAP提示/新密码确认密码/确认重置 11 处中文→t() - SystemRolesTab.tsx: 权限分组标签(云资源/自动化/审批/审计)+toast 校验+confirm description+角色计数+空状态+权限列表计数+编码标签+尾部文案 12 处中文→t()/i18n.t() - LdapManagementSection.tsx: 验证按钮+根OU标签+创建按钮×2+导入按钮+编辑用户描述 8 处中文→t() - 新增 user.profile.* 14键 + user.resetPwd.* 11键 + system.* 16键 共 41 翻译键 - zh/en 7524→7565 +41 对齐 - tsc -b=0, npm build✓ (16.05s), ESLint errors=0/any=0/unused=0, go build=0 - 17/17 集成回归测试通过 --- ROADMAP.md | 1 + .../system/LdapManagementSection.tsx | 12 ++--- .../components/system/ResetPasswordDialog.tsx | 24 +++++----- .../src/components/system/SystemRolesTab.tsx | 24 +++++----- frontend/src/components/user/ProfilePage.tsx | 32 +++++++------ frontend/src/locales/en-US.json | 47 ++++++++++++++++++- frontend/src/locales/zh-CN.json | 47 ++++++++++++++++++- 7 files changed, 141 insertions(+), 46 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index b41c3c1..1e3c93e 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -522,6 +522,7 @@ services/api.ts (1324L) | U31 | AIOps Agent CLI 组件 i18n 修复(AgentCLI + AgentCLIToolbar + AgentCLIHistory + AgentCLIEmptyState + ChatInputArea + agent-cli-constants.ts 6 文件 — Q7/Q35 拆分后丢失的 i18n 集成全部修复,新增 `aiops.cli.*` 命名空间 100 翻译键(8 子分类: toolbar/safety/tips/error/info/viewMode/placeholder/history/empty/input),SAFETY_MODES+TIPS_HOST+TIPS_KUBERNETES 常量数组改为 `getSafetyModes(t)`/`getTipsHost(t)`/`getTipsKubernetes(t)` 接收 TFunction 参数的工厂函数,全部 UI 硬编码中文清零,zh/en 7381→7481 +100 键对齐,tsc -b=0,npm build✓ (18.21s),ESLint=0/53/0,go build=0,17/17 测试通过) | P3 | **done** | | U32 | 全量 hash key 清理 + React Hooks 警告修复(151 个 hash-style flat key (s+hex 后缀) 全部重命名为语义化 nested key — monitor 58/kubernetes 42/aiops 17/agent 12/cloud 13/pipeline 6/system 3;58 个源文件 147 处 t()/i18n.t() 调用更新;3 个命名空间冲突修复(monitor.hostMonitor/agent.agentMonitor/aiops.aiAnalysisResult 原 string → .title 子键);React Hooks 警告 53→43 (-10): exhaustive-deps 4→0 + preserve-manual-memoization 5→0 + 变量遮蔽修复(filter(t)→filter(tg));zh/en 7480=7480 完全对齐 0 hash key 残留;tsc -b=0,npm build✓ (17.59s),ESLint=0/43/any=0,go build=0,17/17 测试通过) | P3 | **done** | | U33 | K8s 资源编辑器 + Pod 日志/Metrics i18n 补全(resource-visual-editors/ 5 文件: ConfigMap/Secret/Service/Ingress/PvcEditor 按钮文案+帮助文本+访问模式+IngressClass 提示全部 i18n;pod-logs-viewer/ 3 文件: ControlBar 导出/刷新/实时/停止+LogContentArea 加载中+LogsHeader 标题;PodLogsViewer 6 处错误消息+DialogTitle/Description;PodMetricsPage subtitle+表头+所有命名空间;17 处 toast description 模板字面量中文→i18n.t() 插值 (DeploymentEditAdvanced/Containers/Storage/View/Detail/Service/ResourceVisualEditor/NamespaceResourcePage/ClusterOverviewPage);5 处 confirm description (DeploymentView/EditService/Detail/WorkloadViewPage/MiddlewareMonitor/SiteMonitor/HealthProbeSection);5 处 subtitle 模板字面量 (PodMetricsPage/CapacityPage/EventsPage/KubernetesNodes/ClusterOverviewPage);新增 k8s.rve 14键+k8s.toast 18键+k8s.podMetrics 4键+k8s.podLogs 13键 共 49 翻译键;zh/en 7481→7524 +43 对齐;tsc -b=0,npm build✓ (17.76s),ESLint=0/43/any=0,go build=0,17/17 测试通过) | P3 | **done** | +| U34 | System & User 模块 i18n 清理(ProfilePage + ResetPasswordDialog + SystemRolesTab + LdapManagementSection 4 文件 — ProfilePage 从零接入 useTranslation + ResetPasswordDialog 从零接入 useTranslation + SystemRolesTab 权限分组标签(云资源/自动化/审批/审计)+toast+confirm description+角色计数+空状态+权限列表计数+编码标签+尾部文案全部 i18n + LdapManagementSection 验证按钮+根OU标签+创建按钮+导入按钮+编辑用户描述全部 i18n;新增 user.profile.* 14键+user.resetPwd.* 11键+system.* 16键 共 41 翻译键;UI 硬编码中文 ProfilePage 15→0 + ResetPasswordDialog 11→0 + SystemRolesTab 12→0 + LdapManagementSection 8→0;zh/en 7524→7565 +41 对齐;tsc -b=0,npm build✓ (16.05s),ESLint=0/43/any=0,go build=0,17/17 测试通过) | P3 | **done** | --- diff --git a/frontend/src/components/system/LdapManagementSection.tsx b/frontend/src/components/system/LdapManagementSection.tsx index 2f8ac54..ff9a1c8 100644 --- a/frontend/src/components/system/LdapManagementSection.tsx +++ b/frontend/src/components/system/LdapManagementSection.tsx @@ -204,7 +204,7 @@ export function LdapManagementSection(props: LdapManagementSectionProps) { disabled={ldapUserTesting || !ldapTestUser} > {ldapUserTesting ? : null} - 验证 + {t('system.testConnection')} @@ -221,7 +221,7 @@ export function LdapManagementSection(props: LdapManagementSectionProps) { - 根 ({ldapConfig.base_dn}) + {t('system.rootOu', { dn: ldapConfig.base_dn })} {ldapOUs.map((ou: JsonData) => ( {ou.description ? `${ou.name} (${ou.description})` : ou.name} @@ -258,7 +258,7 @@ export function LdapManagementSection(props: LdapManagementSectionProps) { @@ -300,7 +300,7 @@ export function LdapManagementSection(props: LdapManagementSectionProps) { @@ -315,7 +315,7 @@ export function LdapManagementSection(props: LdapManagementSectionProps) { disabled={ldapUsers.length === 0 || importing} > {importing ? : } - {selectedLDAPUsers.length > 0 ? `导入 (${selectedLDAPUsers.length})` : `导入全部 (${ldapUsers.length})`} + {selectedLDAPUsers.length > 0 ? t('system.importSelected', { count: selectedLDAPUsers.length }) : t('system.importAll', { count: ldapUsers.length })} @@ -427,7 +427,7 @@ export function LdapManagementSection(props: LdapManagementSectionProps) { {t('system.editLdapUser')} - 修改 {editLdapUser?.uid} 的属性 + {t('system.editLdapUserDesc', { uid: editLdapUser?.uid })} {editLdapUser && ( diff --git a/frontend/src/components/system/ResetPasswordDialog.tsx b/frontend/src/components/system/ResetPasswordDialog.tsx index fe3f7aa..7d4b5cb 100644 --- a/frontend/src/components/system/ResetPasswordDialog.tsx +++ b/frontend/src/components/system/ResetPasswordDialog.tsx @@ -1,5 +1,6 @@ // 管理员重置用户密码弹窗 // Logan.Li import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; @@ -17,6 +18,7 @@ interface ResetPasswordDialogProps { } export function ResetPasswordDialog({ user, onClose }: ResetPasswordDialogProps) { + const { t } = useTranslation(); const { toast } = useToast(); const [password, setPassword] = useState(''); const [confirm, setConfirm] = useState(''); @@ -30,12 +32,12 @@ export function ResetPasswordDialog({ user, onClose }: ResetPasswordDialogProps) setSaving(true); try { await userApi.resetUserPassword(user.id, password); - toast({ title: '密码已重置', description: `用户 ${user.nickname || user.username} 的新密码已生效` }); + toast({ title: t('user.resetPwd.success'), description: t('user.resetPwd.successDesc', { name: user.nickname || user.username }) }); setPassword(''); setConfirm(''); onClose(); } catch (e: unknown) { - toast({ title: '重置失败', description: getErrorMessage(e), variant: 'destructive' }); + toast({ title: t('user.resetPwd.failed'), description: getErrorMessage(e), variant: 'destructive' }); } finally { setSaving(false); } @@ -47,34 +49,34 @@ export function ResetPasswordDialog({ user, onClose }: ResetPasswordDialogProps) - 重置密码 + {t('user.resetPwd.title')} - {user ? `为「${user.nickname || user.username}」设置新密码` : ''} + {user ? t('user.resetPwd.desc', { name: user.nickname || user.username }) : ''} {isLdap ? ( -

LDAP 用户请在 LDAP 目录中管理密码,不支持本地重置。

+

{t('user.resetPwd.ldapHint')}

) : (
- + setPassword(e.target.value)} - placeholder="至少 6 位" + placeholder={t('user.resetPwd.newPwdPlaceholder')} className="mt-1 h-9 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)]" />
- + setConfirm(e.target.value)} - placeholder="再次输入新密码" + placeholder={t('user.resetPwd.confirmPlaceholder')} className="mt-1 h-9 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)]" />
@@ -82,11 +84,11 @@ export function ResetPasswordDialog({ user, onClose }: ResetPasswordDialogProps) )} - + {!isLdap && ( )} diff --git a/frontend/src/components/system/SystemRolesTab.tsx b/frontend/src/components/system/SystemRolesTab.tsx index ea3a48a..7fc5bc7 100644 --- a/frontend/src/components/system/SystemRolesTab.tsx +++ b/frontend/src/components/system/SystemRolesTab.tsx @@ -35,14 +35,14 @@ const ROLE_ICONS: Record = { const PERM_MODULE_GROUPS: { label: string; prefixes: string[] }[] = [ { label: i18n.t('menu.dashboard'), prefixes: ['dashboard:'] }, { label: i18n.t('menu.assets'), prefixes: ['server:'] }, - { label: '云资源', prefixes: ['cloud:'] }, + { label: i18n.t('system.permCloudResource'), prefixes: ['cloud:'] }, { label: 'K8s', prefixes: ['k8s:'] }, { label: i18n.t('common.publish'), prefixes: ['deploy:'] }, { label: i18n.t('monitor.hostDashboard.monitor'), prefixes: ['monitor:', 'agent:'] }, - { label: '自动化', prefixes: ['auto:'] }, + { label: i18n.t('system.permAutomation'), prefixes: ['auto:'] }, { label: 'AI', prefixes: ['aiops:'] }, - { label: '审批', prefixes: ['approval:'] }, - { label: '审计', prefixes: ['audit:'] }, + { label: i18n.t('system.permApproval'), prefixes: ['approval:'] }, + { label: i18n.t('system.permAudit'), prefixes: ['audit:'] }, { label: i18n.t('common.system3'), prefixes: ['system:'] }, ]; @@ -138,8 +138,8 @@ export function SystemRolesTab({ selectedRole, setSelectedRole }: SystemRolesTab }; const handleSubmit = async () => { - if (!form.name.trim()) { toast.error('请输入角色名称'); return; } - if (!form.code.trim()) { toast.error('请输入角色编码'); return; } + if (!form.name.trim()) { toast.error(t('system.roleNameRequired')); return; } + if (!form.code.trim()) { toast.error(t('system.roleCodeRequired')); return; } setSaving(true); try { const payload = { @@ -171,7 +171,7 @@ export function SystemRolesTab({ selectedRole, setSelectedRole }: SystemRolesTab } const ok = await confirm({ title: t('system.deleteRole'), - description: `确认删除角色「${role.name}」吗?该操作不可恢复。`, + description: t('system.confirmDeleteRoleDesc', { name: role.name }), confirmText: t('common.delete'), variant: 'destructive', }); @@ -192,12 +192,12 @@ export function SystemRolesTab({ selectedRole, setSelectedRole }: SystemRolesTab

{t('system.permHowEffect')}

-

{t('system.permEffectDesc')}{t('system.permBackendAuth')}{t('monitor.logCenterArch.connector')}{t('system.permEffectFrontend')},修改后用户需重新登录生效。

+

{t('system.permEffectDesc')}{t('system.permBackendAuth')}{t('monitor.logCenterArch.connector')}{t('system.permEffectFrontend')}{t('system.permEffectTrailing')}

- {roles.length} 个角色 + {t('system.roleCount', { count: roles.length })} @@ -210,7 +210,7 @@ export function SystemRolesTab({ selectedRole, setSelectedRole }: SystemRolesTab ) : roles.length === 0 ? (
-

暂无角色数据

+

{t('system.noRoleData')}

) : (
@@ -321,7 +321,7 @@ function RoleDetailDialog({ )} - 编码:{role?.code} + {t('system.roleCodeLabel', { code: role?.code })}
@@ -330,7 +330,7 @@ function RoleDetailDialog({

- 权限列表 ({role?.permissions?.length || 0} 项) + {t('system.permListCount', { count: role?.permissions?.length || 0 })}

{role && (role.permissions as string[]).length > 0 ? (
diff --git a/frontend/src/components/user/ProfilePage.tsx b/frontend/src/components/user/ProfilePage.tsx index 125c7dc..7c94bb2 100644 --- a/frontend/src/components/user/ProfilePage.tsx +++ b/frontend/src/components/user/ProfilePage.tsx @@ -1,5 +1,6 @@ // ProfilePage — 个人中心(基本信息 / 安全 / API 访问密钥)// Logan.Li import { useState, useEffect, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -29,6 +30,7 @@ type Profile = { } export default function ProfilePage() { + const { t } = useTranslation(); const { toast } = useToast(); const storeUser = useUserStore(s => s.user); @@ -51,7 +53,7 @@ export default function ProfilePage() { useEffect(() => { load(); }, [load]); - const displayName = profile?.real_name || profile?.nickname || storeUser?.nickname || storeUser?.username || '用户'; + const displayName = profile?.real_name || profile?.nickname || storeUser?.nickname || storeUser?.username || t('user.profile.defaultUser'); const username = profile?.username || storeUser?.username || ''; const handleChangePassword = async () => { @@ -59,11 +61,11 @@ export default function ProfilePage() { setPwdSaving(true); try { await authApi.changePassword({ old_password: oldPwd, new_password: newPwd }); - toast({ title: '密码已修改' }); + toast({ title: t('user.profile.pwdChanged') }); setOldPwd(''); setNewPwd(''); } catch (e: unknown) { - toast({ title: '修改失败', description: getErrorMessage(e), variant: 'destructive' }); + toast({ title: t('user.profile.pwdChangeFailed'), description: getErrorMessage(e), variant: 'destructive' }); } finally { setPwdSaving(false); } @@ -77,7 +79,7 @@ export default function ProfilePage() { subtitle={`@${username} · ${profile?.email || '—'}`} icon={} loading={loading} - loadingLabel="加载中..." + loadingLabel={t('common.loading')} actions={(
{(profile?.roles || []).map((r) => ( @@ -92,15 +94,15 @@ export default function ProfilePage() { - 基本信息 + {t('user.profile.basicInfo')} -
用户名{username}
-
邮箱{profile?.email || '—'}
-
手机{profile?.phone || '—'}
-
账号来源 - {profile?.auth_source === 'ldap' ? 'LDAP' : '本地'} +
{t('user.profile.username')}{username}
+
{t('user.profile.email')}{profile?.email || '—'}
+
{t('user.profile.phone')}{profile?.phone || '—'}
+
{t('user.profile.authSource')} + {profile?.auth_source === 'ldap' ? 'LDAP' : t('user.profile.local')}
@@ -108,22 +110,22 @@ export default function ProfilePage() { - 安全设置 + {t('user.profile.security')} - 修改登录密码 + {t('user.profile.changePwd')}
- + setOldPwd(e.target.value)} className="mt-1 h-9 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)]" />
- + setNewPwd(e.target.value)} className="mt-1 h-9 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)]" />
diff --git a/frontend/src/locales/en-US.json b/frontend/src/locales/en-US.json index c1816d4..8d45e77 100644 --- a/frontend/src/locales/en-US.json +++ b/frontend/src/locales/en-US.json @@ -7455,7 +7455,23 @@ "loadFailed": "Failed to load audit settings", "invalidRetentionDays": "Please select valid retention days", "noExpiredRecords": ", no expired records" - } + }, + "permCloudResource": "Cloud Resources", + "permAutomation": "Automation", + "permApproval": "Approval", + "permAudit": "Audit", + "roleCodeRequired": "Please enter role code", + "confirmDeleteRoleDesc": "Are you sure to delete role \"{{name}}\"? This action cannot be undone.", + "roleCount": "{{count}} roles", + "noRoleData": "No role data", + "permListCount": "Permissions ({{count}})", + "roleCodeLabel": "Code: {{code}}", + "testConnection": "Verify", + "rootOu": "Root ({{dn}})", + "importSelected": "Import ({{count}})", + "importAll": "Import All ({{count}})", + "editLdapUserDesc": "Modify attributes of {{uid}}", + "permEffectTrailing": ", changes take effect after user re-login." }, "aiops": { "title": "AIOps", @@ -8273,6 +8289,35 @@ "credentialCopied": "Credential copied", "saved": "I've saved it", "placeholderToken": "Your access credential" + }, + "profile": { + "basicInfo": "Basic Info", + "security": "Security", + "changePwd": "Change Login Password", + "currentPwd": "Current Password", + "newPwd": "New Password", + "savePwd": "Save Password", + "username": "Username", + "email": "Email", + "phone": "Phone", + "authSource": "Account Source", + "local": "Local", + "pwdChanged": "Password Changed", + "pwdChangeFailed": "Change Failed", + "defaultUser": "User" + }, + "resetPwd": { + "title": "Reset Password", + "desc": "Set a new password for \"{{name}}\"", + "ldapHint": "LDAP users must manage passwords in the LDAP directory. Local reset is not supported.", + "newPwd": "New Password", + "newPwdPlaceholder": "At least 6 characters", + "confirmPwd": "Confirm Password", + "confirmPlaceholder": "Enter the new password again", + "confirm": "Confirm Reset", + "success": "Password Reset", + "successDesc": "New password for user {{name}} is now active", + "failed": "Reset Failed" } }, "logCenter": { diff --git a/frontend/src/locales/zh-CN.json b/frontend/src/locales/zh-CN.json index ab0f181..5832f63 100644 --- a/frontend/src/locales/zh-CN.json +++ b/frontend/src/locales/zh-CN.json @@ -7455,7 +7455,23 @@ "loadFailed": "加载审计设置失败", "invalidRetentionDays": "请选择有效的保留天数", "noExpiredRecords": ",无超期记录" - } + }, + "permCloudResource": "云资源", + "permAutomation": "自动化", + "permApproval": "审批", + "permAudit": "审计", + "roleCodeRequired": "请输入角色编码", + "confirmDeleteRoleDesc": "确认删除角色「{{name}}」吗?该操作不可恢复。", + "roleCount": "{{count}} 个角色", + "noRoleData": "暂无角色数据", + "permListCount": "权限列表 ({{count}} 项)", + "roleCodeLabel": "编码:{{code}}", + "testConnection": "验证", + "rootOu": "根 ({{dn}})", + "importSelected": "导入 ({{count}})", + "importAll": "导入全部 ({{count}})", + "editLdapUserDesc": "修改 {{uid}} 的属性", + "permEffectTrailing": ",修改后用户需重新登录生效。" }, "aiops": { "title": "AIOps 智能", @@ -8273,6 +8289,35 @@ "credentialCopied": "凭证已复制", "saved": "我已保存", "placeholderToken": "你的访问凭证" + }, + "profile": { + "basicInfo": "基本信息", + "security": "安全设置", + "changePwd": "修改登录密码", + "currentPwd": "当前密码", + "newPwd": "新密码", + "savePwd": "保存密码", + "username": "用户名", + "email": "邮箱", + "phone": "手机", + "authSource": "账号来源", + "local": "本地", + "pwdChanged": "密码已修改", + "pwdChangeFailed": "修改失败", + "defaultUser": "用户" + }, + "resetPwd": { + "title": "重置密码", + "desc": "为「{{name}}」设置新密码", + "ldapHint": "LDAP 用户请在 LDAP 目录中管理密码,不支持本地重置。", + "newPwd": "新密码", + "newPwdPlaceholder": "至少 6 位", + "confirmPwd": "确认密码", + "confirmPlaceholder": "再次输入新密码", + "confirm": "确认重置", + "success": "密码已重置", + "successDesc": "用户 {{name}} 的新密码已生效", + "failed": "重置失败" } }, "logCenter": {