refactor(i18n): U23 Task 模块 i18n 治理 — 4 文件 phantom key + hash key + 硬编码中文清零
- CronJobManager/CronJobFormDialog/ScriptManager/RemoteExecPage
从 import i18n 全局调用改为 useTranslation() hook
- 42 处 phantom key (i18n.t('Chinese text') → 语义化键)
- 28 个孤儿 hash key 清理 (cronjobformdial.s*/remoteexecpaget.s*)
- 50+ 处硬编码 JSX 中文 → t() 调用
- 新增 task.cronJob.*/task.scriptList.*/task.remoteExec.* 子命名空间
- zh/en 6825→6886 keys 对齐
- tsc -b=0, npm build✓ (17.46s), ESLint=0/45/0, go build=0
- 17/17 集成回归测试通过
This commit is contained in:
1 parent
a8f6fd190b
commit
de6f9454fc
7 files changed
+361
-229
No files matched your search
@@ -1,5 +1,6 @@
|
||||
// ScriptManager — 脚本库主组件 // Logan.Li
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Plus, Search, RotateCcw, Trash2, FileCode } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
@@ -23,9 +24,9 @@ import { PageShell } from '@/components/layout/PageShell';
|
||||
import { ContentPanel } from '@/components/layout/ContentPanel';
|
||||
import { PageEmpty } from '@/components/layout/PageEmpty';
|
||||
import type { JsonData } from '@/types/common';
|
||||
import i18n from '@/i18n';
|
||||
|
||||
export function ScriptManager() {
|
||||
const { t } = useTranslation();
|
||||
const _confirm = useConfirm();
|
||||
const { hasAnyPermission, isAdmin } = usePermission();
|
||||
const canWrite = isAdmin || hasAnyPermission(['auto:script', 'auto:cron', 'auto:ansible']);
|
||||
@@ -52,7 +53,7 @@ export function ScriptManager() {
|
||||
const items = (res as JsonData)?.list || [];
|
||||
setScripts(items.map(mapScript));
|
||||
} catch {
|
||||
toast.error(i18n.t('获取脚本列表失败'));
|
||||
toast.error(t('task.scriptList.toastFetchFailed'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -73,6 +74,7 @@ export function ScriptManager() {
|
||||
useEffect(() => {
|
||||
fetchScripts();
|
||||
fetchRuntime();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const openCreate = () => {
|
||||
@@ -113,7 +115,7 @@ export function ScriptManager() {
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!formData.name.trim() || !formData.content.trim()) {
|
||||
toast.error(i18n.t('获取脚本列表失败'));
|
||||
toast.error(t('task.scriptList.toastNameRequired'));
|
||||
return;
|
||||
}
|
||||
const payload = {
|
||||
@@ -127,49 +129,49 @@ export function ScriptManager() {
|
||||
try {
|
||||
if (editingId !== null) {
|
||||
await scriptApi.updateScript(editingId, payload);
|
||||
toast.success(i18n.t('脚本更新成功'));
|
||||
toast.success(t('task.scriptList.toastSaved'));
|
||||
} else {
|
||||
await scriptApi.createScript(payload);
|
||||
toast.success(i18n.t('脚本更新成功'));
|
||||
toast.success(t('task.scriptList.toastSaved'));
|
||||
}
|
||||
setEditorOpen(false);
|
||||
fetchScripts();
|
||||
} catch (e) {
|
||||
toast.error(e instanceof Error ? e.message : (editingId !== null ? i18n.t('kubernetes.deployment.imageUpdateFailed') : i18n.t('monitor.dashboard.custom.toastCreateFailed')));
|
||||
toast.error(e instanceof Error ? e.message : (editingId !== null ? t('kubernetes.deployment.imageUpdateFailed') : t('monitor.dashboard.custom.toastCreateFailed')));
|
||||
}
|
||||
};
|
||||
|
||||
const handleClone = async (sc: Script) => {
|
||||
try {
|
||||
await scriptApi.createScript({
|
||||
name: `${sc.name} (副本)`,
|
||||
name: `${sc.name} ${t('task.scriptList.copySuffix')}`,
|
||||
code: genCode(),
|
||||
description: sc.description,
|
||||
type: sc.type,
|
||||
content: sc.content,
|
||||
timeout: sc.timeout,
|
||||
});
|
||||
toast.success(i18n.t('common.copySuccess'));
|
||||
toast.success(t('common.copySuccess'));
|
||||
fetchScripts();
|
||||
} catch (e) {
|
||||
toast.error(e instanceof Error ? e.message : i18n.t('monitor.logPanel.copyFailed'));
|
||||
toast.error(e instanceof Error ? e.message : t('monitor.logPanel.copyFailed'));
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (sc: Script) => {
|
||||
const ok = await _confirm({
|
||||
title: i18n.t('automation.deleteScript'),
|
||||
description: `确定删除「${sc.name}」吗?此操作不可恢复。`,
|
||||
confirmText: i18n.t('kubernetes.storage.delete2'),
|
||||
title: t('task.scriptList.delete'),
|
||||
description: t('task.scriptList.confirmDelete', { name: sc.name }),
|
||||
confirmText: t('kubernetes.storage.delete2'),
|
||||
variant: 'destructive',
|
||||
});
|
||||
if (!ok) return;
|
||||
try {
|
||||
await scriptApi.deleteScript(sc.id);
|
||||
toast.success(i18n.t('kubernetes.msg.deleteSuccess'));
|
||||
toast.success(t('kubernetes.msg.deleteSuccess'));
|
||||
fetchScripts();
|
||||
} catch (e) {
|
||||
toast.error(e instanceof Error ? e.message : i18n.t('kubernetes.msg.deleteFailed2'));
|
||||
toast.error(e instanceof Error ? e.message : t('kubernetes.msg.deleteFailed2'));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -194,19 +196,19 @@ export function ScriptManager() {
|
||||
const handleBatchDelete = async () => {
|
||||
if (selectedIds.length === 0) return;
|
||||
const ok = await _confirm({
|
||||
title: i18n.t('删除脚本'),
|
||||
description: `确定删除选中的 ${selectedIds.length} 个脚本吗?此操作不可恢复。`,
|
||||
confirmText: i18n.t('system.terminalBatchDelete'),
|
||||
title: t('task.scriptList.delete'),
|
||||
description: t('task.scriptList.batchDeleteConfirm', { count: selectedIds.length }),
|
||||
confirmText: t('system.terminalBatchDelete'),
|
||||
variant: 'destructive',
|
||||
});
|
||||
if (!ok) return;
|
||||
try {
|
||||
await scriptApi.batchDelete(selectedIds);
|
||||
toast.success(`已删除 ${selectedIds.length} 个脚本`);
|
||||
toast.success(t('task.scriptList.toastDeleted', { count: selectedIds.length }));
|
||||
setSelectedIds([]);
|
||||
fetchScripts();
|
||||
} catch (e) {
|
||||
toast.error(e instanceof Error ? e.message : i18n.t('system.batchDeleteFailed'));
|
||||
toast.error(e instanceof Error ? e.message : t('system.batchDeleteFailed'));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -216,7 +218,7 @@ export function ScriptManager() {
|
||||
<div className="relative flex-1">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 dg-text-muted" />
|
||||
<Input
|
||||
placeholder={i18n.t('deploy.label.searchPlaceholder')}
|
||||
placeholder={t('deploy.label.searchPlaceholder')}
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="pl-10 dg-input"
|
||||
@@ -224,10 +226,10 @@ export function ScriptManager() {
|
||||
</div>
|
||||
<Select value={typeFilter} onValueChange={setTypeFilter}>
|
||||
<SelectTrigger className="w-[140px] dg-input">
|
||||
<SelectValue placeholder={i18n.t('system.credentialType')} />
|
||||
<SelectValue placeholder={t('system.credentialType')} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all"> {i18n.t('k8s.topology.allTypes')} </SelectItem>
|
||||
<SelectItem value="all"> {t('k8s.topology.allTypes')} </SelectItem>
|
||||
{scriptTypes.map(t => (
|
||||
<SelectItem key={t.value} value={t.value}>{t.label}</SelectItem>
|
||||
))}
|
||||
@@ -241,8 +243,8 @@ export function ScriptManager() {
|
||||
<>
|
||||
<PageShell
|
||||
variant="list"
|
||||
title={i18n.t('automation.scripts')}
|
||||
subtitle={i18n.t('脚本编辑与管理')}
|
||||
title={t('automation.scripts')}
|
||||
subtitle={t('task.scriptList.subtitle')}
|
||||
icon={<FileCode className="h-5 w-5" />}
|
||||
toolbar={toolbar}
|
||||
loading={loading}
|
||||
@@ -250,12 +252,12 @@ export function ScriptManager() {
|
||||
<>
|
||||
<Button variant="outline" className="dg-btn-secondary" onClick={fetchScripts}>
|
||||
<RotateCcw className="w-4 h-4 mr-2" />
|
||||
刷新
|
||||
{t('task.scriptList.refresh')}
|
||||
</Button>
|
||||
{canWrite && (
|
||||
<Button className="dg-btn-primary" onClick={openCreate}>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
新建脚本
|
||||
{t('task.scriptList.create')}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
@@ -273,12 +275,12 @@ export function ScriptManager() {
|
||||
className="border-[var(--dg-border)]"
|
||||
/>
|
||||
<span className="text-sm dg-text-secondary">
|
||||
已选择 {selectedIds.length} 项 / 共 {filteredScripts.length} 项
|
||||
{t('task.scriptList.selectedCount', { selected: selectedIds.length, total: filteredScripts.length })}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={() => setSelectedIds([])}>
|
||||
取消选择
|
||||
{t('task.scriptList.cancelSelect')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -287,7 +289,7 @@ export function ScriptManager() {
|
||||
onClick={handleBatchDelete}
|
||||
>
|
||||
<Trash2 className="w-4 h-4 mr-2" />
|
||||
批量删除
|
||||
{t('task.scriptList.batchDelete')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -295,12 +297,12 @@ export function ScriptManager() {
|
||||
|
||||
{filteredScripts.length === 0 ? (
|
||||
<PageEmpty
|
||||
title={scripts.length === 0 ? i18n.t('automation.noScript') : i18n.t('暂无脚本')}
|
||||
description={scripts.length === 0 ? i18n.t('点击「新建脚本」创建第一个') : undefined}
|
||||
title={scripts.length === 0 ? t('automation.noScript') : t('task.scriptList.empty')}
|
||||
description={scripts.length === 0 ? t('task.scriptList.emptyHint') : undefined}
|
||||
action={scripts.length === 0 && canWrite ? (
|
||||
<Button className="dg-btn-primary" onClick={openCreate}>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
新建脚本
|
||||
{t('task.scriptList.create')}
|
||||
</Button>
|
||||
) : undefined}
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user