diff --git a/ROADMAP.md b/ROADMAP.md
index e8b57f8..794e4ab 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -280,7 +280,7 @@ services/api.ts (1324L)
- ✅ **可扩展架构** — `MenuItem.labelKey` 模式支持增量迁移,后续页面只需添加翻译键 + `useTranslation()` 即可
**后续增量迁移路径**(已建立完整模式,各页面可独立迁移):
-- Dashboard / ServerManager / Kubernetes 各页面 / AlertCenter 等页面内容翻译
+- ✅ Dashboard / ServerManager / Kubernetes 各页面 / AlertCenter / **安全审计(audit)** 等页面内容翻译
- 添加更多语言(如日语 `ja-JP`)只需在 `SUPPORTED_LANGUAGES` + 新 JSON 文件中注册
**验收标准**:
diff --git a/frontend/src/locales/en-US.json b/frontend/src/locales/en-US.json
index 1a1b440..7afeec6 100644
--- a/frontend/src/locales/en-US.json
+++ b/frontend/src/locales/en-US.json
@@ -71,7 +71,14 @@
"operationSuccess": "Operation succeeded",
"operationFailed": "Operation failed",
"loadFailed": "Failed to load",
- "saveFailed": "Failed to save"
+ "saveFailed": "Failed to save",
+ "duration": {
+ "sec": "{{count}}s",
+ "min": "{{count}}m",
+ "minSec": "{{m}}m {{s}}s",
+ "hour": "{{count}}h",
+ "hourMin": "{{h}}h {{m}}m"
+ }
},
"menu": {
"dashboard": "Dashboard",
@@ -545,5 +552,107 @@
"templateNotLoaded": "Template not loaded",
"pleaseFill": "Please fill in \"{{label}}\""
}
+ },
+ "audit": {
+ "operations": {
+ "searchPlaceholder": "Search audit logs...",
+ "allModules": "All Modules",
+ "allActions": "All Actions",
+ "noData": "No audit logs",
+ "viewDetail": "View Details",
+ "totalLogs": "{{count}} logs",
+ "virtualScroll": "virtual scroll",
+ "detailTitle": "Audit Log Details",
+ "statsTitle": "Audit Statistics",
+ "noStats": "No statistics available",
+ "topModules": "Top Modules",
+ "topUsers": "Active Users",
+ "col": {
+ "time": "Time",
+ "user": "User",
+ "module": "Module",
+ "actionType": "Action",
+ "resource": "Resource",
+ "ip": "IP",
+ "result": "Result",
+ "detail": "Detail"
+ },
+ "detailCol": {
+ "userId": "User ID",
+ "ipAddress": "IP Address",
+ "userAgent": "User-Agent",
+ "message": "Message"
+ },
+ "stats": {
+ "totalLogs": "Total Logs",
+ "todayLogs": "Today",
+ "successCount": "Success",
+ "failureCount": "Failed"
+ }
+ },
+ "login": {
+ "searchPlaceholder": "Search user/IP/location...",
+ "noData": "No login logs",
+ "all": "All",
+ "col": {
+ "loginTime": "Login Time",
+ "user": "User",
+ "loginType": "Method",
+ "ip": "IP",
+ "location": "Location",
+ "browser": "Browser/OS",
+ "status": "Status"
+ },
+ "type": {
+ "password": "Password",
+ "ldap": "LDAP",
+ "token": "Token",
+ "oauth": "OAuth"
+ }
+ },
+ "terminal": {
+ "searchUserServer": "Search user/server...",
+ "searchUserServerIp": "Search user/server/IP...",
+ "searchUserCommand": "Search user/command...",
+ "sessionIdPlaceholder": "Session ID (optional)",
+ "noActive": "No active sessions",
+ "noHistory": "No history sessions",
+ "noCommands": "No command records",
+ "totalCommands": "{{count}} commands",
+ "virtualScroll": "virtual scroll",
+ "autoRefresh": "Auto Refresh",
+ "autoRefreshHintOn": "Auto-refresh every {{sec}}s · click to disable",
+ "autoRefreshHintOff": "Click to enable auto-refresh",
+ "killTitle": "Terminate Session",
+ "killConfirm": "Force disconnect user \"{{user}}\"'s current session?",
+ "killConfirmBtn": "Terminate",
+ "liveMonitor": "Live Monitor",
+ "replay": "Replay",
+ "downloadRecord": "Download Recording",
+ "tab": {
+ "active": "Active Sessions",
+ "history": "History Sessions",
+ "commands": "Session Commands"
+ },
+ "col": {
+ "id": "ID",
+ "user": "User",
+ "targetServer": "Target Server",
+ "port": "Port",
+ "startTime": "Start Time",
+ "endTime": "End Time",
+ "duration": "Duration",
+ "status": "Status",
+ "actions": "Actions",
+ "command": "Command",
+ "outputSize": "Output Size",
+ "sessionRef": "Session"
+ },
+ "status": {
+ "active": "Active",
+ "failed": "Failed",
+ "closed": "Closed"
+ }
+ }
}
}
diff --git a/frontend/src/locales/zh-CN.json b/frontend/src/locales/zh-CN.json
index d1a24b3..8341b09 100644
--- a/frontend/src/locales/zh-CN.json
+++ b/frontend/src/locales/zh-CN.json
@@ -71,7 +71,14 @@
"operationSuccess": "操作成功",
"operationFailed": "操作失败",
"loadFailed": "加载失败",
- "saveFailed": "保存失败"
+ "saveFailed": "保存失败",
+ "duration": {
+ "sec": "{{count}}秒",
+ "min": "{{count}}分",
+ "minSec": "{{m}}分{{s}}秒",
+ "hour": "{{count}}时",
+ "hourMin": "{{h}}时{{m}}分"
+ }
},
"menu": {
"dashboard": "仪表盘",
@@ -545,5 +552,107 @@
"templateNotLoaded": "模板未加载",
"pleaseFill": "请填写「{{label}}」"
}
+ },
+ "audit": {
+ "operations": {
+ "searchPlaceholder": "搜索操作日志...",
+ "allModules": "全部模块",
+ "allActions": "全部操作",
+ "noData": "暂无操作日志",
+ "viewDetail": "查看详情",
+ "totalLogs": "共 {{count}} 条日志",
+ "virtualScroll": "虚拟滚动渲染",
+ "detailTitle": "操作日志详情",
+ "statsTitle": "审计统计",
+ "noStats": "暂无统计数据",
+ "topModules": "热门模块",
+ "topUsers": "活跃用户",
+ "col": {
+ "time": "时间",
+ "user": "用户",
+ "module": "模块",
+ "actionType": "操作类型",
+ "resource": "资源",
+ "ip": "IP",
+ "result": "结果",
+ "detail": "详情"
+ },
+ "detailCol": {
+ "userId": "用户ID",
+ "ipAddress": "IP地址",
+ "userAgent": "User-Agent",
+ "message": "详情"
+ },
+ "stats": {
+ "totalLogs": "总日志数",
+ "todayLogs": "今日日志",
+ "successCount": "成功数",
+ "failureCount": "失败数"
+ }
+ },
+ "login": {
+ "searchPlaceholder": "搜索用户/IP/归属地...",
+ "noData": "暂无登录日志",
+ "all": "全部",
+ "col": {
+ "loginTime": "登录时间",
+ "user": "用户",
+ "loginType": "登录方式",
+ "ip": "IP",
+ "location": "归属地",
+ "browser": "浏览器/OS",
+ "status": "状态"
+ },
+ "type": {
+ "password": "密码登录",
+ "ldap": "LDAP",
+ "token": "Token",
+ "oauth": "OAuth"
+ }
+ },
+ "terminal": {
+ "searchUserServer": "搜索用户/服务器...",
+ "searchUserServerIp": "搜索用户/服务器/IP...",
+ "searchUserCommand": "搜索用户/命令...",
+ "sessionIdPlaceholder": "会话 ID(可选)",
+ "noActive": "暂无在线会话",
+ "noHistory": "暂无历史会话",
+ "noCommands": "暂无会话命令记录",
+ "totalCommands": "共 {{count}} 条命令",
+ "virtualScroll": "虚拟滚动渲染",
+ "autoRefresh": "自动刷新",
+ "autoRefreshHintOn": "每 {{sec}}s 自动刷新 · 点击关闭",
+ "autoRefreshHintOff": "点击开启自动刷新",
+ "killTitle": "阻断会话",
+ "killConfirm": "确认强制断开用户「{{user}}」的当前会话吗?",
+ "killConfirmBtn": "阻断",
+ "liveMonitor": "实时查看",
+ "replay": "回放",
+ "downloadRecord": "下载录制",
+ "tab": {
+ "active": "在线会话",
+ "history": "历史会话",
+ "commands": "会话命令"
+ },
+ "col": {
+ "id": "ID",
+ "user": "用户",
+ "targetServer": "目标服务器",
+ "port": "端口",
+ "startTime": "开始时间",
+ "endTime": "结束时间",
+ "duration": "时长",
+ "status": "状态",
+ "actions": "操作",
+ "command": "命令",
+ "outputSize": "输出大小",
+ "sessionRef": "关联会话"
+ },
+ "status": {
+ "active": "活动中",
+ "failed": "失败",
+ "closed": "已关闭"
+ }
+ }
}
}
diff --git a/frontend/src/pages/audit/AuditOperationsTab.tsx b/frontend/src/pages/audit/AuditOperationsTab.tsx
index 057f34e..9135edc 100644
--- a/frontend/src/pages/audit/AuditOperationsTab.tsx
+++ b/frontend/src/pages/audit/AuditOperationsTab.tsx
@@ -1,6 +1,7 @@
// 操作审计 Tab — 增强版审计日志列表,含筛选/导出/统计/清除
// v2: 虚拟滚动渲染,支持万级日志流畅查看
import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
+import { useTranslation } from 'react-i18next';
import { useVirtualizer } from '@tanstack/react-virtual';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
@@ -46,6 +47,7 @@ interface AuditStatistics {
}
export function AuditOperationsTab() {
+ const { t } = useTranslation();
const _confirm = useConfirm();
// Filters
const [keyword, setKeyword] = useState('');
@@ -105,11 +107,11 @@ export function AuditOperationsTab() {
const getLogResultBadge = (result: OperationLog['result']) => {
return result === 'success' ? (
- 成功
+ {t('common.success')}
) : (
- 失败
+ {t('common.failed')}
);
};
@@ -163,7 +165,7 @@ export function AuditOperationsTab() {
setKeyword(e.target.value)}
className="pl-9 h-9 bg-slate-800/50 border-slate-700 text-slate-200 placeholder:text-slate-500"
@@ -176,7 +178,7 @@ export function AuditOperationsTab() {
onChange={(e) => setModuleFilter(e.target.value)}
className="h-9 px-3 text-sm rounded-md bg-slate-800/50 border border-slate-700 text-slate-300 focus:outline-none focus:border-blue-500"
>
-
+
{modules.map((m) => (
+
{actions.map((a) => (
@@ -227,7 +229,7 @@ export function AuditOperationsTab() {
) : filteredLogs.length === 0 ? (
-
暂无操作日志
+
{t('audit.operations.noData')}
) : (
<>
@@ -236,14 +238,14 @@ export function AuditOperationsTab() {
className="grid gap-2 px-4 py-2 border-b border-slate-800 bg-slate-900 text-xs font-medium text-slate-400 sticky top-0 z-10"
style={{ gridTemplateColumns: GRID_COLS }}
>
- 时间
- 用户
- 模块
- 操作类型
- 资源
- IP
- 结果
- 详情
+ {t('audit.operations.col.time')}
+ {t('audit.operations.col.user')}
+ {t('audit.operations.col.module')}
+ {t('audit.operations.col.actionType')}
+ {t('audit.operations.col.resource')}
+ {t('audit.operations.col.ip')}
+ {t('audit.operations.col.result')}
+ {t('audit.operations.col.detail')}
{/* 虚拟滚动列表 */}
@@ -290,7 +292,7 @@ export function AuditOperationsTab() {
size="icon"
className="h-8 w-8 text-slate-400 hover:text-blue-400 hover:bg-slate-800"
onClick={() => viewDetail(log)}
- title="查看详情"
+ title={t('audit.operations.viewDetail')}
>
@@ -303,7 +305,7 @@ export function AuditOperationsTab() {
{/* 行数统计 */}
- 共 {filteredLogs.length} 条日志 {filteredLogs.length > 200 && '· 虚拟滚动渲染'}
+ {t('audit.operations.totalLogs', { count: filteredLogs.length })} {filteredLogs.length > 200 && `· ${t('audit.operations.virtualScroll')}`}
>
)}
@@ -330,6 +332,7 @@ export function AuditOperationsTab() {
// =========================================================================
function LogDetailDialog({ log, onClose }: { log: OperationLog | null; onClose: () => void }) {
+ const { t } = useTranslation();
if (!log) return null;
return (
@@ -337,7 +340,7 @@ function LogDetailDialog({ log, onClose }: { log: OperationLog | null; onClose:
-
操作日志详情
+ {t('audit.operations.detailTitle')}
@@ -382,7 +385,7 @@ function LogDetailDialog({ log, onClose }: { log: OperationLog | null; onClose:
className="border-slate-700 text-slate-300 hover:bg-slate-800"
onClick={onClose}
>
- 关闭
+ {t('common.close')}
@@ -416,6 +419,7 @@ function StatisticsDialog({
loading: boolean;
stats: AuditStatistics | null;
}) {
+ const { t } = useTranslation();
if (!open) return null;
return (
@@ -423,7 +427,7 @@ function StatisticsDialog({
-
审计统计
+
{t('audit.operations.statsTitle')}