research: translate detection.tsx UI strings to English

Replace all Chinese text with English equivalents:
- SOURCE_CONFIG labels: 規則->Rule, 融合->Fusion
- SEVERITY_CONFIG labels: 嚴重->Critical, 高->High
- Stats cards: 總警報->Total, 涉及 IP->Unique IPs, etc.
- Controls: 暫停/恢復->Pause/Resume, 監控中/已暫停->Live/Paused
- Filter labels: 全部->All, 來源->Source, 嚴重度->Severity
- AlertDetails: all field labels, section headers, action buttons
- Empty state messages: 尚無警報->No alerts, etc.
- Page title and meta description
- locale: zh-TW -> en-US in formatTimestamp

https://claude.ai/code/session_01Wen51749mAnwBEfh7XmvUw
This commit is contained in:
Claude 2026-05-19 08:37:16 +00:00
parent 9dbc6424a9
commit 7b66c4719e
No known key found for this signature in database

View File

@ -57,13 +57,13 @@ const SOURCE_CONFIG = {
icon: faBrain,
},
Rule: {
label: '規則',
label: 'Rule',
color: '#2563eb',
bgColor: 'rgba(37,99,235,0.10)',
icon: faScroll,
},
Fusion: {
label: '融合',
label: 'Fusion',
color: '#dc2626',
bgColor: 'rgba(220,38,38,0.10)',
icon: faLayerGroup,
@ -72,13 +72,13 @@ const SOURCE_CONFIG = {
const SEVERITY_CONFIG = {
Critical: {
label: '嚴重',
label: 'Critical',
color: '#dc2626',
bgColor: 'rgba(220,38,38,0.10)',
icon: faExclamationTriangle,
},
High: {
label: '',
label: 'High',
color: '#d97706',
bgColor: 'rgba(217,119,6,0.10)',
icon: faExclamationTriangle,
@ -97,7 +97,7 @@ const formatProtocol = (proto: number): string =>
PROTOCOL_MAP[proto] ?? `Proto(${proto})`
const formatTimestamp = (unix: number): string =>
new Date(unix * 1000).toLocaleString('zh-TW', {
new Date(unix * 1000).toLocaleString('en-US', {
month: '2-digit',
day: '2-digit',
hour: '2-digit',
@ -266,8 +266,8 @@ const AlertDetails: React.FC<AlertDetailsProps> = ({ alert, onClose, showNotific
putData(
url,
`${formatted}:${portStr}`,
() => showNotification(`已成功添加到黑名單: ${ip}:${blockAll ? '*' : port}`, 'success'),
(err: any) => showNotification(`添加到黑名單失敗: ${err.message}`, 'error')
() => showNotification(`Added to blocklist: ${ip}:${blockAll ? '*' : port}`, 'success'),
(err: any) => showNotification(`Failed to add to blocklist: ${err.message}`, 'error')
)
},
[isIPv6Src, isIPv6Dst, showNotification]
@ -284,8 +284,8 @@ const AlertDetails: React.FC<AlertDetailsProps> = ({ alert, onClose, showNotific
putData(
url,
`${formatted}:${portStr}`,
() => showNotification(`已成功添加到白名單: ${ip}:${allowAll ? '*' : port}`, 'success'),
(err: any) => showNotification(`添加到白名單失敗: ${err.message}`, 'error')
() => showNotification(`Added to allowlist: ${ip}:${allowAll ? '*' : port}`, 'success'),
(err: any) => showNotification(`Failed to add to allowlist: ${err.message}`, 'error')
)
},
[isIPv6Src, isIPv6Dst, showNotification]
@ -302,28 +302,28 @@ const AlertDetails: React.FC<AlertDetailsProps> = ({ alert, onClose, showNotific
onClick={() => handleBlockIP(ip, port, isSource)}
>
<FontAwesomeIcon icon={faFilter} className="mr-1" />
:{port}
Block :{port}
</button>
<button
className="px-3 py-2 bg-red-700 text-white text-xs rounded-lg hover:bg-red-800 transition-colors"
onClick={() => handleBlockIP(ip, port, isSource, true)}
>
<FontAwesomeIcon icon={faFilter} className="mr-1" />
Block All
</button>
<button
className="px-3 py-2 bg-green-600 text-white text-xs rounded-lg hover:bg-green-700 transition-colors"
onClick={() => handleAllowIP(ip, port, isSource)}
>
<FontAwesomeIcon icon={faCheck} className="mr-1" />
:{port}
Allow :{port}
</button>
<button
className="px-3 py-2 bg-green-700 text-white text-xs rounded-lg hover:bg-green-800 transition-colors"
onClick={() => handleAllowIP(ip, port, isSource, true)}
>
<FontAwesomeIcon icon={faCheck} className="mr-1" />
Allow All
</button>
</div>
</div>
@ -340,7 +340,7 @@ const AlertDetails: React.FC<AlertDetailsProps> = ({ alert, onClose, showNotific
<div className="flex items-center justify-between mb-6">
<h3 className="text-xl font-semibold text-gray-900 flex items-center">
<FontAwesomeIcon icon={faEye} className="mr-2 text-blue-600" />
Alert Details
</h3>
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 transition-colors">
<FontAwesomeIcon icon={faTimes} />
@ -350,11 +350,11 @@ const AlertDetails: React.FC<AlertDetailsProps> = ({ alert, onClose, showNotific
{/* Basic metadata */}
<div className="space-y-3 mb-6">
<div className="flex justify-between items-center">
<span className="text-sm font-medium text-gray-600">:</span>
<span className="text-sm font-medium text-gray-600">Time:</span>
<span className="text-sm text-gray-900">{formatTimestamp(alert.timestamp)}</span>
</div>
<div className="flex justify-between items-center">
<span className="text-sm font-medium text-gray-600">:</span>
<span className="text-sm font-medium text-gray-600">Source:</span>
<span
className="inline-flex items-center px-2 py-1 text-xs font-medium rounded-full"
style={{ backgroundColor: srcCfg.bgColor, color: srcCfg.color }}
@ -364,7 +364,7 @@ const AlertDetails: React.FC<AlertDetailsProps> = ({ alert, onClose, showNotific
</span>
</div>
<div className="flex justify-between items-center">
<span className="text-sm font-medium text-gray-600">:</span>
<span className="text-sm font-medium text-gray-600">Severity:</span>
<span
className="inline-flex items-center px-2 py-1 text-xs font-medium rounded-full"
style={{ backgroundColor: sevCfg.bgColor, color: sevCfg.color }}
@ -374,7 +374,7 @@ const AlertDetails: React.FC<AlertDetailsProps> = ({ alert, onClose, showNotific
</span>
</div>
<div className="flex justify-between items-center">
<span className="text-sm font-medium text-gray-600">:</span>
<span className="text-sm font-medium text-gray-600">Protocol:</span>
<span className="text-sm font-mono text-gray-900">{formatProtocol(alert.protocol)}</span>
</div>
</div>
@ -383,7 +383,7 @@ const AlertDetails: React.FC<AlertDetailsProps> = ({ alert, onClose, showNotific
{(alert.attack_type || alert.rule_msg) && (
<div className="mb-6">
<span className="text-sm font-medium text-gray-600">
{alert.source === 'Ml' ? '攻擊類型' : '規則訊息'}:
{alert.source === 'Ml' ? 'Attack Type' : 'Rule Message'}:
</span>
<div className="mt-1 p-3 bg-gray-50 rounded-lg">
<p className="text-sm text-gray-900">{alert.attack_type ?? alert.rule_msg}</p>
@ -394,9 +394,9 @@ const AlertDetails: React.FC<AlertDetailsProps> = ({ alert, onClose, showNotific
{/* ML inference metrics */}
{(alert.source === 'Ml' || alert.source === 'Fusion') && (
<div className="mb-6 space-y-3">
<h4 className="text-sm font-semibold text-gray-700">AI </h4>
<h4 className="text-sm font-semibold text-gray-700">ML Inference Metrics</h4>
<div className="flex justify-between items-center">
<span className="text-sm text-gray-600">:</span>
<span className="text-sm text-gray-600">Confidence:</span>
<div className="flex items-center space-x-2">
<div className="w-24 bg-gray-200 rounded-full h-2">
<div
@ -422,7 +422,7 @@ const AlertDetails: React.FC<AlertDetailsProps> = ({ alert, onClose, showNotific
{/* Rule SID */}
{(alert.source === 'Rule' || alert.source === 'Fusion') && alert.rule_sid !== null && (
<div className="mb-6">
<h4 className="text-sm font-semibold text-gray-700 mb-2"></h4>
<h4 className="text-sm font-semibold text-gray-700 mb-2">Rule Info</h4>
<div className="flex justify-between items-center">
<span className="text-sm text-gray-600">SID:</span>
<span className="text-sm font-mono text-gray-900">{alert.rule_sid}</span>
@ -432,16 +432,16 @@ const AlertDetails: React.FC<AlertDetailsProps> = ({ alert, onClose, showNotific
{/* Connection */}
<div className="mb-6">
<h4 className="text-sm font-semibold text-gray-700 mb-3"></h4>
<h4 className="text-sm font-semibold text-gray-700 mb-3">Connection Details</h4>
<div className="flex items-center justify-between bg-gray-50 rounded-lg p-4">
<div className="text-center">
<div className="text-xs font-medium text-gray-500"></div>
<div className="text-xs font-medium text-gray-500">Source</div>
<div className="text-sm font-semibold text-blue-600">{alert.src_ip}</div>
<div className="text-xs text-gray-500">:{alert.src_port}</div>
</div>
<FontAwesomeIcon icon={faArrowRight} className="text-gray-400" />
<div className="text-center">
<div className="text-xs font-medium text-gray-500"></div>
<div className="text-xs font-medium text-gray-500">Destination</div>
<div className="text-sm font-semibold text-red-600">{alert.dst_ip}</div>
<div className="text-xs text-gray-500">:{alert.dst_port}</div>
</div>
@ -450,9 +450,9 @@ const AlertDetails: React.FC<AlertDetailsProps> = ({ alert, onClose, showNotific
{/* IP management */}
<div className="space-y-4">
<h4 className="text-sm font-semibold text-gray-700">IP </h4>
<IpActions ip={alert.src_ip} port={alert.src_port} isSource={true} label="來源 IP" bg="bg-blue-50" />
<IpActions ip={alert.dst_ip} port={alert.dst_port} isSource={false} label="目標 IP" bg="bg-red-50" />
<h4 className="text-sm font-semibold text-gray-700">IP Management</h4>
<IpActions ip={alert.src_ip} port={alert.src_port} isSource={true} label="Source IP" bg="bg-blue-50" />
<IpActions ip={alert.dst_ip} port={alert.dst_port} isSource={false} label="Destination IP" bg="bg-red-50" />
</div>
</motion.div>
</AnimatePresence>
@ -547,7 +547,7 @@ const Detection: React.FC = () => {
if (isLoading) {
return (
<>
<Head><title> - NetGuardia</title></Head>
<Head><title>Threat Detection - NetGuardia</title></Head>
<Layout>
<div className="flex items-center justify-center w-full h-full min-h-[calc(100vh-4rem)]">
<LoadingSpinner />
@ -560,8 +560,8 @@ const Detection: React.FC = () => {
return (
<>
<Head>
<title> - NetGuardia</title>
<meta name="description" content="NetGuardia 統一威脅檢測系統" />
<title>Threat Detection - NetGuardia</title>
<meta name="description" content="NetGuardia Unified Threat Detection" />
</Head>
<Layout>
@ -579,22 +579,22 @@ const Detection: React.FC = () => {
<motion.div initial={{ opacity: 0, y: -20 }} animate={{ opacity: 1, y: 0 }} className="mb-6">
<h1 className="text-3xl font-bold text-gray-900 mb-1 flex items-center">
<FontAwesomeIcon icon={faShieldAlt} className="mr-3 text-blue-600" />
Threat Detection
</h1>
<p className="text-gray-500 text-sm">AI </p>
<p className="text-gray-500 text-sm">Real-time threat monitoring powered by ML and rule engine</p>
</motion.div>
{/* Stats cards */}
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-7 gap-4 mb-6">
{(
[
{ label: '總警報', value: stats.total, color: '#2563eb', icon: faList },
{ label: '嚴重', value: stats.critical, color: '#dc2626', icon: faExclamationTriangle },
{ label: '', value: stats.high, color: '#d97706', icon: faExclamationTriangle },
{ label: 'AI', value: stats.ml, color: '#7c3aed', icon: faBrain },
{ label: '規則', value: stats.rule, color: '#2563eb', icon: faScroll },
{ label: '融合', value: stats.fusion, color: '#dc2626', icon: faLayerGroup },
{ label: '涉及 IP', value: stats.uniqueIPs, color: '#059669', icon: faGlobe },
{ label: 'Total', value: stats.total, color: '#2563eb', icon: faList },
{ label: 'Critical', value: stats.critical, color: '#dc2626', icon: faExclamationTriangle },
{ label: 'High', value: stats.high, color: '#d97706', icon: faExclamationTriangle },
{ label: 'ML', value: stats.ml, color: '#7c3aed', icon: faBrain },
{ label: 'Rule', value: stats.rule, color: '#2563eb', icon: faScroll },
{ label: 'Fusion', value: stats.fusion, color: '#dc2626', icon: faLayerGroup },
{ label: 'Unique IPs', value: stats.uniqueIPs, color: '#059669', icon: faGlobe },
] as const
).map(({ label, value, color, icon }, i) => (
<motion.div
@ -632,7 +632,7 @@ const Detection: React.FC = () => {
}`}
>
<FontAwesomeIcon icon={isPaused ? faPlay : faPause} />
{isPaused ? '恢復' : '暫停'}
{isPaused ? 'Resume' : 'Pause'}
</button>
{/* Live indicator */}
@ -643,7 +643,7 @@ const Detection: React.FC = () => {
}`}
/>
<span className={`text-xs font-medium ${isPaused ? 'text-orange-600' : 'text-green-600'}`}>
{isPaused ? '已暫停' : '監控中'}
{isPaused ? 'Paused' : 'Live'}
</span>
</div>
@ -651,7 +651,7 @@ const Detection: React.FC = () => {
{/* Source filter */}
<div className="flex items-center gap-1.5">
<span className="text-xs text-gray-500">:</span>
<span className="text-xs text-gray-500">Source:</span>
{(['all', 'Ml', 'Rule', 'Fusion'] as const).map(s => (
<FilterButton
key={s}
@ -664,7 +664,7 @@ const Detection: React.FC = () => {
: '#dc2626'
}
>
{s === 'all' ? '全部' : SOURCE_CONFIG[s].label}
{s === 'all' ? 'All' : SOURCE_CONFIG[s].label}
</FilterButton>
))}
</div>
@ -673,7 +673,7 @@ const Detection: React.FC = () => {
{/* Severity filter */}
<div className="flex items-center gap-1.5">
<span className="text-xs text-gray-500">:</span>
<span className="text-xs text-gray-500">Severity:</span>
{(['all', 'Critical', 'High'] as const).map(s => (
<FilterButton
key={s}
@ -683,13 +683,13 @@ const Detection: React.FC = () => {
s === 'all' ? '#374151' : s === 'Critical' ? '#dc2626' : '#d97706'
}
>
{s === 'all' ? '全部' : SEVERITY_CONFIG[s].label}
{s === 'all' ? 'All' : SEVERITY_CONFIG[s].label}
</FilterButton>
))}
</div>
<div className="ml-auto text-xs text-gray-400">
{filteredAlerts.length} / {alerts.length}
{filteredAlerts.length} / {alerts.length} alerts
</div>
</div>
</motion.div>
@ -706,9 +706,9 @@ const Detection: React.FC = () => {
<div className="px-5 py-3 border-b border-gray-100 flex items-center justify-between">
<h2 className="text-base font-semibold text-gray-900 flex items-center gap-2">
<FontAwesomeIcon icon={faShieldAlt} className="text-blue-600" />
Security Alerts
</h2>
<span className="text-xs text-gray-400">{filteredAlerts.length} </span>
<span className="text-xs text-gray-400">{filteredAlerts.length} alerts</span>
</div>
<div className="max-h-[620px] overflow-y-auto">
@ -716,12 +716,12 @@ const Detection: React.FC = () => {
<div className="p-12 text-center">
<FontAwesomeIcon icon={faShieldAlt} className="text-5xl text-gray-200 mb-4" />
<h3 className="text-lg font-semibold text-gray-600 mb-1">
{alerts.length === 0 ? '尚無警報' : '無符合條件的警報'}
{alerts.length === 0 ? 'No alerts' : 'No matching alerts'}
</h3>
<p className="text-sm text-gray-400">
{alerts.length === 0
? '系統偵測到威脅時將在此顯示'
: '請調整過濾條件'}
? 'Alerts will appear here when threats are detected'
: 'Try adjusting the filters'}
</p>
</div>
) : (