refactor(i18n): U32 全量 hash key 清理 — 151 个 hash key → 语义化键 + React Hooks 警告 53→43

- 151 个 hash-style flat key (s+hex 后缀) 全部重命名为语义化 nested key
  - monitor 58 个: alertChannelDialog/alertManager/alertRulesTab/chartRangeHint/
    esIndexPicker/helpers/hostMonitor/logAlertSpanel/logClusterPanel/logDingAssistant/
    logQueryToolbar/logRbac/lokiLabelBrowse/metricsExplorer/middlewareMonit/
    mwOverviewChart/portPolicyDialog/prometheusDashb/queryHistoryPan/savedQueriesPan/
    siteMonitor/snmpMonitor/snmpOverviewChart/zabbixDashboard/zabbixDatasource
  - kubernetes 42 个: deploymentBundle/deploymentEditAdvanced/deploymentEditService/
    deploymentView/deploymentWorkload/eventsPage/kubernetesCluster/namespaceManage/
    namespaceResource/podExecTerminal/podFileBrowser/podLogsViewer/podMetricsPage/
    resourceVisualEditor/workloadViewPage
  - aiops 17 个: agentCliMessage/aiAnalysisResult/analyzerHelpers/assistant/predictUtils
  - agent 12 个: agentAlerts/agentMonitor/hostDetailDialog
  - cloud 13 个: billingRecordDialog/billingRuleDialog/platformManager
  - pipeline 6 个: buildNodesManage/deployRbac/deployTargetField
  - system 3 个: auditSetting
- 58 个源文件中的 147 处 t()/i18n.t() 调用更新为语义化键路径
- 3 个命名空间冲突修复 (monitor.hostMonitor/agent.agentMonitor/aiops.aiAnalysisResult
  原 string 值提升为 .title 子键)
- React Hooks 警告修复 (53→43, -10):
  - exhaustive-deps 4→0: useMemo/useCallback 添加 t 到 deps 数组
  - preserve-manual-memoization 5→0: 变量遮蔽修复 (filter(t)→filter(tg))
  - 1 个冗余 eslint-disable 指令移除 (CronJobManager)
- zh/en 7480=7480 完全对齐, 0 个 hash key 残留
- tsc -b=0, npm build✓ (17.59s), ESLint errors=0/warnings=43/any=0
- go build=0, 17/17 集成测试通过, Docker healthy
This commit is contained in:
attacker committed 2026-07-12 14:35:01 +08:00
1 parent 4f2d279865
commit ff426d908b
63 files changed
+674 -477

No files matched your search

