refactor(i18n): U36 全量 JSX 属性 + toast 硬编码中文清零 — 20 文件 30 处 → 0
清理 23 处 JSX 属性 (title/placeholder/label/description) + 7 处 toast 调用硬编码中文: - Deploy 模块 (AppRelease/DeployCredentials/PipelineManager/WorkspaceTermDialog/CredFormDialog/JobRoleCreateDialog/PipelineCreateDialog/AdvancedSettingsPanel) - Server 模块 (ServerForm) - System 模块 (LdapManagementSection/LiveMonitorDialog/SystemManager) - Audit 模块 (ActiveSessionsPanel/HistorySessionsPanel) - Monitor 模块 (AlertRulesTab/PrometheusDatasourceManager/SNMPMonitor) - AIOps 模块 (PredictiveCapacityTab) - Common/UI (TerminalLoadingOverlay/copy-button) 新增 25 翻译键 (common.connectingEllipsis/enteringWorkspace/connectingMonitor + deploy.* 11键 + server.jsonFormatPlaceholder + system.* 3键 + audit.* 2键 + monitor.* 3键 + aiops.aiAnalysisFailedToast + pipeline.subtitle) copy-button.tsx 从 import i18n → useTranslation() hook 3 组件新增 useTranslation hook (TerminalLoadingOverlay/WorkspaceTermDialog/LiveMonitorDialog/PredictiveCapacityTab) zh/en 7616→7640 +24 对齐, tsc -b=0, npm build✓ (17.25s), ESLint=0/43/0, go build=0, 17/17 测试通过
This commit is contained in:
1 parent
8d64936caf
commit
09b7fd4ad5
22 files changed
+114
-55
No files matched your search
@@ -4,6 +4,7 @@ import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { toast } from 'sonner';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { aiApi } from '@/api';
|
||||
import { buildCapacityInsightPrompt, type CapacityForecast } from './predictUtils';
|
||||
import { AgentMarkdown, looksLikeMarkdown } from './AgentMarkdown';
|
||||
@@ -15,6 +16,7 @@ export function PredictiveCapacityTab({
|
||||
forecasts: CapacityForecast[];
|
||||
loading: boolean;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [insight, setInsight] = useState('');
|
||||
const [analyzing, setAnalyzing] = useState(false);
|
||||
|
||||
@@ -28,7 +30,7 @@ export function PredictiveCapacityTab({
|
||||
const res = await aiApi.chat([{ role: 'user', content: prompt }]);
|
||||
setInsight(res?.content || '');
|
||||
} catch {
|
||||
toast.error('AI 解读失败,请检查大模型配置或稍后重试');
|
||||
toast.error(t('aiops.aiAnalysisFailedToast'));
|
||||
} finally {
|
||||
setAnalyzing(false);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
interface TerminalLoadingOverlayProps {
|
||||
show: boolean;
|
||||
@@ -7,14 +8,15 @@ interface TerminalLoadingOverlayProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function TerminalLoadingOverlay({ show, label = '连接中...', className }: TerminalLoadingOverlayProps) {
|
||||
export function TerminalLoadingOverlay({ show, label, className }: TerminalLoadingOverlayProps) {
|
||||
const { t } = useTranslation();
|
||||
if (!show) return null;
|
||||
|
||||
return (
|
||||
<div className={cn('absolute inset-0 z-10 flex items-center justify-center bg-black/35', className)}>
|
||||
<div className="flex flex-col items-center gap-2 text-white/85">
|
||||
<Loader2 className="h-7 w-7 animate-spin" />
|
||||
{label && <span className="text-xs">{label}</span>}
|
||||
{label && <span className="text-xs">{label || t('common.connectingEllipsis')}</span>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -100,7 +100,7 @@ export function AppReleaseManager() {
|
||||
</div>
|
||||
<Select value={envFilter} onValueChange={setEnvFilter}>
|
||||
<SelectTrigger className="w-32 h-9 dg-input">
|
||||
<SelectValue placeholder="环境" />
|
||||
<SelectValue placeholder={t('deploy.placeholderEnv')} />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="dg-input">
|
||||
<SelectItem value="all">{t('deploy.filterAllEnv')}</SelectItem>
|
||||
@@ -112,7 +112,7 @@ export function AppReleaseManager() {
|
||||
</Select>
|
||||
<Select value={statusFilter} onValueChange={setStatusFilter}>
|
||||
<SelectTrigger className="w-32 h-9 dg-input">
|
||||
<SelectValue placeholder="状态" />
|
||||
<SelectValue placeholder={t('deploy.placeholderStatus')} />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="dg-input">
|
||||
<SelectItem value="all">{t('deploy.filterAllStatus')}</SelectItem>
|
||||
|
||||
@@ -275,8 +275,8 @@ export default function DeployCredentials() {
|
||||
{environments.length === 0 ? (
|
||||
<PageEmpty
|
||||
icon={<Globe className="h-10 w-10" />}
|
||||
title="暂无环境配置"
|
||||
description='点击"新增环境"创建'
|
||||
title={t('deploy.noEnvConfig')}
|
||||
description={t('deploy.clickEnvConfigHint')}
|
||||
action={(
|
||||
<Button size="sm" onClick={() => { setEditEnv(null); setEnvDialogOpen(true); }}>
|
||||
<Plus className="w-4 h-4 mr-1" />新增环境
|
||||
|
||||
@@ -214,8 +214,8 @@ export function PipelineManager() {
|
||||
return (
|
||||
<PageShell
|
||||
variant="list"
|
||||
title="流水线"
|
||||
subtitle="CI/CD 构建、镜像打包与双通道部署"
|
||||
title={t('pipeline.title')}
|
||||
subtitle={t('pipeline.subtitle')}
|
||||
icon={<Workflow className="h-5 w-5" />}
|
||||
loading={loading}
|
||||
toolbar={toolbar}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { cn } from '@/lib/utils';
|
||||
import { attachTerminalResize, mountXterm } from '@/lib/xterm-utils';
|
||||
import { useUserStore } from '@/store';
|
||||
import { pipelineApi } from '@/api';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TerminalLoadingOverlay } from '@/components/common/TerminalLoadingOverlay';
|
||||
import type { JsonData } from '@/types/common';
|
||||
|
||||
@@ -32,6 +33,7 @@ export function WorkspaceTermDialog({
|
||||
runId,
|
||||
buildNumber,
|
||||
}: WorkspaceTermDialogProps) {
|
||||
const { t } = useTranslation();
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const termRef = useRef<XTerm | null>(null);
|
||||
const wsRef = useRef<WebSocket | null>(null);
|
||||
@@ -186,7 +188,7 @@ export function WorkspaceTermDialog({
|
||||
</div>
|
||||
<div className="relative bg-[#0a0e14]" style={{ height: fullscreen ? 'calc(100vh - 49px)' : '480px' }}>
|
||||
<div ref={containerRef} className="xterm-terminal-host h-full" />
|
||||
<TerminalLoadingOverlay show={!hasOutput} label="正在进入工作区..." />
|
||||
<TerminalLoadingOverlay show={!hasOutput} label={t('common.enteringWorkspace')} />
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@@ -123,8 +123,8 @@ export function CredFormDialog({ open, onClose, onSaved, editData }: CredFormDia
|
||||
if (sshPrivateKey.trim()) payload.ssh_private_key = sshPrivateKey.trim();
|
||||
if (sshPublicKey.trim()) payload.ssh_public_key = sshPublicKey.trim();
|
||||
} else if (secretType === 'rdp') {
|
||||
if (!rdpUsername.trim()) { toast.error('请填写 RDP 用户'); return; }
|
||||
if (!isEdit && !rdpPassword.trim()) { toast.error('请填写 RDP 密码'); return; }
|
||||
if (!rdpUsername.trim()) { toast.error(t('deploy.rdpUserRequired')); return; }
|
||||
if (!isEdit && !rdpPassword.trim()) { toast.error(t('deploy.rdpPasswordRequired')); return; }
|
||||
if (rdpUsername.trim()) payload.rdp_username = rdpUsername.trim();
|
||||
if (rdpPassword.trim()) payload.rdp_password = rdpPassword.trim();
|
||||
if (rdpDomain.trim()) payload.rdp_domain = rdpDomain.trim();
|
||||
@@ -310,11 +310,11 @@ export function CredFormDialog({ open, onClose, onSaved, editData }: CredFormDia
|
||||
</Select>
|
||||
</div>
|
||||
{/* 用户名(左上)与密码(左下)同列上下行,安全模式/域归右列 // Logan.Li */}
|
||||
<div><FieldWithToggle label="Windows 密码" value={rdpPassword} onChange={setRdpPassword}
|
||||
placeholder="Windows 登录密码" fieldKey="rdp_pwd" required /></div>
|
||||
<div><FieldWithToggle label={t('deploy.rdpPassword')} value={rdpPassword} onChange={setRdpPassword}
|
||||
placeholder={t('deploy.rdpPasswordPlaceholder')} fieldKey="rdp_pwd" required /></div>
|
||||
<div className="space-y-1">
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">域</Label>
|
||||
<Input value={rdpDomain} onChange={e => setRdpDomain(e.target.value)} placeholder="可选"
|
||||
<Input value={rdpDomain} onChange={e => setRdpDomain(e.target.value)} placeholder={t('common.optional')}
|
||||
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -89,7 +89,7 @@ export function JobRoleCreateDialog({
|
||||
<Input
|
||||
value={pattern}
|
||||
onChange={(e) => onPatternChange(e.target.value)}
|
||||
placeholder="^cex-.* 或 ^api-server$"
|
||||
placeholder={t('deploy.jobNamePatternHint')}
|
||||
className="h-8 text-sm font-mono dg-input"
|
||||
/>
|
||||
<p className="text-[10px] text-[var(--dg-text-muted)]">{patternHint}</p>
|
||||
|
||||
@@ -371,7 +371,7 @@ export function PipelineCreateDialog({ open, onOpenChange, onCreated }: Pipeline
|
||||
onValueChange={(v) => setNewGitCredentialId(v === '__none' ? '' : v)}
|
||||
>
|
||||
<SelectTrigger className="h-8 text-xs dg-input">
|
||||
<SelectValue placeholder="选择凭证" />
|
||||
<SelectValue placeholder={t('deploy.selectCredentialPlaceholder')} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="__none">{t('deploy.notLinked')}</SelectItem>
|
||||
@@ -396,7 +396,7 @@ export function PipelineCreateDialog({ open, onOpenChange, onCreated }: Pipeline
|
||||
onValueChange={(v) => setNewRegistryCredentialId(v === '__none' ? '' : v)}
|
||||
>
|
||||
<SelectTrigger className="h-8 text-xs dg-input">
|
||||
<SelectValue placeholder="Docker 凭证" />
|
||||
<SelectValue placeholder={t('deploy.dockerCredPlaceholder')} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="__none">不关联</SelectItem>
|
||||
|
||||
+1
-1
@@ -106,7 +106,7 @@ export function AdvancedSettingsPanel({
|
||||
<Input
|
||||
value={watchBranches}
|
||||
onChange={(e) => onWatchBranchesChange(e.target.value)}
|
||||
placeholder="监听分支,如 main, release/*"
|
||||
placeholder={t('deploy.branchListenPlaceholder')}
|
||||
className="h-8 text-xs font-mono dg-input"
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -82,7 +82,7 @@ export function AlertRulesTab({
|
||||
if (!ok) return;
|
||||
try {
|
||||
await alertApi.batchDeleteRules(selectedIds);
|
||||
toast.success(`已删除 ${selectedIds.length} 条告警规则`);
|
||||
toast.success(i18n.t('monitor.deletedRules', { count: selectedIds.length }));
|
||||
setSelectedIds([]);
|
||||
onReload();
|
||||
} catch (error) {
|
||||
|
||||
@@ -65,7 +65,7 @@ export default function PrometheusDatasourceManager() {
|
||||
const handleSetDefault = async (ds: PromDS) => {
|
||||
try {
|
||||
await setDefaultMutation.mutateAsync(ds.id);
|
||||
toast.success(`已将「${ds.name}」设为默认数据源`);
|
||||
toast.success(i18n.t('monitor.setDefaultDsSuccess', { name: ds.name }));
|
||||
} catch {
|
||||
toast.error(i18n.t('monitor.promDs2.deleteFailed'));
|
||||
}
|
||||
|
||||
@@ -396,7 +396,7 @@ export function ServerForm({
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)]">{t('server.formTags')}</Label>
|
||||
<Input
|
||||
placeholder='JSON 格式,如 {"project":"web","team":"ops"}'
|
||||
placeholder={t('server.jsonFormatPlaceholder')}
|
||||
value={formData.tags}
|
||||
onChange={(e) => setFormData({ ...formData, tags: e.target.value })}
|
||||
className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)] font-mono text-sm"
|
||||
|
||||
@@ -288,7 +288,7 @@ export function LdapManagementSection(props: LdapManagementSectionProps) {
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs dg-text-muted mb-1 block">{t('system.userDisplayName')}</label>
|
||||
<Input value={newUser.display_name} onChange={(e) => setNewUser({ ...newUser, display_name: e.target.value })} placeholder="张三" className="dg-input" />
|
||||
<Input value={newUser.display_name} onChange={(e) => setNewUser({ ...newUser, display_name: e.target.value })} placeholder={t('system.namePlaceholder')} className="dg-input" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs dg-text-muted mb-1 block">{t('system.userEmail')}</label>
|
||||
@@ -450,7 +450,7 @@ export function LdapManagementSection(props: LdapManagementSectionProps) {
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs dg-text-muted mb-1 block">{t('system.jobTitle')}</label>
|
||||
<Input value={editLdapForm.title} onChange={(e) => setEditLdapForm({ ...editLdapForm, title: e.target.value })} placeholder="运维工程师" className="dg-input" />
|
||||
<Input value={editLdapForm.title} onChange={(e) => setEditLdapForm({ ...editLdapForm, title: e.target.value })} placeholder={t('system.roleTitlePlaceholder')} className="dg-input" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// 终端审计实时监控 Dialog — 与 WebSSH 终端弹框一致的布局与适配 // Logan.Li
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Terminal as XTerm } from 'xterm';
|
||||
import { FitAddon } from 'xterm-addon-fit';
|
||||
import 'xterm/css/xterm.css';
|
||||
@@ -33,6 +34,7 @@ interface LiveMonitorProps {
|
||||
}
|
||||
|
||||
export function LiveMonitorDialog({ open, onOpenChange, sessionId, sessionInfo }: LiveMonitorProps) {
|
||||
const { t } = useTranslation();
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const termRef = useRef<XTerm | null>(null);
|
||||
const fitRef = useRef<FitAddon | null>(null);
|
||||
@@ -250,7 +252,7 @@ export function LiveMonitorDialog({ open, onOpenChange, sessionId, sessionInfo }
|
||||
|
||||
<div className="relative bg-[#0a0e14]" style={{ height: fullscreen ? 'calc(100vh - 49px)' : '520px' }}>
|
||||
<div ref={containerRef} className="xterm-terminal-host h-full" />
|
||||
<TerminalLoadingOverlay show={!connected && !connectError} label="正在连接监控..." />
|
||||
<TerminalLoadingOverlay show={!connected && !connectError} label={t('common.connectingMonitor')} />
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@@ -237,7 +237,7 @@ export function SystemManager() {
|
||||
const res = (await userApi.batchDelete(selectedUserIds)) as {
|
||||
message?: string;
|
||||
};
|
||||
toast.success(res?.message || `已删除 ${selectedUserIds.length} 个用户`);
|
||||
toast.success(res?.message || t('system.batchDeleteUsers', { count: selectedUserIds.length }));
|
||||
setSelectedUserIds([]);
|
||||
await loadUsers();
|
||||
} catch (error: unknown) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Button } from '@/components/ui/button';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { copyToClipboard } from '@/utils/clipboard';
|
||||
import { cn } from '@/lib/utils';
|
||||
import i18n from '@/i18n';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export interface CopyButtonProps {
|
||||
text: string;
|
||||
@@ -22,14 +22,17 @@ export interface CopyButtonProps {
|
||||
|
||||
export function CopyButton({
|
||||
text,
|
||||
label = '已复制',
|
||||
label,
|
||||
children,
|
||||
className,
|
||||
size = 'sm',
|
||||
variant = 'outline',
|
||||
disabled,
|
||||
title = '复制',
|
||||
title,
|
||||
}: CopyButtonProps) {
|
||||
const { t } = useTranslation();
|
||||
const resolvedLabel = label ?? t('common.copied');
|
||||
const resolvedTitle = title ?? t('common.copy');
|
||||
const { toast } = useToast();
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
@@ -37,12 +40,12 @@ export function CopyButton({
|
||||
const ok = await copyToClipboard(text);
|
||||
if (ok) {
|
||||
setCopied(true);
|
||||
toast({ title: label });
|
||||
toast({ title: resolvedLabel });
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} else {
|
||||
toast({
|
||||
title: i18n.t('monitor.logPanel.copyFailed'),
|
||||
description: i18n.t('common.copyManually'),
|
||||
title: t('monitor.logPanel.copyFailed'),
|
||||
description: t('common.copyManually'),
|
||||
variant: 'destructive',
|
||||
});
|
||||
}
|
||||
@@ -61,8 +64,8 @@ export function CopyButton({
|
||||
)}
|
||||
onClick={handleCopy}
|
||||
disabled={disabled || !text}
|
||||
title={title}
|
||||
aria-label={title}
|
||||
title={resolvedTitle}
|
||||
aria-label={resolvedTitle}
|
||||
>
|
||||
{copied ? (
|
||||
<Check className={cn('dg-text-green', children ? 'h-3.5 w-3.5 mr-1' : 'h-3.5 w-3.5')} />
|
||||
|
||||
@@ -516,7 +516,10 @@
|
||||
"seconds": "sec",
|
||||
"copyManually": "Please manually select and copy",
|
||||
"scrollToTop": "Scroll to top",
|
||||
"scrollToBottom": "Scroll to bottom"
|
||||
"scrollToBottom": "Scroll to bottom",
|
||||
"connectingEllipsis": "Connecting...",
|
||||
"enteringWorkspace": "Entering workspace...",
|
||||
"connectingMonitor": "Connecting to monitor..."
|
||||
},
|
||||
"menu": {
|
||||
"dashboard": "Dashboard",
|
||||
@@ -1142,7 +1145,8 @@
|
||||
"loadDirFailed": "Failed to load directory",
|
||||
"downloadFailed": "Download failed: ",
|
||||
"directory": "Directory"
|
||||
}
|
||||
},
|
||||
"jsonFormatPlaceholder": "JSON format, e.g. {\"key\":\"value\"}"
|
||||
},
|
||||
"alert": {
|
||||
"stat": {
|
||||
@@ -1510,7 +1514,9 @@
|
||||
}
|
||||
},
|
||||
"reportTitle": "Terminal Session Audit Report",
|
||||
"csvHeader": "Time,User,Command,Output Size,Session ID,Critical"
|
||||
"csvHeader": "Time,User,Command,Output Size,Session ID,Critical",
|
||||
"rdpRealtimeAudit": "RDP Real-time Audit",
|
||||
"rdpHistoryReplay": "RDP History Replay"
|
||||
},
|
||||
"deploy": {
|
||||
"pipeline": {
|
||||
@@ -2408,7 +2414,19 @@
|
||||
"copyRef": "Copy Reference ID",
|
||||
"testConn": "Test Connection",
|
||||
"noTags": "No tags"
|
||||
}
|
||||
},
|
||||
"placeholderEnv": "Environment",
|
||||
"placeholderStatus": "Status",
|
||||
"noEnvConfig": "No environment configuration",
|
||||
"clickEnvConfigHint": "Click \"Add Environment\" to create",
|
||||
"rdpPassword": "Windows Password",
|
||||
"rdpPasswordPlaceholder": "Windows login password",
|
||||
"selectCredentialPlaceholder": "Select credential",
|
||||
"dockerCredPlaceholder": "Docker credential",
|
||||
"branchListenPlaceholder": "Listen branches, e.g. main, release/*",
|
||||
"rdpUserRequired": "Please fill in RDP username",
|
||||
"rdpPasswordRequired": "Please fill in RDP password",
|
||||
"jobNamePatternHint": "^cex-.* or ^api-server$"
|
||||
},
|
||||
"k8s": {
|
||||
"cluster": {
|
||||
@@ -5075,7 +5093,10 @@
|
||||
"configDesc": "Configure Zabbix API connection to get host monitoring data",
|
||||
"directDb": "Direct DB Connection",
|
||||
"emptyDatasource": "No Zabbix data sources"
|
||||
}
|
||||
},
|
||||
"deletedRules": "Deleted {{count}} alert rules",
|
||||
"setDefaultDsSuccess": "Set \"{{name}}\" as default datasource",
|
||||
"collectSuccess": "{{name}} collection success"
|
||||
},
|
||||
"task": {
|
||||
"cronJob": {
|
||||
@@ -6134,7 +6155,7 @@
|
||||
},
|
||||
"pipeline": {
|
||||
"title": "Pipelines",
|
||||
"subtitle": "CI/CD Build · Deploy · Release All-in-One",
|
||||
"subtitle": "CI/CD build, image packaging & dual-channel deployment",
|
||||
"pipelines": "Pipelines",
|
||||
"addPipeline": "New Pipeline",
|
||||
"editPipeline": "Edit Pipeline",
|
||||
@@ -7374,7 +7395,7 @@
|
||||
"savingDot": "Saving...",
|
||||
"recordAllOps": "Record All Operations",
|
||||
"searchFilterHint": "Search filter (optional, use %s for username)",
|
||||
"batchDeleteUsers": "Batch Delete Users",
|
||||
"batchDeleteUsers": "Deleted {{count}} users",
|
||||
"forcePasswordComplexity": "Enforce Password Complexity",
|
||||
"systemVersion": "System Version",
|
||||
"saveConfig": "Save Configuration",
|
||||
@@ -7530,7 +7551,9 @@
|
||||
"importSelected": "Import ({{count}})",
|
||||
"importAll": "Import All ({{count}})",
|
||||
"editLdapUserDesc": "Modify attributes of {{uid}}",
|
||||
"permEffectTrailing": ", changes take effect after user re-login."
|
||||
"permEffectTrailing": ", changes take effect after user re-login.",
|
||||
"namePlaceholder": "John Doe",
|
||||
"roleTitlePlaceholder": "DevOps Engineer"
|
||||
},
|
||||
"aiops": {
|
||||
"title": "AIOps",
|
||||
@@ -8238,7 +8261,8 @@
|
||||
"predictUtils": {
|
||||
"dailyCostHint": "Review {{scope}}today's high-cost services, pause non-essential resources or adjust auto-scaling",
|
||||
"monthlyBillHint": "Review {{scope}}this month's bill composition, optimize storage/CDN/compute, reduce over-budget quotas"
|
||||
}
|
||||
},
|
||||
"aiAnalysisFailedToast": "AI analysis failed, please check LLM config or try again later"
|
||||
},
|
||||
"breadcrumb": {
|
||||
"dashboard": "Dashboard",
|
||||
|
||||
@@ -516,7 +516,10 @@
|
||||
"seconds": "秒",
|
||||
"copyManually": "请手动选中内容后复制",
|
||||
"scrollToTop": "回到顶部",
|
||||
"scrollToBottom": "回到底部"
|
||||
"scrollToBottom": "回到底部",
|
||||
"connectingEllipsis": "连接中...",
|
||||
"enteringWorkspace": "正在进入工作区...",
|
||||
"connectingMonitor": "正在连接监控..."
|
||||
},
|
||||
"menu": {
|
||||
"dashboard": "仪表盘",
|
||||
@@ -1142,7 +1145,8 @@
|
||||
"loadDirFailed": "加载目录失败",
|
||||
"downloadFailed": "下载失败: ",
|
||||
"directory": "目录"
|
||||
}
|
||||
},
|
||||
"jsonFormatPlaceholder": "JSON 格式,如 {\"key\":\"value\"}"
|
||||
},
|
||||
"alert": {
|
||||
"stat": {
|
||||
@@ -1510,7 +1514,9 @@
|
||||
}
|
||||
},
|
||||
"reportTitle": "终端会话审计报告",
|
||||
"csvHeader": "时间,用户,命令,输出大小,会话ID,高危"
|
||||
"csvHeader": "时间,用户,命令,输出大小,会话ID,高危",
|
||||
"rdpRealtimeAudit": "RDP 实时审计",
|
||||
"rdpHistoryReplay": "RDP 历史回放"
|
||||
},
|
||||
"deploy": {
|
||||
"pipeline": {
|
||||
@@ -2408,7 +2414,19 @@
|
||||
"copyRef": "复制引用ID",
|
||||
"testConn": "测试连接",
|
||||
"noTags": "无标签"
|
||||
}
|
||||
},
|
||||
"placeholderEnv": "环境",
|
||||
"placeholderStatus": "状态",
|
||||
"noEnvConfig": "暂无环境配置",
|
||||
"clickEnvConfigHint": "点击\"新增环境\"创建",
|
||||
"rdpPassword": "Windows 密码",
|
||||
"rdpPasswordPlaceholder": "Windows 登录密码",
|
||||
"selectCredentialPlaceholder": "选择凭证",
|
||||
"dockerCredPlaceholder": "Docker 凭证",
|
||||
"branchListenPlaceholder": "监听分支,如 main, release/*",
|
||||
"rdpUserRequired": "请填写 RDP 用户",
|
||||
"rdpPasswordRequired": "请填写 RDP 密码",
|
||||
"jobNamePatternHint": "^cex-.* 或 ^api-server$"
|
||||
},
|
||||
"k8s": {
|
||||
"cluster": {
|
||||
@@ -5075,7 +5093,10 @@
|
||||
"configDesc": "配置 Zabbix API 连接,获取主机监控数据",
|
||||
"directDb": "直连DB",
|
||||
"emptyDatasource": "暂无 Zabbix 数据源"
|
||||
}
|
||||
},
|
||||
"deletedRules": "已删除 {{count}} 条告警规则",
|
||||
"setDefaultDsSuccess": "已将「{{name}}」设为默认数据源",
|
||||
"collectSuccess": "{{name}} 采集成功"
|
||||
},
|
||||
"task": {
|
||||
"cronJob": {
|
||||
@@ -6134,7 +6155,7 @@
|
||||
},
|
||||
"pipeline": {
|
||||
"title": "流水线",
|
||||
"subtitle": "CI/CD 构建 · 部署 · 发布一体化",
|
||||
"subtitle": "CI/CD 构建、镜像打包与双通道部署",
|
||||
"pipelines": "流水线列表",
|
||||
"addPipeline": "新建流水线",
|
||||
"editPipeline": "编辑流水线",
|
||||
@@ -7374,7 +7395,7 @@
|
||||
"savingDot": "保存中...",
|
||||
"recordAllOps": "记录所有操作",
|
||||
"searchFilterHint": "搜索过滤器(可选,用 %s 代表用户名)",
|
||||
"batchDeleteUsers": "批量删除用户",
|
||||
"batchDeleteUsers": "已删除 {{count}} 个用户",
|
||||
"forcePasswordComplexity": "强制密码复杂度",
|
||||
"systemVersion": "系统版本",
|
||||
"saveConfig": "保存配置",
|
||||
@@ -7530,7 +7551,9 @@
|
||||
"importSelected": "导入 ({{count}})",
|
||||
"importAll": "导入全部 ({{count}})",
|
||||
"editLdapUserDesc": "修改 {{uid}} 的属性",
|
||||
"permEffectTrailing": ",修改后用户需重新登录生效。"
|
||||
"permEffectTrailing": ",修改后用户需重新登录生效。",
|
||||
"namePlaceholder": "张三",
|
||||
"roleTitlePlaceholder": "运维工程师"
|
||||
},
|
||||
"aiops": {
|
||||
"title": "AIOps 智能",
|
||||
@@ -8238,7 +8261,8 @@
|
||||
"predictUtils": {
|
||||
"dailyCostHint": "检查{{scope}}今日高消费服务,暂停非必要资源或调整弹性伸缩策略",
|
||||
"monthlyBillHint": "复盘{{scope}}本月账单构成,优化存储/CDN/计算规格,必要时下调预算外资源配额"
|
||||
}
|
||||
},
|
||||
"aiAnalysisFailedToast": "AI 解读失败,请检查大模型配置或稍后重试"
|
||||
},
|
||||
"breadcrumb": {
|
||||
"dashboard": "仪表盘",
|
||||
|
||||
@@ -138,7 +138,7 @@ function RdpActiveSessionRow({
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8 dg-text-secondary hover:dg-text-red" onClick={() => onKill(session.id, session.username)} title={t('audit.terminal.killTitle')}>
|
||||
<XOctagon className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8 dg-text-secondary hover:dg-text-blue" onClick={() => onMonitor(session.id)} title="RDP 实时审计">
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8 dg-text-secondary hover:dg-text-blue" onClick={() => onMonitor(session.id)} title={t('audit.rdpRealtimeAudit')}>
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -290,7 +290,7 @@ export function HistorySessionsPanel() {
|
||||
<TableCell>{getStatusBadge(s.status)}</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex items-center justify-end gap-0.5">
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8 dg-text-secondary hover:dg-text-blue" onClick={() => openRdpReplay(s.id)} disabled={!s.record_size} title="RDP 历史回放">
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8 dg-text-secondary hover:dg-text-blue" onClick={() => openRdpReplay(s.id)} disabled={!s.record_size} title={t('audit.rdpHistoryReplay')}>
|
||||
<Play className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8 dg-text-secondary hover:dg-text-green" onClick={() => rdpSessionApi.download(s.id)} disabled={!s.record_size} title={t('audit.terminal.downloadRecord')}>
|
||||
|
||||
@@ -135,7 +135,7 @@ export function SNMPMonitor() {
|
||||
setCollectingId(d.id);
|
||||
try {
|
||||
await snmpApi.collectDevice(d.id);
|
||||
toast.success(`${d.name} 采集成功`);
|
||||
toast.success(t('monitor.collectSuccess', { name: d.name }));
|
||||
refresh();
|
||||
} catch (err) {
|
||||
toast.error((err as { message?: string }).message || t('monitor.lbl.collectFail'));
|
||||
|
||||
Reference in new issue
Block a user