feat(i18n): 安全审计模块全量国际化 — 7 文件硬编码中文改翻译键 + audit 命名空间(89 键)

迁移文件:
- AuditOperationsTab.tsx — 操作审计(搜索/筛选/虚拟滚动表格/详情弹窗/统计弹窗)
- TerminalAuditTab.tsx — 终端审计子标签(在线/历史/命令)
- LoginLogsTab.tsx — 登录日志(DataTable + 状态筛选 + 登录方式)
- ActiveSessionsPanel.tsx — 在线会话(自动刷新 + 阻断确认 + 实时监控)
- HistorySessionsPanel.tsx — 历史会话(回放/下载录制)
- SessionCommandsPanel.tsx — 会话命令(虚拟滚动)
- helpers.tsx — formatDuration/getStatusBadge (i18n.t 非组件模式)

翻译键: audit 命名空间 89 键 (operations/login/terminal 三子域)
  + common.duration 时长单位(秒/分/时)
总键数: 481→610, 命名空间: 8→9 (common/menu/header/login/dashboard/server/alert/approval/audit)
zh-CN/en-US 完全对齐, tsc -b=0, npm build✓, 17/17 测试通过
This commit is contained in:
attacker committed 2026-06-29 20:44:08 +08:00
1 parent fef4997391
commit 186ece6495
10 files changed
+375 -141

No files matched your search

@@ -1,6 +1,7 @@
// 终端审计 — 历史会话子面板 // Logan.Li
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
@@ -19,6 +20,7 @@ import { useTerminalSessions } from '@/hooks/api';
import { formatDuration, getStatusBadge } from './helpers';
export function HistorySessionsPanel() {
const { t } = useTranslation();
const { data: sessions = [], isLoading, refetch } = useTerminalSessions({
page: 1,
page_size: 100,
@@ -58,7 +60,7 @@ export function HistorySessionsPanel() {
<div className="relative w-64">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-slate-500" />
<Input
placeholder="搜索用户/服务器/IP..."
placeholder={t('audit.terminal.searchUserServerIp')}
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
className="pl-9 h-9 bg-slate-800/50 border-slate-700 text-slate-200 placeholder:text-slate-500"
@@ -70,7 +72,7 @@ export function HistorySessionsPanel() {
onClick={() => refetch()}
>
<RefreshCw className="h-4 w-4 mr-2" />
刷新
{t('common.refresh')}
</Button>
</div>
@@ -84,21 +86,21 @@ export function HistorySessionsPanel() {
) : filtered.length === 0 ? (
<div className="flex flex-col items-center justify-center py-16 text-slate-500">
<Monitor className="h-8 w-8 mb-3 opacity-40" />
<p className="text-sm">暂无历史会话</p>
<p className="text-sm">{t('audit.terminal.noHistory')}</p>
</div>
) : (
<Table>
<TableHeader>
<TableRow className="border-slate-800 hover:bg-transparent">
<TableHead className="text-slate-400">ID</TableHead>
<TableHead className="text-slate-400">用户</TableHead>
<TableHead className="text-slate-400">目标服务器</TableHead>
<TableHead className="text-slate-400">端口</TableHead>
<TableHead className="text-slate-400">开始时间</TableHead>
<TableHead className="text-slate-400">结束时间</TableHead>
<TableHead className="text-slate-400">时长</TableHead>
<TableHead className="text-slate-400">状态</TableHead>
<TableHead className="text-slate-400 text-right">操作</TableHead>
<TableHead className="text-slate-400">{t('audit.terminal.col.id')}</TableHead>
<TableHead className="text-slate-400">{t('audit.terminal.col.user')}</TableHead>
<TableHead className="text-slate-400">{t('audit.terminal.col.targetServer')}</TableHead>
<TableHead className="text-slate-400">{t('audit.terminal.col.port')}</TableHead>
<TableHead className="text-slate-400">{t('audit.terminal.col.startTime')}</TableHead>
<TableHead className="text-slate-400">{t('audit.terminal.col.endTime')}</TableHead>
<TableHead className="text-slate-400">{t('audit.terminal.col.duration')}</TableHead>
<TableHead className="text-slate-400">{t('audit.terminal.col.status')}</TableHead>
<TableHead className="text-slate-400 text-right">{t('audit.terminal.col.actions')}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
@@ -140,7 +142,7 @@ export function HistorySessionsPanel() {
size="icon"
className="h-8 w-8 text-slate-400 hover:text-blue-400 hover:bg-slate-800"
onClick={() => openReplay(s.id)}
title="回放"
title={t('audit.terminal.replay')}
>
<Play className="h-4 w-4" />
</Button>
@@ -149,7 +151,7 @@ export function HistorySessionsPanel() {
size="icon"
className="h-8 w-8 text-slate-400 hover:text-green-400 hover:bg-slate-800"
onClick={() => handleDownload(s.id)}
title="下载录制"
title={t('audit.terminal.downloadRecord')}
>
<Download className="h-4 w-4" />
</Button>
@@ -167,4 +169,4 @@ export function HistorySessionsPanel() {
<ReplayDialog open={replayOpen} onOpenChange={setReplayOpen} sessionId={replayId} />
</>
);
}
}