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

@@ -1,4 +1,5 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { cn } from '@/lib/utils';
import { readFromClipboard } from '@/utils/clipboard';
import { rdpFilesApi, type RdpFileItem } from '@/api/server/rdp-files';
@@ -29,6 +30,7 @@ export function RdpToolsPanel({
onSyncToRemote,
onAutoExec,
}: RdpToolsPanelProps) {
const { t } = useTranslation();
const [tab, setTab] = useState<ToolsTab>('general');
const [localClipDraft, setLocalClipDraft] = useState('');
const [files, setFiles] = useState<RdpFileItem[]>([]);
@@ -43,7 +45,7 @@ export function RdpToolsPanel({
setFiles(data?.list ?? []);
} catch (err) {
setFiles([]);
toast.error(err instanceof Error ? err.message : '无法列出远端文件(SMB 445 可能未开放)');
toast.error(err instanceof Error ? err.message : t('server.listFileError'));
} finally {
setLoadingFiles(false);
}
@@ -59,17 +61,17 @@ export function RdpToolsPanel({
setLocalClipDraft(text);
return;
}
toast.info('无法自动读取剪贴板,请 Ctrl+V 粘贴(HTTPS 下可自动读取)');
toast.info(t('server.clipboardHint'));
};
const syncLocalClip = () => {
const trimmed = localClipDraft.trim();
if (!trimmed) {
toast.info('内容为空');
toast.info(t('server.emptyContent'));
return;
}
onSyncToRemote(trimmed);
toast.success('已同步到远端');
toast.success(t('server.syncedToRemote'));
};
const uploadWithFallback = async (file: File) => {
@@ -77,7 +79,7 @@ export function RdpToolsPanel({
try {
try {
await rdpFilesApi.upload(serverId, file);
toast.success(`${file.name} 已上传到 Download`);
toast.success(t('server.uploadedToDownload', { name: file.name }));
void refreshFiles();
return;
} catch {
@@ -85,9 +87,9 @@ export function RdpToolsPanel({
}
const staged = await rdpFilesApi.stage(serverId, file);
onAutoExec(staged.deploy_command);
toast.success(`${file.name} 已自动下发(SMB 不可用时的回退模式)`);
toast.success(t('server.autoSent', { name: file.name }));
} catch (err) {
toast.error(err instanceof Error ? err.message : '上传失败');
toast.error(err instanceof Error ? err.message : t('server.rdpUploadFailed'));
} finally {
setUploading(false);
}
@@ -102,9 +104,9 @@ export function RdpToolsPanel({
const handleDownload = async (name: string) => {
try {
await rdpFilesApi.download(serverId, name);
toast.success(`已开始下载 ${name}`);
toast.success(t('server.downloadStarted', { name }));
} catch (err) {
toast.error(err instanceof Error ? err.message : '下载失败');
toast.error(err instanceof Error ? err.message : t('server.rdpDownloadFailed'));
}
};
@@ -122,7 +124,7 @@ export function RdpToolsPanel({
)}
>
<Settings2 className="h-3.5 w-3.5" />
通用
{t('server.general')}
</button>
<button
type="button"
@@ -135,24 +137,24 @@ export function RdpToolsPanel({
)}
>
<FolderOpen className="h-3.5 w-3.5" />
文件
{t('server.rdpFiles')}
</button>
</div>
{tab === 'general' && (
<div className="p-3 space-y-3">
<div className="space-y-1.5">
<label className="text-xs text-[var(--dg-text-muted)]">远端剪贴板</label>
<label className="text-xs text-[var(--dg-text-muted)]">{t('server.remoteClipboard')}</label>
<textarea
readOnly
value={remoteClipboard}
rows={3}
placeholder="远端复制内容将显示在此"
placeholder={t('server.remoteClipPlaceholder')}
className="w-full resize-none rounded border border-[var(--dg-border)] bg-[var(--dg-bg-primary)] px-2 py-1.5 text-xs text-[var(--dg-text-secondary)] outline-none"
/>
</div>
<div className="space-y-1.5">
<label className="text-xs text-[var(--dg-text-muted)]">同步到远端</label>
<label className="text-xs text-[var(--dg-text-muted)]">{t('server.syncToRemote')}</label>
<textarea
value={localClipDraft}
onChange={(e) => setLocalClipDraft(e.target.value)}
@@ -162,11 +164,11 @@ export function RdpToolsPanel({
if (pasted) setLocalClipDraft(pasted);
}}
rows={3}
placeholder="点击输入框自动粘贴本地剪贴板"
placeholder={t('server.syncPlaceholder')}
className="w-full resize-none rounded border border-[var(--dg-border)] bg-[var(--dg-bg-secondary)] px-2 py-1.5 text-xs text-[var(--dg-text-primary)] outline-none"
/>
<Button type="button" size="sm" className="w-full" onClick={syncLocalClip}>
同步到远端
{t('server.syncButton')}
</Button>
</div>
</div>
@@ -194,12 +196,12 @@ export function RdpToolsPanel({
/>
<Button type="button" size="sm" className="w-full" disabled={uploading} onClick={() => fileInputRef.current?.click()}>
<Upload className="h-3.5 w-3.5 mr-1.5" />
{uploading ? '上传中...' : '上传文件'}
{uploading ? t('server.rdpUploading') : t('server.uploadFile')}
</Button>
<div className="max-h-40 overflow-y-auto rounded border border-[var(--dg-border)]">
{files.length === 0 ? (
<p className="px-2 py-3 text-[10px] text-[var(--dg-text-muted)] text-center">
{loadingFiles ? '加载中...' : '暂无文件(需 SMB 445 可达才能列出)'}
{loadingFiles ? t('server.rdpLoading') : t('server.noRdpFiles')}
</p>
) : (
files.map((f) => (
@@ -216,7 +218,7 @@ export function RdpToolsPanel({
)}
</div>
<p className="text-[10px] text-[var(--dg-text-muted)] leading-relaxed">
优先 SMB 直传(一键);若 445 不通则自动暂存到 AIOps 并由 RDP 执行下载命令。
{t('server.smbHint')}
</p>
</div>
)}