@@ -121,7 +121,7 @@ export function AlertChannelDialog({
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="dg-input max-w-2xl">
<DialogHeader>
<DialogTitle className="text-[var(--dg-text-primary)]"> {i18n.t('monitor.alertchanneldia.s976ad48f')} </DialogTitle>
<DialogTitle className="text-[var(--dg-text-primary)]"> {i18n.t('monitor.alertChannelDialog.title')} </DialogTitle>
<DialogDescription className="dg-text-muted">
管理告警通知渠道(钉钉/企业微信/邮件/短信/Webhook),配置后可在告警规则中引用
</DialogDescription>
@@ -176,7 +176,7 @@ export function AlertChannelDialog({
{ch.test_status === 'success' ? (
<span className="text-[11px] dg-text-green"> {i18n.t('monitor.alertChannels.testPassed')} </span>
) : (
<span className="text-[11px] dg-text-muted"> {i18n.t('monitor.alertchanneldia.s8c852a4c')} </span>
<span className="text-[11px] dg-text-muted"> {i18n.t('monitor.alertChannelDialog.notTested')} </span>
)}
<div className="flex items-center gap-1" onClick={(e) => e.stopPropagation()}>
<Button
@@ -247,7 +247,7 @@ export function AlertChannelDialog({
onCheckedChange={(v) => setChannelForm({ ...channelForm, enabled: v })}
className="data-[state=checked]:bg-emerald-600"
/>
<Label className="dg-text-secondary text-xs"> {i18n.t('monitor.alertchanneldia.sbdab1c59')} </Label>
<Label className="dg-text-secondary text-xs"> {i18n.t('monitor.alertChannelDialog.enableChannel')} </Label>
</div>
)}
</div>
@@ -118,8 +118,8 @@ export function AlertManager() {
>
<Card className="dg-card">
<CardHeader className="pb-2">
<CardTitle className="text-[var(--dg-text-primary)]"> {i18n.t('monitor.alertmanagertsx.s358b05d0')} </CardTitle>
<CardDescription className="dg-text-muted"> {i18n.t('monitor.alertmanagertsx.s615fa6cb')} </CardDescription>
<CardTitle className="text-[var(--dg-text-primary)]"> {i18n.t('monitor.alertManager.trendTitle')} </CardTitle>
<CardDescription className="dg-text-muted"> {i18n.t('monitor.alertManager.last24hStats')} </CardDescription>
</CardHeader>
<CardContent>
<div className="h-[200px]">
@@ -135,7 +135,7 @@ export function AlertRulesTab({
</TableHead>
<TableHead className="dg-text-secondary"> {i18n.t('monitor.ruleName')} </TableHead>
<TableHead className="dg-text-secondary"> {i18n.t('monitor.dashboard.custom.datasource')} </TableHead>
<TableHead className="dg-text-secondary"> {i18n.t('monitor.alertrulestabts.s65939ccd')} </TableHead>
<TableHead className="dg-text-secondary"> {i18n.t('monitor.alertRulesTab.condition')} </TableHead>
<TableHead className="dg-text-secondary"> {i18n.t('kubernetes.event.eventLevel')} </TableHead>
<TableHead className="dg-text-secondary"> {i18n.t('monitor.alertDuration')} </TableHead>
<TableHead className="dg-text-secondary"> {i18n.t('monitor.notificationChannel')} </TableHead>
@@ -42,7 +42,7 @@ export function EsIndexPicker({ datasourceId, value, onChange }: Props) {
onClick={() => setOpen(!open)}
className="h-10 px-2 text-xs rounded flex items-center gap-1 font-mono min-w-[150px] max-w-[220px]"
style={{ background: 'var(--dg-bg-tertiary)', border: '1px solid var(--dg-border)', color: 'var(--dg-text-primary)' }}
title={i18n.t('monitor.esindexpickerts.sa0eff8ea')}
title={i18n.t('monitor.esIndexPicker.selectHint')}
>
<span className="truncate flex-1 text-left">{value || i18n.t('monitor.selectIndex')}</span>
{loading ? <Loader2 className="h-3 w-3 animate-spin shrink-0" /> : <ChevronDown className="h-3 w-3 shrink-0" />}
@@ -60,7 +60,7 @@ export function EsIndexPicker({ datasourceId, value, onChange }: Props) {
<input
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder={i18n.t('monitor.esindexpickerts.s9ae0c004')}
placeholder={i18n.t('monitor.esIndexPicker.searchPlaceholder')}
className="flex-1 bg-transparent text-xs outline-none font-mono"
style={{ color: 'var(--dg-text-primary)' }}
/>
@@ -90,7 +90,7 @@ export function EsIndexPicker({ datasourceId, value, onChange }: Props) {
</button>
))}
{!loading && filtered.length === 0 && (
<p className="text-xs px-2 py-3 text-center" style={{ color: 'var(--dg-text-muted)' }}> {i18n.t('monitor.esindexpickerts.saf5cc132')} </p>
<p className="text-xs px-2 py-3 text-center" style={{ color: 'var(--dg-text-muted)' }}> {i18n.t('monitor.esIndexPicker.noMatch')} </p>
)}
</div>
</div>
@@ -58,7 +58,7 @@ export function LogAlertsPanel() {
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<p className="text-sm text-[var(--dg-text-muted)]"> {i18n.t('monitor.logalertspanelt.s5bc514d8')} </p>
<p className="text-sm text-[var(--dg-text-muted)]"> {i18n.t('monitor.logAlertSpanel.desc')} </p>
<div className="flex gap-2">
<Button variant="outline" size="sm" onClick={load} disabled={loading}>
<RefreshCw className={`h-4 w-4 mr-1 ${loading ? 'animate-spin' : ''}`} />刷新
@@ -42,7 +42,7 @@ export function LogClusterPanel({ clusters }: Props) {
{/* 头部 */}
<div className="flex items-center justify-between px-4 py-2" style={{ background: 'var(--dg-bg-tertiary)', borderBottom: '1px solid var(--dg-border)' }}>
<span className="text-xs" style={{ color: 'var(--dg-text-muted)' }}>共 {clusters.length} 个聚类模式,总计 {total.toLocaleString()} 条日志</span>
<span className="text-[10px]" style={{ color: 'var(--dg-text-muted)' }}> {i18n.t('monitor.logclusterpanel.s38112792')} </span>
<span className="text-[10px]" style={{ color: 'var(--dg-text-muted)' }}> {i18n.t('monitor.logClusterPanel.desc')} </span>
</div>
{/* 聚类列表 */}
@@ -91,7 +91,7 @@ export function LogClusterPanel({ clusters }: Props) {
{/* 展开详情 */}
{expanded[i] && (
<div className="px-4 py-3 animate-fade-in" style={{ background: 'var(--dg-bg-primary)', paddingLeft: '48px' }}>
<p className="text-[10px] mb-1" style={{ color: 'var(--dg-text-muted)' }}> {i18n.t('monitor.logclusterpanel.se909e426')} </p>
<p className="text-[10px] mb-1" style={{ color: 'var(--dg-text-muted)' }}> {i18n.t('monitor.logClusterPanel.sampleLog')} </p>
<div className="p-2 rounded text-[11px] font-mono break-all" style={{ background: 'var(--dg-bg-tertiary)', color: 'var(--dg-text-secondary)' }}>
{cluster.sample}
</div>
@@ -20,10 +20,10 @@ export function LogDingAssistant({ onRuleCreated }: { onRuleCreated?: () => void
size="lg"
className="h-12 rounded-full shadow-lg bg-gradient-to-r from-amber-500 to-orange-500 hover:from-amber-400 hover:to-orange-400 text-white gap-2 px-5"
onClick={() => setOpen(true)}
title={i18n.t('monitor.logdingassistan.s26b79b6b')}
title={i18n.t('monitor.logDingAssistant.addAlert')}
>
<Sparkles className="h-5 w-5" />
<span className="font-medium"> {i18n.t('monitor.logdingassistan.s1a663141')} </span>
<span className="font-medium"> {i18n.t('monitor.logDingAssistant.ding')} </span>
</Button>
</div>
<LogAlertFromQueryDialog
@@ -122,7 +122,7 @@ export function LogQueryToolbar({
</button>
<div className="flex items-center gap-1 ml-1">
<span className="text-[10px]" style={{ color: 'var(--dg-text-muted)' }}> {i18n.t('monitor.logquerytoolbar.s8b76adb2')} </span>
<span className="text-[10px]" style={{ color: 'var(--dg-text-muted)' }}> {i18n.t('monitor.logQueryToolbar.goTo')} </span>
<input
type="number"
value={jumpPage}
@@ -217,7 +217,7 @@ export default function LogRBAC() {
icon={<Shield className="h-10 w-10" />}
title={i18n.t('monitor.logRbac.noPolicies')}
description={i18n.t('monitor.logRbac.noPoliciesDesc')}
action={<Button onClick={() => setCreateOpen(true)}><Plus className="h-4 w-4 mr-1" /> {i18n.t('monitor.logrbactsx.s6058845c')} </Button>}
action={<Button onClick={() => setCreateOpen(true)}><Plus className="h-4 w-4 mr-1" /> {i18n.t('monitor.logRbac.newPermission')} </Button>}
/>
) : (
<div className="space-y-2">
@@ -319,7 +319,7 @@ export default function LogRBAC() {
</div>
{selectedUser ? (
assignments.length === 0 ? (
<p className="text-xs text-[var(--dg-text-muted)]"> {i18n.t('monitor.logrbactsx.sa120fc78')} </p>
<p className="text-xs text-[var(--dg-text-muted)]"> {i18n.t('monitor.logRbac.noPermissionAssigned')} </p>
) : (
<div className="space-y-1.5">
{assignments.map((a) => {
@@ -340,7 +340,7 @@ export default function LogRBAC() {
</div>
)
) : (
<p className="text-xs text-[var(--dg-text-muted)] flex items-center gap-1"><Users className="w-3.5 h-3.5" /> {i18n.t('monitor.logrbactsx.s9be00c95')} </p>
<p className="text-xs text-[var(--dg-text-muted)] flex items-center gap-1"><Users className="w-3.5 h-3.5" /> {i18n.t('monitor.logRbac.selectUserHint')} </p>
)}
</CardContent>
</Card>
@@ -373,7 +373,7 @@ export default function LogRBAC() {
</Badge>
))}
{previewRoleId !== null && previewMatches.length === 0 && (
<span className="text-xs text-[var(--dg-text-muted)]"> {i18n.t('monitor.logrbactsx.s64096f4d')} </span>
<span className="text-xs text-[var(--dg-text-muted)]"> {i18n.t('monitor.logRbac.noMatchHint')} </span>
)}
</div>
</CardContent>
@@ -98,7 +98,7 @@ export function LokiLabelBrowser({ datasourceId, query, timeRange, onQueryChange
))}
</div>
<div className="flex-1 overflow-y-auto max-h-48 p-1">
{!activeLabel && <p className="text-[11px] px-2 py-4 text-center" style={{ color: 'var(--dg-text-muted)' }}> {i18n.t('monitor.lokilabelbrowse.s088b9477')} </p>}
{!activeLabel && <p className="text-[11px] px-2 py-4 text-center" style={{ color: 'var(--dg-text-muted)' }}> {i18n.t('monitor.lokiLabelBrowse.selectLabelHint')} </p>}
{loadingValues && <div className="flex justify-center py-6"><Loader2 className="h-4 w-4 animate-spin" /></div>}
{!loadingValues && activeLabel && values.map((v) => (
<button
@@ -90,8 +90,8 @@ export function QueryHistoryPanel({ visible, onClose, onSelect, onCreateAlert }:
{history.length === 0 ? (
<div className="flex flex-col items-center justify-center h-48 gap-2">
<Clock className="h-8 w-8" style={{ color: 'var(--dg-border)' }} />
<span className="text-xs" style={{ color: 'var(--dg-text-muted)' }}> {i18n.t('monitor.queryhistorypan.s1e60bc17')} </span>
<span className="text-[10px]" style={{ color: 'var(--dg-text-muted)' }}> {i18n.t('monitor.queryhistorypan.s3895021f')} </span>
<span className="text-xs" style={{ color: 'var(--dg-text-muted)' }}> {i18n.t('monitor.queryHistoryPan.emptyHistory')} </span>
<span className="text-[10px]" style={{ color: 'var(--dg-text-muted)' }}> {i18n.t('monitor.queryHistoryPan.autoRecordHint')} </span>
</div>
) : (
<div className="divide-y" style={{ borderColor: 'var(--dg-border)' }}>
@@ -139,7 +139,7 @@ export function QueryHistoryPanel({ visible, onClose, onSelect, onCreateAlert }:
onClick={() => onSelect(item)}
className="h-6 w-6 flex items-center justify-center rounded transition-colors"
style={{ background: 'var(--dg-bg-tertiary)', color: 'var(--dg-green)' }}
title={i18n.t('monitor.queryhistorypan.s4fb36a61')}
title={i18n.t('monitor.queryHistoryPan.runQuery')}
>
<Play className="h-3 w-3" />
</button>
@@ -43,7 +43,7 @@ export function SavedQueriesPanel({ visible, onClose, onSelect }: Props) {
</div>
<div className="flex-1 overflow-y-auto p-3 space-y-2">
{items.length === 0 ? (
<p className="text-xs text-center py-8" style={{ color: 'var(--dg-text-muted)' }}> {i18n.t('monitor.savedqueriespan.s9c8dc62b')} </p>
<p className="text-xs text-center py-8" style={{ color: 'var(--dg-text-muted)' }}> {i18n.t('monitor.savedQueriesPan.emptySaved')} </p>
) : items.map((item) => (
<div
key={item.id}