feat(i18n): 全量中英文国际化改造

- 翻译 en-US.json 中 1006 条未翻译的中文条目为英文
- 提取 30+ 组件文件的硬编码中文到 locale JSON
- 新增 900+ 翻译键覆盖所有业务模块:
  - Layout (Sidebar, Header, Breadcrumb)
  - Server (ServerForm, CloudServerImport, WebShell, RdpTools)
  - Deploy (ReleaseManager, PipelineCreate, AppRelease)
  - Kubernetes (DeploymentEdit, PodExec, PodLogs, ResourceEditor)
  - Monitor (UnifiedQueryPanel, AlertCenter, Prometheus/Grafana Dashboard)
  - AIOps (predictUtils, PredictiveCostTab, AIPredictive)
  - System (useLdapManagement, AgentMonitor, AgentWizard)
- tsc --noEmit 类型检查通过
This commit is contained in:
attacker committed 2026-07-04 03:42:27 +08:00
1 parent 76ec9387f0
commit a9513c2056
44 files changed
+4342 -1792

No files matched your search

@@ -41,15 +41,15 @@ interface PipelineCreateDialogProps {
const PRESETS = [
{
key: 'ssh',
label: '主机发布',
desc: 'SCP 上传构建产物到关联主机',
label: t('deploy.presetHost'),
desc: t('deploy.presetHostDesc'),
stages: ['checkout', 'build', 'deploy'],
deployTarget: 'ssh' as const,
},
{
key: 'k8s',
label: 'Kubernetes 发布',
desc: '镜像打包后 CI 更新 Deployment',
label: t('deploy.presetK8s'),
desc: t('deploy.presetK8sDesc'),
stages: ['checkout', 'build', 'image', 'deploy'],
deployTarget: 'k8s' as const,
},
@@ -153,7 +153,7 @@ export function PipelineCreateDialog({ open, onOpenChange, onCreated }: Pipeline
const validateStep1 = () => {
if (!newName.trim()) {
toast.error('请输入流水线名称');
toast.error(t('deploy.enterPipelineName'));
return false;
}
return true;
@@ -170,11 +170,11 @@ export function PipelineCreateDialog({ open, onOpenChange, onCreated }: Pipeline
}
if (selectedStages.includes('deploy')) {
if (deployTarget.deployTarget === 'ssh' && deployTarget.serverIds.length === 0) {
toast.error('主机发布需至少关联一台主机');
toast.error(t('deploy.needHost'));
return false;
}
if (deployTarget.deployTarget === 'k8s' && (!deployTarget.k8sClusterId || !deployTarget.k8sDeployment)) {
toast.error('Kubernetes 发布需选择集群与 Deployment');
toast.error(t('deploy.needK8s'));
return false;
}
}
@@ -241,9 +241,9 @@ export function PipelineCreateDialog({ open, onOpenChange, onCreated }: Pipeline
<Dialog open={open} onOpenChange={(v) => { if (!v) resetForm(); onOpenChange(v); }}>
<DialogContent className="max-w-lg max-h-[88vh] overflow-y-auto bg-[var(--dg-bg-primary)] border-[var(--dg-border)]">
<DialogHeader>
<DialogTitle className="text-[var(--dg-text-primary)]">新建流水线</DialogTitle>
<DialogTitle className="text-[var(--dg-text-primary)]">{t('deploy.newPipelineTitle')}</DialogTitle>
<DialogDescription className="text-[var(--dg-text-muted)]">
选择发布方式,关联主机或 Kubernetes Deployment,创建后可继续编辑阶段脚本
{t('deploy.newPipelineDesc')}
</DialogDescription>
</DialogHeader>
@@ -257,7 +257,7 @@ export function PipelineCreateDialog({ open, onOpenChange, onCreated }: Pipeline
step === 1 ? 'bg-[var(--dg-bg-hover)] text-[var(--dg-text-primary)]' : 'text-[var(--dg-text-muted)]',
)}
>
1. 基础
{t('deploy.step1')}
</button>
<span className="text-[var(--dg-border)]">/</span>
<button
@@ -268,7 +268,7 @@ export function PipelineCreateDialog({ open, onOpenChange, onCreated }: Pipeline
step === 2 ? 'bg-[var(--dg-bg-hover)] text-[var(--dg-text-primary)]' : 'text-[var(--dg-text-muted)]',
)}
>
2. 代码与发布
{t('deploy.step2')}
</button>
</div>
@@ -294,35 +294,35 @@ export function PipelineCreateDialog({ open, onOpenChange, onCreated }: Pipeline
</div>
<div className="space-y-1.5">
<Label className="text-xs">名称 <span className="text-[var(--dg-red)]">*</span></Label>
<Label className="text-xs">{t('deploy.pipeName')} <span className="text-[var(--dg-red)]">*</span></Label>
<Input
value={newName}
onChange={(e) => setNewName(e.target.value)}
placeholder="例如 demo-api"
placeholder={t('deploy.pipeNamePlaceholder')}
className="h-9 dg-input"
/>
</div>
<div className="space-y-1.5">
<Label className="text-xs">描述</Label>
<Label className="text-xs">{t('deploy.pipeDesc')}</Label>
<Textarea
value={newDesc}
onChange={(e) => setNewDesc(e.target.value)}
placeholder="可选"
placeholder={t('deploy.pipeDescPlaceholder')}
rows={2}
className="dg-input text-sm"
/>
</div>
<div className="space-y-1.5">
<Label className="text-xs">环境分组</Label>
<Label className="text-xs">{t('deploy.envGroup')}</Label>
<Select
value={String(newEnvironmentId)}
onValueChange={(v) => setNewEnvironmentId(v === '__none' ? '' : v)}
>
<SelectTrigger className="h-9 dg-input"><SelectValue placeholder="选择环境" /></SelectTrigger>
<SelectTrigger className="h-9 dg-input"><SelectValue placeholder={t('deploy.selectEnv')} /></SelectTrigger>
<SelectContent>
<SelectItem value="__none">不指定</SelectItem>
<SelectItem value="__none">{t('deploy.notSpecified')}</SelectItem>
{environments.map((env) => (
<SelectItem key={env.id as number} value={String(env.id)}>
{String(env.display_name)}
@@ -348,7 +348,7 @@ export function PipelineCreateDialog({ open, onOpenChange, onCreated }: Pipeline
<div className="grid grid-cols-2 gap-2">
<div className="space-y-1.5">
<Label className="text-xs">
Git 仓库 {selectedStages.includes('checkout') && <span className="text-[var(--dg-red)]">*</span>}
{t('deploy.gitRepo')} {selectedStages.includes('checkout') && <span className="text-[var(--dg-red)]">*</span>}
</Label>
<Input
value={newSource}
@@ -358,18 +358,18 @@ export function PipelineCreateDialog({ open, onOpenChange, onCreated }: Pipeline
/>
</div>
<div className="space-y-1.5">
<Label className="text-xs">分支</Label>
<Label className="text-xs">{t('deploy.pipeBranch')}</Label>
<Input value={newBranch} onChange={(e) => setNewBranch(e.target.value)} className="h-8 text-xs dg-input" />
</div>
</div>
<div className="grid grid-cols-2 gap-2">
<div className="space-y-1.5">
<Label className="text-xs">Git 凭证</Label>
<Label className="text-xs">{t('deploy.gitCredential')}</Label>
<Select value={String(newGitCredentialId)} onValueChange={(v) => setNewGitCredentialId(v === '__none' ? '' : v)}>
<SelectTrigger className="h-8 text-xs dg-input"><SelectValue placeholder="选择凭证" /></SelectTrigger>
<SelectContent>
<SelectItem value="__none">不关联</SelectItem>
<SelectItem value="__none">{t('deploy.notLinked')}</SelectItem>
{gitCredentials.map((c) => (
<SelectItem key={c.id as number} value={String(c.id)}>
<span className="flex items-center gap-1"><Globe className="h-3 w-3" />{String(c.name)}</span>
@@ -380,7 +380,7 @@ export function PipelineCreateDialog({ open, onOpenChange, onCreated }: Pipeline
</div>
{selectedStages.includes('image') && (
<div className="space-y-1.5">
<Label className="text-xs">镜像凭证 <span className="text-[var(--dg-red)]">*</span></Label>
<Label className="text-xs">{t('deploy.imageCredential')} <span className="text-[var(--dg-red)]">*</span></Label>
<Select value={String(newRegistryCredentialId)} onValueChange={(v) => setNewRegistryCredentialId(v === '__none' ? '' : v)}>
<SelectTrigger className="h-8 text-xs dg-input"><SelectValue placeholder="Docker 凭证" /></SelectTrigger>
<SelectContent>
@@ -398,7 +398,7 @@ export function PipelineCreateDialog({ open, onOpenChange, onCreated }: Pipeline
{deployTarget.deployTarget === 'k8s' && (
<div className="space-y-1.5">
<Label className="text-xs">Dockerfile 路径</Label>
<Label className="text-xs">{t('deploy.dockerfilePath')}</Label>
<Input
value={newDockerfile}
onChange={(e) => setNewDockerfile(e.target.value)}
@@ -409,7 +409,7 @@ export function PipelineCreateDialog({ open, onOpenChange, onCreated }: Pipeline
)}
<div className="space-y-1.5">
<Label className="text-xs">CI 阶段</Label>
<Label className="text-xs">{t('deploy.ciStages')}</Label>
<div className="flex flex-wrap gap-1.5">
{STAGE_DEFS.map((def) => {
const active = selectedStages.includes(def.key);
@@ -441,17 +441,17 @@ export function PipelineCreateDialog({ open, onOpenChange, onCreated }: Pipeline
className="flex items-center gap-1.5 text-xs text-[var(--dg-text-muted)] hover:text-[var(--dg-text-primary)]"
>
<Settings2 className="h-3.5 w-3.5" />
高级设置
{t('deploy.advancedSettings')}
<ChevronDown className={cn('h-3.5 w-3.5 transition-transform', advancedOpen && 'rotate-180')} />
</button>
</CollapsibleTrigger>
<CollapsibleContent className="mt-3 space-y-3 pt-3 border-t border-[var(--dg-border)]">
<div className="space-y-1.5">
<Label className="text-xs">构建节点</Label>
<Label className="text-xs">{t('deploy.buildNode')}</Label>
<Select value={buildNodeId || '__auto'} onValueChange={(v) => setBuildNodeId(v === '__auto' ? '' : v)}>
<SelectTrigger className="h-8 text-xs dg-input"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="__auto">自动调度(Master 优先)</SelectItem>
<SelectItem value="__auto">{t('deploy.autoSchedule')}</SelectItem>
{buildNodes.map((n) => (
<SelectItem key={n.id} value={String(n.id)}>
{n.name} ({n.running_count || 0}/{n.max_executors})
@@ -465,13 +465,13 @@ export function PipelineCreateDialog({ open, onOpenChange, onCreated }: Pipeline
<Input value={newJobName} onChange={(e) => setNewJobName(e.target.value)} className="h-8 text-xs font-mono dg-input" />
</div>
<div className="space-y-1.5">
<Label className="text-xs">触发方式</Label>
<Label className="text-xs">{t('deploy.triggerMethod')}</Label>
<Select value={newTriggerMode} onValueChange={setNewTriggerMode}>
<SelectTrigger className="h-8 text-xs dg-input"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="manual">手动</SelectItem>
<SelectItem value="git_push">代码推送</SelectItem>
<SelectItem value="cron">定时</SelectItem>
<SelectItem value="manual">{t('deploy.triggerManual')}</SelectItem>
<SelectItem value="git_push">{t('deploy.codePush')}</SelectItem>
<SelectItem value="cron">{t('deploy.triggerCron')}</SelectItem>
<SelectItem value="webhook">Webhook</SelectItem>
</SelectContent>
</Select>
@@ -494,7 +494,7 @@ export function PipelineCreateDialog({ open, onOpenChange, onCreated }: Pipeline
)}
<div className="space-y-1.5">
<div className="flex items-center justify-between">
<Label className="text-xs">环境变量</Label>
<Label className="text-xs">{t('deploy.envVars')}</Label>
<Button
type="button"
variant="ghost"
@@ -502,7 +502,7 @@ export function PipelineCreateDialog({ open, onOpenChange, onCreated }: Pipeline
className="h-6 text-[10px]"
onClick={() => setNewEnvVars([...newEnvVars, { key: '', value: '' }])}
>
<Plus className="h-3 w-3 mr-0.5" />添加
<Plus className="h-3 w-3 mr-0.5" />{t('deploy.addVar')}
</Button>
</div>
{newEnvVars.map((v, i) => (
@@ -543,12 +543,12 @@ export function PipelineCreateDialog({ open, onOpenChange, onCreated }: Pipeline
</Collapsible>
<div className="flex justify-between gap-2 pt-2 border-t border-[var(--dg-border)]">
<Button variant="ghost" onClick={() => setStep(1)}>上一步</Button>
<Button variant="ghost" onClick={() => setStep(1)}>{t('deploy.prevStep')}</Button>
<div className="flex gap-2">
<Button variant="outline" onClick={() => onOpenChange(false)}>取消</Button>
<Button onClick={handleCreate} disabled={saving}>
{saving ? <Loader2 className="h-4 w-4 animate-spin mr-1" /> : <Rocket className="h-4 w-4 mr-1" />}
{saving ? '创建中' : '创建'}
{saving ? t('deploy.creating') : t('deploy.pipeCreate')}
</Button>
</div>
</div>