refactor(i18n): U34 System & User 模块 i18n 清理 — ProfilePage + ResetPasswordDialog + SystemRolesTab + LdapManagementSection 4 文件 UI 硬编码中文清零

- 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 集成回归测试通过
This commit is contained in:
attacker committed 2026-07-12 21:29:58 +08:00
1 parent ad9acea1e5
commit 0dbb85f5b8
7 files changed
+141 -46

No files matched your search

@@ -204,7 +204,7 @@ export function LdapManagementSection(props: LdapManagementSectionProps) {
disabled={ldapUserTesting || !ldapTestUser}
>
{ldapUserTesting ? <Loader2 className="h-4 w-4 mr-1.5 animate-spin" /> : null}
验证
{t('system.testConnection')}
</Button>
</div>
</DialogContent>
@@ -221,7 +221,7 @@ export function LdapManagementSection(props: LdapManagementSectionProps) {
<SelectValue placeholder={t('system.selectOu')} />
</SelectTrigger>
<SelectContent className="dg-input">
<SelectItem value={ldapConfig.base_dn}>根 ({ldapConfig.base_dn})</SelectItem>
<SelectItem value={ldapConfig.base_dn}>{t('system.rootOu', { dn: ldapConfig.base_dn })}</SelectItem>
{ldapOUs.map((ou: JsonData) => (
<SelectItem key={ou.dn} value={ou.dn}>
{ou.description ? `${ou.name} (${ou.description})` : ou.name}
@@ -258,7 +258,7 @@ export function LdapManagementSection(props: LdapManagementSectionProps) {
</div>
<Button className="w-full bg-blue-600 hover:bg-blue-700 text-white" onClick={handleCreateOU} disabled={!ouForm.ou_name || ldapSaving}>
{ldapSaving ? <Loader2 className="h-4 w-4 mr-1.5 animate-spin" /> : null}
创建
{t('common.create')}
</Button>
</div>
</DialogContent>
@@ -300,7 +300,7 @@ export function LdapManagementSection(props: LdapManagementSectionProps) {
</div>
<Button className="w-full bg-blue-600 hover:bg-blue-700 text-white" onClick={handleCreateLDAPUser} disabled={!newUser.username || !newUser.password || ldapSaving}>
{ldapSaving ? <Loader2 className="h-4 w-4 mr-1.5 animate-spin" /> : null}
创建
{t('common.create')}
</Button>
</div>
</DialogContent>
@@ -315,7 +315,7 @@ export function LdapManagementSection(props: LdapManagementSectionProps) {
disabled={ldapUsers.length === 0 || importing}
>
{importing ? <Loader2 className="h-3.5 w-3.5 mr-1 animate-spin" /> : <Download className="h-3.5 w-3.5 mr-1" />}
{selectedLDAPUsers.length > 0 ? `导入 (${selectedLDAPUsers.length})` : `导入全部 (${ldapUsers.length})`}
{selectedLDAPUsers.length > 0 ? t('system.importSelected', { count: selectedLDAPUsers.length }) : t('system.importAll', { count: ldapUsers.length })}
</Button>
</div>
@@ -427,7 +427,7 @@ export function LdapManagementSection(props: LdapManagementSectionProps) {
{t('system.editLdapUser')}
</DialogTitle>
<DialogDescription className="dg-text-muted">
修改 {editLdapUser?.uid} 的属性
{t('system.editLdapUserDesc', { uid: editLdapUser?.uid })}
</DialogDescription>
</DialogHeader>
{editLdapUser && (
@@ -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)
<DialogHeader>
<DialogTitle className="text-[var(--dg-text-primary)] flex items-center gap-2">
<Key className="h-5 w-5 text-[var(--dg-blue)]" />
重置密码
{t('user.resetPwd.title')}
</DialogTitle>
<DialogDescription className="text-[var(--dg-text-muted)]">
{user ? `为「${user.nickname || user.username}」设置新密码` : ''}
{user ? t('user.resetPwd.desc', { name: user.nickname || user.username }) : ''}
</DialogDescription>
</DialogHeader>
{isLdap ? (
<p className="text-sm dg-text-yellow/90 py-2">LDAP 用户请在 LDAP 目录中管理密码,不支持本地重置。</p>
<p className="text-sm dg-text-yellow/90 py-2">{t('user.resetPwd.ldapHint')}</p>
) : (
<div className="space-y-3 py-1">
<div>
<Label className="text-xs text-[var(--dg-text-muted)]">新密码</Label>
<Label className="text-xs text-[var(--dg-text-muted)]">{t('user.resetPwd.newPwd')}</Label>
<Input
type="password"
value={password}
onChange={e => 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)]"
/>
</div>
<div>
<Label className="text-xs text-[var(--dg-text-muted)]">确认密码</Label>
<Label className="text-xs text-[var(--dg-text-muted)]">{t('user.resetPwd.confirmPwd')}</Label>
<Input
type="password"
value={confirm}
onChange={e => setConfirm(e.target.value)}
placeholder="再次输入新密码"
placeholder={t('user.resetPwd.confirmPlaceholder')}
className="mt-1 h-9 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)]"
/>
</div>
@@ -82,11 +84,11 @@ export function ResetPasswordDialog({ user, onClose }: ResetPasswordDialogProps)
)}
<DialogFooter>
<Button variant="outline" className="border-[var(--dg-border)]" onClick={onClose}>取消</Button>
<Button variant="outline" className="border-[var(--dg-border)]" onClick={onClose}>{t('common.cancel')}</Button>
{!isLdap && (
<Button onClick={handleReset} disabled={!canSubmit || saving} className="bg-[var(--dg-blue)] hover:opacity-90">
{saving ? <Loader2 className="h-4 w-4 animate-spin mr-1" /> : null}
确认重置
{t('user.resetPwd.confirm')}
</Button>
)}
</DialogFooter>
@@ -35,14 +35,14 @@ const ROLE_ICONS: Record<string, string> = {
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
<ShieldCheck className="h-5 w-5 dg-text-blue shrink-0 mt-0.5" />
<div className="text-sm text-[var(--dg-text-secondary)] space-y-1">
<p className="font-medium text-[var(--dg-text-primary)]">{t('system.permHowEffect')}</p>
<p>{t('system.permEffectDesc')}<strong className="text-[var(--dg-text-primary)]">{t('system.permBackendAuth')}</strong>{t('monitor.logCenterArch.connector')}<strong className="text-[var(--dg-text-primary)]">{t('system.permEffectFrontend')}</strong>,修改后用户需重新登录生效。</p>
<p>{t('system.permEffectDesc')}<strong className="text-[var(--dg-text-primary)]">{t('system.permBackendAuth')}</strong>{t('monitor.logCenterArch.connector')}<strong className="text-[var(--dg-text-primary)]">{t('system.permEffectFrontend')}</strong>{t('system.permEffectTrailing')}</p>
</div>
</div>
<div className="flex items-center justify-end gap-2">
<span className="text-xs text-[var(--dg-text-muted)]">{roles.length} 个角色</span>
<span className="text-xs text-[var(--dg-text-muted)]">{t('system.roleCount', { count: roles.length })}</span>
<Button className="bg-blue-600 hover:bg-blue-700 text-white" onClick={openCreate}>
<Plus className="h-4 w-4 mr-2" />{t('pipeline.newRole')}
</Button>
@@ -210,7 +210,7 @@ export function SystemRolesTab({ selectedRole, setSelectedRole }: SystemRolesTab
) : roles.length === 0 ? (
<div className="flex flex-col items-center justify-center py-16 text-[var(--dg-text-muted)]">
<Shield className="h-10 w-10 mb-3 opacity-40" />
<p className="text-sm">暂无角色数据</p>
<p className="text-sm">{t('system.noRoleData')}</p>
</div>
) : (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
@@ -321,7 +321,7 @@ function RoleDetailDialog({
</Badge>
)}
</DialogTitle>
<DialogDescription className="text-[var(--dg-text-muted)]">编码:{role?.code}</DialogDescription>
<DialogDescription className="text-[var(--dg-text-muted)]">{t('system.roleCodeLabel', { code: role?.code })}</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div className="rounded-lg bg-[var(--dg-bg-tertiary)] p-4">
@@ -330,7 +330,7 @@ function RoleDetailDialog({
</div>
<div>
<h4 className="text-sm font-medium text-[var(--dg-text-primary)] mb-2">
权限列表 ({role?.permissions?.length || 0} 项)
{t('system.permListCount', { count: role?.permissions?.length || 0 })}
</h4>
{role && (role.permissions as string[]).length > 0 ? (
<div className="flex flex-wrap gap-2 max-h-60 overflow-y-auto">