fix(i18n): 回退到稳定的 32% 覆盖率状态 (tsc + Vite 构建通过)

批量转换脚本引入的语法错误已回退,保留稳定的 i18n 转换:
- 翻译词典: 22 namespaces, 3600+ keys
- 已接入: 145+ 文件 (Dashboard/Login/K8s 主页面/Monitor/System/Deploy)
- 语言切换器在 Header 中可用 (🇨🇳/🇺🇸)
- tsc 零错误, Vite 构建成功
This commit is contained in:
attacker committed 2026-07-03 02:02:29 +08:00
1 parent ecec73989b
commit 72bfb0e12d
191 files changed
+5057 -7592

No files matched your search

@@ -1,4 +1,5 @@
import { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { Plus, Pencil, Trash2, RefreshCw, GitBranch, Container, Globe, Settings2 } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
@@ -18,7 +19,6 @@ import { deployApi } from '@/api';
import CredentialFormDialog from './CredentialFormDialog';
import ToolConfigTab from './ToolConfigTab';
import type { JsonData } from '@/types/common';
import { useTranslation } from 'react-i18next';
interface Repo {
id: number; name: string; description: string; url: string;
@@ -67,31 +67,31 @@ function EnvFormDialog({ open, onClose, onSaved, editData }: { open: boolean; on
toast.success(editData ? t('deploy.toast.updated') : t('deploy.toast.created'));
onSaved();
onClose();
} catch { toast.error(t('common.operationFailed')); }
} catch { toast.error(t('deploy.toast.operationFailed')); }
finally { setSubmitting(false); }
};
return (
<Dialog open={open} onOpenChange={onClose}>
<DialogContent>
<DialogHeader><DialogTitle>{editData ? t('pipeline.editEnv') : t('pipeline.addEnv')}</DialogTitle></DialogHeader>
<DialogHeader><DialogTitle>{editData ? t('deploy.env.editEnv') : t('deploy.env.addEnv')}</DialogTitle></DialogHeader>
<div className="space-y-3">
<div className="space-y-1"><Label>{t('pipeline.envLabel')}</Label><Input value={name} onChange={e => setName(e.target.value)} placeholder="production" /></div>
<div className="space-y-1"><Label>{t('system.userDisplayName')}</Label><Input value={displayName} onChange={e => setDisplayName(e.target.value)} placeholder=t('pipeline.prodEnv') /></div>
<div className="space-y-1"><Label>{t('pipeline.envType')}</Label>
<div className="space-y-1"><Label>{t('deploy.env.envLabel')}</Label><Input value={name} onChange={e => setName(e.target.value)} placeholder="production" /></div>
<div className="space-y-1"><Label>{t('deploy.table.displayName')}</Label><Input value={displayName} onChange={e => setDisplayName(e.target.value)} placeholder={t('deploy.env.prodEnv')} /></div>
<div className="space-y-1"><Label>{t('deploy.env.envType')}</Label>
<Select value={envType} onValueChange={setEnvType}>
<SelectTrigger><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="production">{t('pipeline.prodEnv')}</SelectItem>
<SelectItem value="staging">{t('pipeline.staging')}</SelectItem>
<SelectItem value="testing">{t('pipeline.testEnv')}</SelectItem>
<SelectItem value="development">{t('pipeline.devEnv')}</SelectItem>
<SelectItem value="production">{t('deploy.env.prodEnv')}</SelectItem>
<SelectItem value="staging">{t('deploy.env.staging')}</SelectItem>
<SelectItem value="testing">{t('deploy.env.testEnv')}</SelectItem>
<SelectItem value="development">{t('deploy.env.devEnv')}</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-1"><Label>{t('deploy.deploycredentials.sc06ac4')}</Label><Input value={color} onChange={e => setColor(e.target.value)} type="color" className="w-16 h-9 p-1" /></div>
<div className="space-y-1"><Label>{t('deploy.deploycredentials.s65dd48')}</Label><Input value={sortOrder} onChange={e => setSortOrder(Number(e.target.value))} type="number" /></div>
<div className="space-y-1"><Label>{t('common.description')}</Label><Input value={description} onChange={e => setDescription(e.target.value)} placeholder=t('common.optional') /></div>
<div className="space-y-1"><Label>{t('deploy.table.color')}</Label><Input value={color} onChange={e => setColor(e.target.value)} type="color" className="w-16 h-9 p-1" /></div>
<div className="space-y-1"><Label>{t('deploy.table.sortOrder')}</Label><Input value={sortOrder} onChange={e => setSortOrder(Number(e.target.value))} type="number" /></div>
<div className="space-y-1"><Label>{t('deploy.form.desc')}</Label><Input value={description} onChange={e => setDescription(e.target.value)} placeholder={t('common.optional')} /></div>
</div>
<div className="flex justify-end gap-2 mt-4">
<Button variant="outline" onClick={onClose}>{t('common.cancel')}</Button>
@@ -136,32 +136,32 @@ export default function DeployCredentials() {
useEffect(() => { fetchAll(); }, []);
const handleDelete = async (type: 'repo' | 'registry' | 'env', item: JsonData) => {
const ok = await _confirm({ title: t('common.delete'), description: t('deploy.deploycredentials.s999d97', { item.name || item.display_name: item.name || item.display_name }), confirmText: t('common.delete'), variant: 'destructive' });
const ok = await _confirm({ title: t('common.delete'), description: t('deploy.toast.deleteConfirm', { name: item.name || item.display_name }), confirmText: t('common.delete'), variant: 'destructive' });
if (!ok) return;
try {
if (type === 'repo') await deployApi.deleteRepo(item.id);
else if (type === 'registry') await deployApi.deleteRegistry(item.id);
else await deployApi.deleteEnvironment(item.id);
toast.success(t('alert.deleted'));
toast.success(t('deploy.toast.deleted'));
fetchAll();
} catch { toast.error(t('common.deleteFailed')); }
} catch { toast.error(t('deploy.toast.deleteFailed')); }
};
const envTypeLabel: Record<string, string> = { production: t('pipeline.production'), staging: t('pipeline.staging'), testing: t('common.test'), development: t('pipeline.development') };
const envTypeLabel: Record<string, string> = { production: t('deploy.env.production'), staging: t('deploy.env.staging'), testing: t('deploy.env.testing'), development: t('deploy.env.development') };
const pageTabs = [
{ id: 'repos', label: t('deploy.deploycredentials.sd71d03'), badge: repos.length || undefined },
{ id: 'registries', label: t('pipeline.registry'), badge: registries.length || undefined },
{ id: 'environments', label: t('pipeline.envManage'), badge: environments.length || undefined },
{ id: 'build-nodes', label: t('pipeline.buildNode') },
{ id: 'repos', label: t('deploy.credForm.gitRepo'), badge: repos.length || undefined },
{ id: 'registries', label: t('deploy.credForm.imageRegistry'), badge: registries.length || undefined },
{ id: 'environments', label: t('deploy.label.envManage'), badge: environments.length || undefined },
{ id: 'build-nodes', label: t('deploy.deployNav.buildNodes') },
{ id: 'tools', label: t('deploy.tool.title') },
];
return (
<PageShell
variant="list"
title=t('menu.deployConfig')
subtitle=t('deploy.deploycredentials.s15a730')
title={t('deploy.label.releaseConfig')}
subtitle={t('deploy.credForm.pageSubtitle')}
icon={<Settings2 className="h-5 w-5" />}
loading={loading}
tabs={pageTabs}
@@ -170,13 +170,13 @@ export default function DeployCredentials() {
actions={activeTab === 'build-nodes' ? undefined : (
<>
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={fetchAll}>
<RefreshCw className="w-4 h-4 mr-1" />刷新
<RefreshCw className="w-4 h-4 mr-1" />{t('common.refresh')}
</Button>
<Button size="sm" className="dg-btn-primary" onClick={() => { setEditType('repo'); setEditingId(null); setEditorOpen(true); }}>
<Plus className="w-4 h-4 mr-1" />新增 Git 仓库
<Plus className="w-4 h-4 mr-1" />{t('deploy.credForm.newRepo')}
</Button>
<Button size="sm" variant="secondary" onClick={() => { setEditType('registry'); setEditingId(null); setEditorOpen(true); }}>
<Plus className="w-4 h-4 mr-1" />新增镜像仓库
<Plus className="w-4 h-4 mr-1" />{t('deploy.credForm.newRegistry')}
</Button>
</>
)}
@@ -184,17 +184,17 @@ export default function DeployCredentials() {
>
{activeTab === 'repos' && (
repos.length === 0 ? (
<PageEmpty icon={<GitBranch className="h-10 w-10" />} title=t('deploy.deploycredentials.sb19733') />
<PageEmpty icon={<GitBranch className="h-10 w-10" />} title={t('deploy.credForm.noRepo')} />
) : (
<ContentPanel noPadding>
<div className="overflow-hidden">
<table className="w-full text-sm">
<thead className="bg-[var(--dg-bg-tertiary)] border-b border-[var(--dg-border)]">
<tr>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">{t('common.name')}</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">{t('deploy.table.name')}</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)] hidden md:table-cell">{t('deploy.table.repoUrl')}</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">{t('deploy.cred.authMethod')}</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">{t('common.status')}</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">{t('deploy.table.status')}</th>
<th className="px-3 py-2 text-right font-medium text-[var(--dg-text-secondary)]">{t('common.actions')}</th>
</tr>
</thead>
@@ -224,18 +224,18 @@ export default function DeployCredentials() {
{activeTab === 'registries' && (
registries.length === 0 ? (
<PageEmpty icon={<Container className="h-10 w-10" />} title=t('deploy.deploycredentials.sdf33f0') />
<PageEmpty icon={<Container className="h-10 w-10" />} title={t('deploy.credForm.noRegistry')} />
) : (
<ContentPanel noPadding>
<div className="overflow-hidden">
<table className="w-full text-sm">
<thead className="bg-[var(--dg-bg-tertiary)] border-b border-[var(--dg-border)]">
<tr>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">{t('common.name')}</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">{t('deploy.table.name')}</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)] hidden md:table-cell">Registry URL</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)] hidden sm:table-cell">{t('k8s.column.namespace')}</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">{t('deploy.deploycredentials.sb7158a')}</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">{t('common.status')}</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)] hidden sm:table-cell">{t('deploy.credForm.namespace')}</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">{t('deploy.cred.authMethod')}</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">{t('deploy.table.status')}</th>
<th className="px-3 py-2 text-right font-medium text-[var(--dg-text-secondary)]">{t('common.actions')}</th>
</tr>
</thead>
@@ -274,8 +274,8 @@ export default function DeployCredentials() {
{environments.length === 0 ? (
<PageEmpty
icon={<Globe className="h-10 w-10" />}
title=t('pipeline.noEnv')
description=t('deploy.deploycredentials.sac4fb6')
title="暂无环境配置"
description='点击"新增环境"创建'
action={(
<Button size="sm" onClick={() => { setEditEnv(null); setEnvDialogOpen(true); }}>
<Plus className="w-4 h-4 mr-1" />新增环境
@@ -288,13 +288,13 @@ export default function DeployCredentials() {
<table className="w-full text-sm">
<thead className="bg-[var(--dg-bg-tertiary)] border-b border-[var(--dg-border)]">
<tr>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">{t('pipeline.envLabel')}</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">{t('system.userDisplayName')}</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">{t('common.type')}</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">{t('common.color')}</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">{t('common.sort')}</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">{t('common.status')}</th>
<th className="px-3 py-2 text-right font-medium text-[var(--dg-text-secondary)]">{t('common.actions')}</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">环境标识</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">显示名称</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">类型</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">颜色</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">排序</th>
<th className="px-3 py-2 text-left font-medium text-[var(--dg-text-secondary)]">状态</th>
<th className="px-3 py-2 text-right font-medium text-[var(--dg-text-secondary)]">操作</th>
</tr>
</thead>
<tbody>
@@ -311,7 +311,7 @@ export default function DeployCredentials() {
<td className="px-3 py-2"><div className="w-6 h-6 rounded border" style={{ backgroundColor: env.color }} /></td>
<td className="px-3 py-2 text-[var(--dg-text-secondary)]">{env.sort_order}</td>
<td className="px-3 py-2">
<Badge className={env.enabled ? 'bg-emerald-500/20 dg-text-green border-emerald-500/30' : 'bg-[var(--dg-bg-active)]/20 dg-text-secondary border-[var(--dg-border)]'}>{env.enabled ? t('common.enable') : t('common.disable')}</Badge>
<Badge className={env.enabled ? 'bg-emerald-500/20 dg-text-green border-emerald-500/30' : 'bg-[var(--dg-bg-active)]/20 dg-text-secondary border-[var(--dg-border)]'}>{env.enabled ? '启用' : '禁用'}</Badge>
</td>
<td className="px-3 py-2 text-right">
<div className="flex justify-end gap-1">