refactor(i18n): U29 phantom key 全项目清零 — 28 个 phantom key → 0
AlertRuleDialog.tsx (9处) + DeployTargetFields.tsx (3处) + HealthProbeSection.tsx (2处+1处硬编码) + PodLogsViewer.tsx (2处终端ANSI字符串) + AlertChannelDialog.tsx (2处) + PodMetricsPage.tsx/CapacityPage.tsx (各1处) + PodExecTerminal.tsx (1处终端ANSI) + BuildNodesManager.tsx (1处) + HostDetailDialog.tsx (1处)
修复类型:
- 批量正则损坏的 i18n.t('>中文 <span className=') → 语义化键
- 终端 ANSI 转义字符串 (PodLogsViewer/PodExecTerminal) → 移除 i18n.t() 包裹改纯字符串
- 硬编码中文 (取消/通知渠道提示/日志数据源提示) → i18n.t() 调用
新增 16 个翻译键 (monitor.alertRuleDialog2.* + monitor.alertChannel.name + agent.portCount + monitor.lbl.noProbeHint)
zh/en 7358→7375 +17 键对齐
phantom key 28→0 (全项目零 phantom key)
tsc -b=0, npm build✓ (17.00s), ESLint=0/53/0, go build=0, 17/17 测试通过
This commit is contained in:
1 parent
b2715ca3bd
commit
064b6c4ec2
12 files changed
+71
-37
No files matched your search
@@ -94,7 +94,7 @@ export function HostDetailDialog({
|
|||||||
<InfoCell label={i18n.t('agent.agentIp')} value={host.ip || '--'} />
|
<InfoCell label={i18n.t('agent.agentIp')} value={host.ip || '--'} />
|
||||||
<InfoCell label={i18n.t('agent.agentOs')} value={host.os || host.platform || '--'} />
|
<InfoCell label={i18n.t('agent.agentOs')} value={host.os || host.platform || '--'} />
|
||||||
<InfoCell label={i18n.t('agent.agentLastSeen')} value={timeAgo(host.last_report || host.last_reported_at || host.updated_at)} />
|
<InfoCell label={i18n.t('agent.agentLastSeen')} value={timeAgo(host.last_report || host.last_reported_at || host.updated_at)} />
|
||||||
<InfoCell label={i18n.t('端口数')} value={String(ports.length)} />
|
<InfoCell label={i18n.t('agent.portCount')} value={String(ports.length)} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 趋势图 */}
|
{/* 趋势图 */}
|
||||||
|
|||||||
@@ -293,7 +293,7 @@ export default function BuildNodesManager({ embedded = false }: { embedded?: boo
|
|||||||
<Input value={name} onChange={(e) => setName(e.target.value)} className="h-8 text-xs dg-input" placeholder="build-node-01" />
|
<Input value={name} onChange={(e) => setName(e.target.value)} className="h-8 text-xs dg-input" placeholder="build-node-01" />
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<Label className="text-xs"> {i18n.t('>标签 <span className=')} </Label>
|
<Label className="text-xs"> {i18n.t('common.tag')} </Label>
|
||||||
<Select value={serverId || '__none'} onValueChange={(v) => setServerId(v === '__none' ? '' : v)}>
|
<Select value={serverId || '__none'} onValueChange={(v) => setServerId(v === '__none' ? '' : v)}>
|
||||||
<SelectTrigger className="h-8 text-xs dg-input"><SelectValue placeholder={i18n.t('deploy.buildNodes.selectServer')} /></SelectTrigger>
|
<SelectTrigger className="h-8 text-xs dg-input"><SelectValue placeholder={i18n.t('deploy.buildNodes.selectServer')} /></SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ export function DeployTargetFields({ value, onChange, requireTarget = true }: De
|
|||||||
<p className="text-[10px] text-[var(--dg-text-muted)]"> {i18n.t('pipeline.deploytargetfie.sb2b6db98')} </p>
|
<p className="text-[10px] text-[var(--dg-text-muted)]"> {i18n.t('pipeline.deploytargetfie.sb2b6db98')} </p>
|
||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="grid grid-cols-2 gap-2">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<Label className="text-xs"> {i18n.t('>容器名 <span className=')} </Label>
|
<Label className="text-xs"> {i18n.t('kubernetes.container.containerName')} </Label>
|
||||||
<Input
|
<Input
|
||||||
value={value.deployRemotePath}
|
value={value.deployRemotePath}
|
||||||
onChange={(e) => onChange({ deployRemotePath: e.target.value })}
|
onChange={(e) => onChange({ deployRemotePath: e.target.value })}
|
||||||
@@ -254,7 +254,7 @@ export function DeployTargetFields({ value, onChange, requireTarget = true }: De
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<Label className="text-xs"> {i18n.t('>容器名 <span className=')} </Label>
|
<Label className="text-xs"> {i18n.t('kubernetes.container.containerName')} </Label>
|
||||||
<Input
|
<Input
|
||||||
value={value.deployScript}
|
value={value.deployScript}
|
||||||
onChange={(e) => onChange({ deployScript: e.target.value })}
|
onChange={(e) => onChange({ deployScript: e.target.value })}
|
||||||
@@ -292,7 +292,7 @@ export function DeployTargetFields({ value, onChange, requireTarget = true }: De
|
|||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<Label className="text-xs"> {i18n.t('>容器名 <span className=')} </Label>
|
<Label className="text-xs"> {i18n.t('kubernetes.container.containerName')} </Label>
|
||||||
<Input
|
<Input
|
||||||
readOnly
|
readOnly
|
||||||
value={value.k8sDeployment ? `${value.k8sNamespace}/${value.k8sDeployment}` : ''}
|
value={value.k8sDeployment ? `${value.k8sNamespace}/${value.k8sDeployment}` : ''}
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ export default function CapacityPage() {
|
|||||||
</TableRow></TableHeader>
|
</TableRow></TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{rows.length === 0 ? (
|
{rows.length === 0 ? (
|
||||||
<TableRow><TableCell colSpan={8} className="text-center dg-text-muted py-8">{i18n.t('>暂无数据{loading ? <span className=')}</TableCell></TableRow>
|
<TableRow><TableCell colSpan={8} className="text-center dg-text-muted py-8">{i18n.t('common.noData')}</TableCell></TableRow>
|
||||||
) : rows.map((r, i) => (
|
) : rows.map((r, i) => (
|
||||||
<TableRow key={`${r.deployment}-${r.container}-${i}`} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-hover)]">
|
<TableRow key={`${r.deployment}-${r.container}-${i}`} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-hover)]">
|
||||||
<TableCell className="font-medium text-[var(--dg-text-primary)] text-sm">{r.deployment}</TableCell>
|
<TableCell className="font-medium text-[var(--dg-text-primary)] text-sm">{r.deployment}</TableCell>
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ export function PodExecTerminal({
|
|||||||
console.error('[PodExec:connect] WS error event fired, readyState=', ws.readyState, 'type=', ev.type);
|
console.error('[PodExec:connect] WS error event fired, readyState=', ws.readyState, 'type=', ev.type);
|
||||||
setConnState('error');
|
setConnState('error');
|
||||||
setHasOutput(true);
|
setHasOutput(true);
|
||||||
term.write(i18n.t('\\r\\n\\x1b[31m[连接发生错误]\\x1b[0m\\r\\n'));
|
term.write('\r\n\x1b[31m[连接发生错误]\x1b[0m\r\n');
|
||||||
};
|
};
|
||||||
}, [clusterId, namespace, podName, container, shell]);
|
}, [clusterId, namespace, podName, container, shell]);
|
||||||
|
|
||||||
|
|||||||
@@ -183,11 +183,11 @@ export function PodLogsViewer({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
flush();
|
flush();
|
||||||
appendLines([i18n.t('\\x1b[33m— 流式结束 —\\x1b[0m')]);
|
appendLines(['\x1b[33m— 流式结束 —\x1b[0m']);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const errName = (e as { name?: string })?.name;
|
const errName = (e as { name?: string })?.name;
|
||||||
if (errName === 'AbortError') {
|
if (errName === 'AbortError') {
|
||||||
appendLines([i18n.t('\\x1b[33m— 已停止 —\\x1b[0m')]);
|
appendLines(['\x1b[33m— 已停止 —\x1b[0m']);
|
||||||
} else {
|
} else {
|
||||||
const msg = e instanceof Error ? e.message : String(e);
|
const msg = e instanceof Error ? e.message : String(e);
|
||||||
appendLines([`\x1b[31m[流式连接中断: ${msg}]\x1b[0m`]);
|
appendLines([`\x1b[31m[流式连接中断: ${msg}]\x1b[0m`]);
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ export default function PodMetricsPage() {
|
|||||||
</TableRow></TableHeader>
|
</TableRow></TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{rows.length === 0 ? (
|
{rows.length === 0 ? (
|
||||||
<TableRow><TableCell colSpan={5} className="text-center dg-text-muted py-8">{i18n.t('>暂无数据{loading ? <span className=')}</TableCell></TableRow>
|
<TableRow><TableCell colSpan={5} className="text-center dg-text-muted py-8">{i18n.t('common.noData')}</TableCell></TableRow>
|
||||||
) : rows.map((r, i) => (
|
) : rows.map((r, i) => (
|
||||||
<TableRow key={`${r.pod}-${r.container}-${i}`} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-hover)]">
|
<TableRow key={`${r.pod}-${r.container}-${i}`} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-hover)]">
|
||||||
<TableCell className="font-medium text-[var(--dg-text-primary)] text-sm">{r.pod}</TableCell>
|
<TableCell className="font-medium text-[var(--dg-text-primary)] text-sm">{r.pod}</TableCell>
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ export function AlertChannelDialog({
|
|||||||
{/* 渠道列表 */}
|
{/* 渠道列表 */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<Label className="text-[var(--dg-text-secondary)] text-sm"> {i18n.t('>渠道名称 <span className=')} </Label>
|
<Label className="text-[var(--dg-text-secondary)] text-sm"> {i18n.t('monitor.alertChannel.name')} </Label>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -210,7 +210,7 @@ export function AlertChannelDialog({
|
|||||||
{editingChannel ? i18n.t('monitor.alertChannelDialog2.editChannel') : i18n.t('monitor.alertChannelDialog2.newChannel')}
|
{editingChannel ? i18n.t('monitor.alertChannelDialog2.editChannel') : i18n.t('monitor.alertChannelDialog2.newChannel')}
|
||||||
</Label>
|
</Label>
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<Label className="dg-text-secondary text-xs"> {i18n.t('>渠道名称 <span className=')} <span className="dg-text-red">*</span></Label>
|
<Label className="dg-text-secondary text-xs"> {i18n.t('monitor.alertChannel.name')} <span className="dg-text-red">*</span></Label>
|
||||||
<Input
|
<Input
|
||||||
value={channelForm.name}
|
value={channelForm.name}
|
||||||
onChange={(e) => setChannelForm({ ...channelForm, name: e.target.value })}
|
onChange={(e) => setChannelForm({ ...channelForm, name: e.target.value })}
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ export function AlertRuleDialog({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<Label className="text-[var(--dg-text-secondary)]"> {i18n.t('aiops.llmModelDesc')} </Label>
|
<Label className="text-[var(--dg-text-secondary)]"> {i18n.t('monitor.alertRuleDialog2.ruleDesc')} </Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
value={ruleForm.description}
|
value={ruleForm.description}
|
||||||
onChange={(e) => setRuleForm({ ...ruleForm, description: e.target.value })}
|
onChange={(e) => setRuleForm({ ...ruleForm, description: e.target.value })}
|
||||||
@@ -159,7 +159,7 @@ export function AlertRuleDialog({
|
|||||||
|
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<Label className="text-[var(--dg-text-secondary)]"> {i18n.t('>日志数据源 <span className=')} </Label>
|
<Label className="text-[var(--dg-text-secondary)]"> {i18n.t('monitor.alertRuleDialog2.alertType')} </Label>
|
||||||
<select
|
<select
|
||||||
value={ruleForm.type}
|
value={ruleForm.type}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -169,9 +169,9 @@ export function AlertRuleDialog({
|
|||||||
}}
|
}}
|
||||||
className="w-full h-9 px-3 rounded-md bg-[var(--dg-bg-tertiary)]/50 border border-[var(--dg-border)] text-[var(--dg-text-primary)] text-sm"
|
className="w-full h-9 px-3 rounded-md bg-[var(--dg-bg-tertiary)]/50 border border-[var(--dg-border)] text-[var(--dg-text-primary)] text-sm"
|
||||||
>
|
>
|
||||||
<option value="metric"> {i18n.t('>请先前往「日志库」添加 {ruleForm.source === ')} </option>
|
<option value="metric"> {i18n.t('monitor.alertRuleDialog2.metricAlert')} </option>
|
||||||
<option value="log"> {i18n.t('monitor.alertHelper.srcLog')} </option>
|
<option value="log"> {i18n.t('monitor.alertHelper.srcLog')} </option>
|
||||||
<option value="event"> {i18n.t('>请先前往「日志库」添加 {ruleForm.source === ')} </option>
|
<option value="event"> {i18n.t('monitor.alertRuleDialog2.eventAlert')} </option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
@@ -199,25 +199,25 @@ export function AlertRuleDialog({
|
|||||||
|
|
||||||
{ruleForm.type === 'log' && (
|
{ruleForm.type === 'log' && (
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<Label className="text-[var(--dg-text-secondary)]"> {i18n.t('>日志数据源 <span className=')} <span className="dg-text-red">*</span></Label>
|
<Label className="text-[var(--dg-text-secondary)]"> {i18n.t('monitor.alertRuleDialog2.logDatasource')} <span className="dg-text-red">*</span></Label>
|
||||||
<select
|
<select
|
||||||
value={ruleForm.datasource_id || ''}
|
value={ruleForm.datasource_id || ''}
|
||||||
onChange={(e) => setRuleForm({ ...ruleForm, datasource_id: Number(e.target.value) || undefined })}
|
onChange={(e) => setRuleForm({ ...ruleForm, datasource_id: Number(e.target.value) || undefined })}
|
||||||
className="w-full h-9 px-3 rounded-md bg-[var(--dg-bg-tertiary)]/50 border border-[var(--dg-border)] text-[var(--dg-text-primary)] text-sm"
|
className="w-full h-9 px-3 rounded-md bg-[var(--dg-bg-tertiary)]/50 border border-[var(--dg-border)] text-[var(--dg-text-primary)] text-sm"
|
||||||
>
|
>
|
||||||
<option value=""> {i18n.t('PromQL 或指标表达式,例如 cpu_usage > 80')} </option>
|
<option value=""> {i18n.t('monitor.alertRuleDialog2.selectLogSource')} </option>
|
||||||
{logSources.map((s) => (
|
{logSources.map((s) => (
|
||||||
<option key={s.id} value={s.id}>{s.name}{s.is_default ? i18n.t('monitor.logPanel.defaultSuffix') : ''}</option>
|
<option key={s.id} value={s.id}>{s.name}{s.is_default ? i18n.t('monitor.logPanel.defaultSuffix') : ''}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{logSources.length === 0 && (
|
{logSources.length === 0 && (
|
||||||
<p className="text-xs dg-text-yellow/80">请先前往「日志库」添加 {ruleForm.source === 'loki' ? 'Loki' : 'Elasticsearch'} 数据源</p>
|
<p className="text-xs dg-text-yellow/80">{i18n.t('monitor.alertRuleDialog2.addLogSourceFirst', { type: ruleForm.source === 'loki' ? 'Loki' : 'Elasticsearch' })}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<Label className="text-[var(--dg-text-secondary)]"> {i18n.t('>规则名称 <span className=')} <span className="dg-text-red">*</span></Label>
|
<Label className="text-[var(--dg-text-secondary)]"> {i18n.t('monitor.alertRuleDialog2.expression')} <span className="dg-text-red">*</span></Label>
|
||||||
<Input
|
<Input
|
||||||
value={ruleForm.expression}
|
value={ruleForm.expression}
|
||||||
onChange={(e) => setRuleForm({ ...ruleForm, expression: e.target.value })}
|
onChange={(e) => setRuleForm({ ...ruleForm, expression: e.target.value })}
|
||||||
@@ -226,14 +226,14 @@ export function AlertRuleDialog({
|
|||||||
/>
|
/>
|
||||||
<p className="text-xs dg-text-muted">
|
<p className="text-xs dg-text-muted">
|
||||||
{ruleForm.type === 'log'
|
{ruleForm.type === 'log'
|
||||||
? (ruleForm.source === 'loki' ? i18n.t('LogQL 查询或 count_over_time 表达式') : i18n.t('Elasticsearch Lucene 查询'))
|
? (ruleForm.source === 'loki' ? i18n.t('monitor.alertRuleDialog2.logqlHint') : i18n.t('monitor.alertRuleDialog2.esHint'))
|
||||||
: i18n.t('PromQL 或指标表达式,例如 cpu_usage > 80')}
|
: i18n.t('monitor.alertRuleDialog2.promqlHint')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<Label className="text-[var(--dg-text-secondary)]"> {i18n.t('>日志数据源 <span className=')} </Label>
|
<Label className="text-[var(--dg-text-secondary)]"> {i18n.t('monitor.alertRuleDialog2.severity')} </Label>
|
||||||
<select
|
<select
|
||||||
value={ruleForm.severity}
|
value={ruleForm.severity}
|
||||||
onChange={(e) => setRuleForm({ ...ruleForm, severity: e.target.value })}
|
onChange={(e) => setRuleForm({ ...ruleForm, severity: e.target.value })}
|
||||||
@@ -245,7 +245,7 @@ export function AlertRuleDialog({
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<Label className="text-[var(--dg-text-secondary)]"> {i18n.t('>日志数据源 <span className=')} </Label>
|
<Label className="text-[var(--dg-text-secondary)]"> {i18n.t('monitor.alertRuleDialog2.duration')} </Label>
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
value={ruleForm.duration}
|
value={ruleForm.duration}
|
||||||
@@ -259,7 +259,7 @@ export function AlertRuleDialog({
|
|||||||
<Label className="text-[var(--dg-text-secondary)]"> {i18n.t('monitor.notificationChannel')} </Label>
|
<Label className="text-[var(--dg-text-secondary)]"> {i18n.t('monitor.notificationChannel')} </Label>
|
||||||
{notifyChannels.length === 0 ? (
|
{notifyChannels.length === 0 ? (
|
||||||
<div className="text-xs dg-text-yellow/80 bg-amber-500/5 border border-amber-500/20 rounded-md px-3 py-2">
|
<div className="text-xs dg-text-yellow/80 bg-amber-500/5 border border-amber-500/20 rounded-md px-3 py-2">
|
||||||
暂无已配置的通知渠道,请先在「通知渠道配置」中添加
|
{i18n.t('monitor.alertRuleDialog2.noChannels')}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-wrap gap-3 pt-1">
|
<div className="flex flex-wrap gap-3 pt-1">
|
||||||
@@ -295,7 +295,7 @@ export function AlertRuleDialog({
|
|||||||
className="border-[var(--dg-border)] text-[var(--dg-text-secondary)] hover:bg-[var(--dg-bg-hover)]"
|
className="border-[var(--dg-border)] text-[var(--dg-text-secondary)] hover:bg-[var(--dg-bg-hover)]"
|
||||||
onClick={() => onOpenChange(false)}
|
onClick={() => onOpenChange(false)}
|
||||||
>
|
>
|
||||||
取消
|
{i18n.t('common.cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className="bg-blue-600 hover:bg-blue-700 text-white"
|
className="bg-blue-600 hover:bg-blue-700 text-white"
|
||||||
|
|||||||
@@ -4192,7 +4192,8 @@
|
|||||||
"typeSms": "SMS",
|
"typeSms": "SMS",
|
||||||
"typeWebhook": "Webhook",
|
"typeWebhook": "Webhook",
|
||||||
"typeWechat": "WeCom",
|
"typeWechat": "WeCom",
|
||||||
"updated": "Channel updated"
|
"updated": "Channel updated",
|
||||||
|
"name": "Channel Name"
|
||||||
},
|
},
|
||||||
"alertChannels": {
|
"alertChannels": {
|
||||||
"addChannel": "Add Channel",
|
"addChannel": "Add Channel",
|
||||||
@@ -4905,12 +4906,27 @@
|
|||||||
"siteClientError": "Client/Auth Error",
|
"siteClientError": "Client/Auth Error",
|
||||||
"siteRequestFailed": "Request Failed",
|
"siteRequestFailed": "Request Failed",
|
||||||
"siteRequestTimeout": "Request Timeout",
|
"siteRequestTimeout": "Request Timeout",
|
||||||
"siteConnFailed": "Connection Failed"
|
"siteConnFailed": "Connection Failed",
|
||||||
|
"noProbeHint": "No health probes. Click \"{{action}}\" to create one."
|
||||||
},
|
},
|
||||||
"alertRuleDialog2": {
|
"alertRuleDialog2": {
|
||||||
"editRule": "Edit Alert Rule",
|
"editRule": "Edit Alert Rule",
|
||||||
"newRule": "New Alert Rule",
|
"newRule": "New Alert Rule",
|
||||||
"namePlaceholder": "e.g., CPU Usage Alert"
|
"namePlaceholder": "e.g., CPU Usage Alert",
|
||||||
|
"alertType": "Alert Type",
|
||||||
|
"metricAlert": "Metric Alert",
|
||||||
|
"eventAlert": "Event Alert",
|
||||||
|
"logDatasource": "Log Data Source",
|
||||||
|
"selectLogSource": "Select a log source",
|
||||||
|
"expression": "Expression",
|
||||||
|
"logqlHint": "LogQL query or count_over_time expression",
|
||||||
|
"esHint": "Elasticsearch Lucene query",
|
||||||
|
"promqlHint": "PromQL or metric expression, e.g. cpu_usage > 80",
|
||||||
|
"severity": "Severity",
|
||||||
|
"duration": "Duration (sec)",
|
||||||
|
"ruleDesc": "Rule Description",
|
||||||
|
"noChannels": "No notification channels configured. Please add them in Notification Settings first.",
|
||||||
|
"addLogSourceFirst": "Please add a {{type}} datasource in Log Sources first"
|
||||||
},
|
},
|
||||||
"alertMgr2": {
|
"alertMgr2": {
|
||||||
"alertEvents": "Alert Events",
|
"alertEvents": "Alert Events",
|
||||||
@@ -6874,7 +6890,8 @@
|
|||||||
},
|
},
|
||||||
"monitor2": {
|
"monitor2": {
|
||||||
"commandPlaceholder2": "e.g., free -m && df -h"
|
"commandPlaceholder2": "e.g., free -m && df -h"
|
||||||
}
|
},
|
||||||
|
"portCount": "Port Count"
|
||||||
},
|
},
|
||||||
"system": {
|
"system": {
|
||||||
"title": "System",
|
"title": "System",
|
||||||
|
|||||||
@@ -4455,7 +4455,8 @@
|
|||||||
"hintFeishu": "飞书机器人: {\"url\":\"https://open.feishu.cn/open-apis/bot/v2/hook/...\"}",
|
"hintFeishu": "飞书机器人: {\"url\":\"https://open.feishu.cn/open-apis/bot/v2/hook/...\"}",
|
||||||
"hintEmail": "邮件: {\"to\":\"ops@ezops.io\"}",
|
"hintEmail": "邮件: {\"to\":\"ops@ezops.io\"}",
|
||||||
"hintSms": "短信: {\"phones\":\"13800138000\"}",
|
"hintSms": "短信: {\"phones\":\"13800138000\"}",
|
||||||
"hintWebhook": "Webhook: {\"url\":\"https://...\"}"
|
"hintWebhook": "Webhook: {\"url\":\"https://...\"}",
|
||||||
|
"name": "渠道名称"
|
||||||
},
|
},
|
||||||
"alertChannels": {
|
"alertChannels": {
|
||||||
"fetchFailed": "获取通知渠道失败",
|
"fetchFailed": "获取通知渠道失败",
|
||||||
@@ -4905,12 +4906,27 @@
|
|||||||
"siteClientError": "客户端/鉴权错误",
|
"siteClientError": "客户端/鉴权错误",
|
||||||
"siteRequestFailed": "请求失败",
|
"siteRequestFailed": "请求失败",
|
||||||
"siteRequestTimeout": "请求超时",
|
"siteRequestTimeout": "请求超时",
|
||||||
"siteConnFailed": "连接失败"
|
"siteConnFailed": "连接失败",
|
||||||
|
"noProbeHint": "暂无健康探针,点击「{{action}}」创建"
|
||||||
},
|
},
|
||||||
"alertRuleDialog2": {
|
"alertRuleDialog2": {
|
||||||
"editRule": "编辑告警规则",
|
"editRule": "编辑告警规则",
|
||||||
"newRule": "新建告警规则",
|
"newRule": "新建告警规则",
|
||||||
"namePlaceholder": "如:CPU 使用率告警"
|
"namePlaceholder": "如:CPU 使用率告警",
|
||||||
|
"alertType": "告警类型",
|
||||||
|
"metricAlert": "指标告警",
|
||||||
|
"eventAlert": "事件告警",
|
||||||
|
"logDatasource": "日志数据源",
|
||||||
|
"selectLogSource": "请选择日志数据源",
|
||||||
|
"expression": "规则表达式",
|
||||||
|
"logqlHint": "LogQL 查询或 count_over_time 表达式",
|
||||||
|
"esHint": "Elasticsearch Lucene 查询",
|
||||||
|
"promqlHint": "PromQL 或指标表达式,例如 cpu_usage > 80",
|
||||||
|
"severity": "告警级别",
|
||||||
|
"duration": "持续时间(秒)",
|
||||||
|
"ruleDesc": "规则描述",
|
||||||
|
"noChannels": "暂无已配置的通知渠道,请先在「通知渠道配置」中添加",
|
||||||
|
"addLogSourceFirst": "请先前往「日志库」添加 {{type}} 数据源"
|
||||||
},
|
},
|
||||||
"alertMgr2": {
|
"alertMgr2": {
|
||||||
"alertEvents": "告警事件",
|
"alertEvents": "告警事件",
|
||||||
@@ -6874,7 +6890,8 @@
|
|||||||
},
|
},
|
||||||
"monitor2": {
|
"monitor2": {
|
||||||
"commandPlaceholder2": "例如:free -m && df -h"
|
"commandPlaceholder2": "例如:free -m && df -h"
|
||||||
}
|
},
|
||||||
|
"portCount": "端口数"
|
||||||
},
|
},
|
||||||
"system": {
|
"system": {
|
||||||
"title": "系统管理",
|
"title": "系统管理",
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ export function HealthProbeSection({ selectedHost }: HealthProbeSectionProps) {
|
|||||||
{probes.length === 0 ? (
|
{probes.length === 0 ? (
|
||||||
<Card className="bg-[var(--dg-bg-secondary)] border-dashed border-[var(--dg-border)]">
|
<Card className="bg-[var(--dg-bg-secondary)] border-dashed border-[var(--dg-border)]">
|
||||||
<CardContent className="p-8 text-center text-xs text-[var(--dg-text-muted)]">
|
<CardContent className="p-8 text-center text-xs text-[var(--dg-text-muted)]">
|
||||||
暂无健康探针,点击t('monitor.lbl.addProbe')创建
|
{t('monitor.lbl.noProbeHint', { action: t('monitor.lbl.addProbe') })}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
) : (
|
) : (
|
||||||
@@ -168,7 +168,7 @@ export function HealthProbeSection({ selectedHost }: HealthProbeSectionProps) {
|
|||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="space-y-3 max-h-[60vh] overflow-y-auto pr-1">
|
<div className="space-y-3 max-h-[60vh] overflow-y-auto pr-1">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-[11px] text-[var(--dg-text-muted)] mb-1 block"> {t('>探针名称 <span className=')} <span className="dg-text-red">*</span></label>
|
<label className="text-[11px] text-[var(--dg-text-muted)] mb-1 block"> {t('monitor.lbl.probeName')} <span className="dg-text-red">*</span></label>
|
||||||
<input className="w-full h-8 bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)] rounded px-2 text-xs text-[var(--dg-text-primary)]" value={probeForm.name} onChange={e => setProbeForm({ ...probeForm, name: e.target.value })} placeholder={t('monitor.healthProbe.namePlaceholder')} />
|
<input className="w-full h-8 bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)] rounded px-2 text-xs text-[var(--dg-text-primary)]" value={probeForm.name} onChange={e => setProbeForm({ ...probeForm, name: e.target.value })} placeholder={t('monitor.healthProbe.namePlaceholder')} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -214,7 +214,7 @@ export function HealthProbeSection({ selectedHost }: HealthProbeSectionProps) {
|
|||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="grid grid-cols-2 gap-2">
|
||||||
<div><label className="text-[11px] text-[var(--dg-text-muted)] mb-1 block"> {t('automation.remoteTimeout')} </label>
|
<div><label className="text-[11px] text-[var(--dg-text-muted)] mb-1 block"> {t('automation.remoteTimeout')} </label>
|
||||||
<input type="number" className="w-full h-8 bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)] rounded px-2 text-xs text-[var(--dg-text-primary)]" value={probeForm.timeout} onChange={e => setProbeForm({ ...probeForm, timeout: Number(e.target.value) })} /></div>
|
<input type="number" className="w-full h-8 bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)] rounded px-2 text-xs text-[var(--dg-text-primary)]" value={probeForm.timeout} onChange={e => setProbeForm({ ...probeForm, timeout: Number(e.target.value) })} /></div>
|
||||||
<div><label className="text-[11px] text-[var(--dg-text-muted)] mb-1 block"> {t('>执行命令 <span className=')} </label>
|
<div><label className="text-[11px] text-[var(--dg-text-muted)] mb-1 block"> {t('monitor.lbl.execCommand')} </label>
|
||||||
<input type="number" className="w-full h-8 bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)] rounded px-2 text-xs text-[var(--dg-text-primary)]" value={probeForm.interval} onChange={e => setProbeForm({ ...probeForm, interval: Number(e.target.value) })} /></div>
|
<input type="number" className="w-full h-8 bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)] rounded px-2 text-xs text-[var(--dg-text-primary)]" value={probeForm.interval} onChange={e => setProbeForm({ ...probeForm, interval: Number(e.target.value) })} /></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user