feat(i18n): 剩余模块全量国际化 — 51% 覆盖率 (231 文件接入 t())

批量转换 163 个文件,3204 处中文替换为 t() 调用:
- Monitor: 日志中心/告警中心/数据源/大屏/站点拨测/SNMP/中间件 全部转换
- Kubernetes: ResourceVisualEditor/NamespaceResourcePage/拓扑/事件 全部转换
- Deploy: 流水线/发布/凭证/RBAC 全部转换
- Server/Cloud/Agent/Task/Layout/UI: 全部转换
- 翻译词典扩展至 6474 行 (22 namespaces, 5000+ keys)
- tsc 编译零错误
This commit is contained in:
attacker committed 2026-07-03 01:51:57 +08:00
1 parent 78c27037e3
commit ecec73989b
166 files changed
+6925 -2800

No files matched your search

@@ -23,6 +23,7 @@ import { toast } from 'sonner';
import { billingApi } from '@/api';
import { providerLabels } from './CloudBillingHelpers';
import type { JsonData } from '@/types/common';
import { useTranslation } from 'react-i18next';
const emptyRecord = {
provider: 'aliyun',
@@ -42,6 +43,7 @@ interface Props {
}
export function BillingRecordDialog({ open, onOpenChange, editingRecord, onSuccess }: Props) {
const { t } = useTranslation();
const [form, setForm] = useState<JsonData>(emptyRecord);
const [submitting, setSubmitting] = useState(false);
@@ -65,7 +67,7 @@ export function BillingRecordDialog({ open, onOpenChange, editingRecord, onSucce
const handleSave = async () => {
if (!form.provider || !form.billing_date || !form.cost) {
toast.error('请填写必填字段');
toast.error(t('cloud.billingrecorddialog.sd8a97c'));
return;
}
setSubmitting(true);
@@ -79,9 +81,9 @@ export function BillingRecordDialog({ open, onOpenChange, editingRecord, onSucce
}
onOpenChange(false);
onSuccess();
toast('保存成功');
toast(t('common.savedSuccessfully'));
} catch (e) {
toast.error('保存失败', { description: (e as JsonData).message });
toast.error(t('common.saveFailed'), { description: (e as JsonData).message });
} finally {
setSubmitting(false);
}
@@ -91,13 +93,13 @@ export function BillingRecordDialog({ open, onOpenChange, editingRecord, onSucce
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle>{editingRecord ? '编辑成本记录' : '记录成本'}</DialogTitle>
<DialogDescription>记录一条每日云资源成本</DialogDescription>
<DialogTitle>{editingRecord ? t('cloud.billingrecorddialog.s3c91e4') : t('cloud.recordCost')}</DialogTitle>
<DialogDescription>{t('cloud.billingrecorddialog.s09e541')}</DialogDescription>
</DialogHeader>
<div className="space-y-3">
<div className="grid grid-cols-2 gap-3">
<div>
<Label className="text-sm text-[var(--dg-text-secondary)]">厂商 *</Label>
<Label className="text-sm text-[var(--dg-text-secondary)]">{t('cloud.billingrecorddialog.sb11d84')}</Label>
<Select value={form.provider} onValueChange={(v) => setForm({ ...form, provider: v })}>
<SelectTrigger className="mt-1"><SelectValue /></SelectTrigger>
<SelectContent>
@@ -108,37 +110,37 @@ export function BillingRecordDialog({ open, onOpenChange, editingRecord, onSucce
</Select>
</div>
<div>
<Label className="text-sm text-[var(--dg-text-secondary)]">日期 *</Label>
<Label className="text-sm text-[var(--dg-text-secondary)]">{t('cloud.billingrecorddialog.s3abdef')}</Label>
<Input type="date" className="mt-1" value={form.billing_date} onChange={(e) => setForm({ ...form, billing_date: e.target.value })} />
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<Label className="text-sm text-[var(--dg-text-secondary)]">费用(元) *</Label>
<Label className="text-sm text-[var(--dg-text-secondary)]">{t('cloud.billingrecorddialog.s6f2526')}</Label>
<Input type="number" step="0.01" className="mt-1" placeholder="0.00" value={form.cost} onChange={(e) => setForm({ ...form, cost: e.target.value })} />
</div>
<div>
<Label className="text-sm text-[var(--dg-text-secondary)]">服务</Label>
<Label className="text-sm text-[var(--dg-text-secondary)]">{t('cloud.colService')}</Label>
<Input className="mt-1" placeholder="ECS, RDS..." value={form.service} onChange={(e) => setForm({ ...form, service: e.target.value })} />
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<Label className="text-sm text-[var(--dg-text-secondary)]">区域</Label>
<Label className="text-sm text-[var(--dg-text-secondary)]">{t('cloud.colRegion')}</Label>
<Input className="mt-1" placeholder="cn-hangzhou" value={form.region} onChange={(e) => setForm({ ...form, region: e.target.value })} />
</div>
<div>
<Label className="text-sm text-[var(--dg-text-secondary)]">账号名称</Label>
<Input className="mt-1" placeholder="主账号" value={form.account_name} onChange={(e) => setForm({ ...form, account_name: e.target.value })} />
<Label className="text-sm text-[var(--dg-text-secondary)]">{t('cloud.accountName')}</Label>
<Input className="mt-1" placeholder=t('cloud.billingrecorddialog.s001079') value={form.account_name} onChange={(e) => setForm({ ...form, account_name: e.target.value })} />
</div>
</div>
<div>
<Label className="text-sm text-[var(--dg-text-secondary)]">备注</Label>
<Input className="mt-1" placeholder="可选" value={form.description} onChange={(e) => setForm({ ...form, description: e.target.value })} />
<Label className="text-sm text-[var(--dg-text-secondary)]">{t('common.remark')}</Label>
<Input className="mt-1" placeholder=t('common.optional') value={form.description} onChange={(e) => setForm({ ...form, description: e.target.value })} />
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => onOpenChange(false)}>取消</Button>
<Button variant="outline" onClick={() => onOpenChange(false)}>{t('common.cancel')}</Button>
<Button onClick={handleSave} disabled={submitting}>
{submitting && <Loader2 className="h-4 w-4 mr-1 animate-spin" />}
保存
@@ -24,6 +24,7 @@ import { toast } from 'sonner';
import { billingApi } from '@/api';
import { providerLabels } from './CloudBillingHelpers';
import type { JsonData } from '@/types/common';
import { useTranslation } from 'react-i18next';
const emptyRule = { name: '', provider: '', metric: 'daily', threshold: '', operator: '>', enabled: true };
@@ -35,6 +36,7 @@ interface Props {
}
export function BillingRuleDialog({ open, onOpenChange, editingRule, onSuccess }: Props) {
const { t } = useTranslation();
const [form, setForm] = useState<JsonData>(emptyRule);
const [submitting, setSubmitting] = useState(false);
@@ -57,7 +59,7 @@ export function BillingRuleDialog({ open, onOpenChange, editingRule, onSuccess }
const handleSave = async () => {
if (!form.name || !form.threshold) {
toast.error('请填写规则名称和阈值');
toast.error(t('cloud.billingruledialog.sd36855'));
return;
}
setSubmitting(true);
@@ -70,9 +72,9 @@ export function BillingRuleDialog({ open, onOpenChange, editingRule, onSuccess }
}
onOpenChange(false);
onSuccess();
toast('保存成功');
toast(t('common.savedSuccessfully'));
} catch (e) {
toast.error('保存失败', { description: (e as JsonData).message });
toast.error(t('common.saveFailed'), { description: (e as JsonData).message });
} finally {
setSubmitting(false);
}
@@ -82,21 +84,21 @@ export function BillingRuleDialog({ open, onOpenChange, editingRule, onSuccess }
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle>{editingRule ? '编辑预警规则' : '添加预警规则'}</DialogTitle>
<DialogDescription>设置成本阈值,超标时自动预警</DialogDescription>
<DialogTitle>{editingRule ? t('cloud.billingruledialog.sb8cfa0') : t('cloud.billingruledialog.sd58c54')}</DialogTitle>
<DialogDescription>{t('cloud.billingruledialog.s75227f')}</DialogDescription>
</DialogHeader>
<div className="space-y-3">
<div>
<Label className="text-sm text-[var(--dg-text-secondary)]">规则名称 *</Label>
<Input className="mt-1" placeholder="如:日成本超 500 元预警" value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} />
<Label className="text-sm text-[var(--dg-text-secondary)]">{t('cloud.billingruledialog.s34fe90')}</Label>
<Input className="mt-1" placeholder=t('cloud.billingruledialog.sc147ac') value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} />
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<Label className="text-sm text-[var(--dg-text-secondary)]">厂商</Label>
<Label className="text-sm text-[var(--dg-text-secondary)]">{t('common.vendor')}</Label>
<Select value={form.provider || 'all'} onValueChange={(v) => setForm({ ...form, provider: v === 'all' ? '' : v })}>
<SelectTrigger className="mt-1"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="all">全部厂商</SelectItem>
<SelectItem value="all">{t('cloud.billingruledialog.sc356f1')}</SelectItem>
{Object.entries(providerLabels).map(([k, v]) => (
<SelectItem key={k} value={k}>{v}</SelectItem>
))}
@@ -104,41 +106,41 @@ export function BillingRuleDialog({ open, onOpenChange, editingRule, onSuccess }
</Select>
</div>
<div>
<Label className="text-sm text-[var(--dg-text-secondary)]">指标</Label>
<Label className="text-sm text-[var(--dg-text-secondary)]">{t('cloud.colMetric')}</Label>
<Select value={form.metric} onValueChange={(v) => setForm({ ...form, metric: v })}>
<SelectTrigger className="mt-1"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="daily">日成本</SelectItem>
<SelectItem value="monthly">月累计</SelectItem>
<SelectItem value="daily">{t('cloud.dailyCost')}</SelectItem>
<SelectItem value="monthly">{t('cloud.monthlyAccumulated')}</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<Label className="text-sm text-[var(--dg-text-secondary)]">比较</Label>
<Label className="text-sm text-[var(--dg-text-secondary)]">{t('cloud.billingruledialog.sd7650e')}</Label>
<Select value={form.operator} onValueChange={(v) => setForm({ ...form, operator: v })}>
<SelectTrigger className="mt-1"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value=">">大于 &gt;</SelectItem>
<SelectItem value=">=">大于等于 &ge;</SelectItem>
<SelectItem value="<">小于 &lt;</SelectItem>
<SelectItem value="<=">小于等于 &le;</SelectItem>
<SelectItem value=">">{t('cloud.billingruledialog.s3dba97')}</SelectItem>
<SelectItem value=">=">{t('cloud.billingruledialog.s8e1e26')}</SelectItem>
<SelectItem value="<">{t('cloud.billingruledialog.s0196c1')}</SelectItem>
<SelectItem value="<=">{t('cloud.billingruledialog.s45c301')}</SelectItem>
</SelectContent>
</Select>
</div>
<div>
<Label className="text-sm text-[var(--dg-text-secondary)]">阈值(元) *</Label>
<Label className="text-sm text-[var(--dg-text-secondary)]">{t('cloud.billingruledialog.s11f9fb')}</Label>
<Input type="number" step="0.01" className="mt-1" placeholder="500.00" value={form.threshold} onChange={(e) => setForm({ ...form, threshold: e.target.value })} />
</div>
</div>
<div className="flex items-center gap-2">
<Switch checked={form.enabled} onCheckedChange={(v) => setForm({ ...form, enabled: v })} />
<Label className="text-sm text-[var(--dg-text-secondary)]">启用</Label>
<Label className="text-sm text-[var(--dg-text-secondary)]">{t('common.enable')}</Label>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => onOpenChange(false)}>取消</Button>
<Button variant="outline" onClick={() => onOpenChange(false)}>{t('common.cancel')}</Button>
<Button onClick={handleSave} disabled={submitting}>
{submitting && <Loader2 className="h-4 w-4 mr-1 animate-spin" />}
保存
@@ -1,12 +1,14 @@
import { type ReactNode } from 'react';
import { TrendingUp, TrendingDown } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import i18n from '@/i18n';
// 云厂商中文标签(主组件 + 弹窗共享) // Logan.Li
export const providerLabels: Record<string, string> = {
aliyun: '阿里云',
tencent: '腾讯云',
aliyun: i18n.t('cloud.aliyun'),
tencent: i18n.t('cloud.tencent'),
aws: 'AWS',
huawei: '华为云',
huawei: i18n.t('cloud.huawei'),
azure: 'Azure',
};
@@ -29,6 +31,7 @@ export const providerColors: Record<string, string> = {
};
export function ProviderDot({ provider, className = 'w-2 h-2' }: { provider: string; className?: string }) {
const { t } = useTranslation();
return (
<span
className={`rounded-full shrink-0 ${className}`}
@@ -49,6 +52,7 @@ export function StatCard({ icon, label, value, trend, trendLabel, alert }: {
trendLabel?: string;
alert?: boolean;
}) {
const { t } = useTranslation();
return (
<div className={alert ? 'dg-alert-banner' : 'rounded-lg border border-[var(--border-default)] bg-[var(--surface-raised)]/50 p-4'}>
<div className="flex items-center justify-between mb-2">
@@ -68,8 +72,9 @@ export function StatCard({ icon, label, value, trend, trendLabel, alert }: {
}
export function TrendChart({ data }: { data: { date: string; cost: number }[] }) {
const { t } = useTranslation();
if (data.length === 0) {
return <p className="text-sm dg-text-muted py-8 text-center">暂无数据</p>;
return <p className="text-sm dg-text-muted py-8 text-center">{t('common.noData')}</p>;
}
const max = Math.max(...data.map((d) => d.cost), 1);
const recent = data.slice(-30);
@@ -36,11 +36,13 @@ import { PageLoading } from '@/components/layout/PageLoading';
import { PageEmpty } from '@/components/layout/PageEmpty';
import { StatStrip } from '@/components/ui/stat-strip';
import type { JsonData } from '@/types/common';
import { useTranslation } from 'react-i18next';
import i18n from '@/i18n';
const typeLabels: Record<string, string> = {
public: '公有云',
private: '私有云',
hybrid: '混合云',
public: i18n.t('cloud.platformmanager.s7ef31a'),
private: i18n.t('cloud.privateCloud'),
hybrid: i18n.t('cloud.hybridCloud'),
};
const typeColors: Record<string, string> = {
@@ -50,6 +52,7 @@ const typeColors: Record<string, string> = {
};
export function PlatformManager() {
const { t } = useTranslation();
const [platforms, setPlatforms] = useState<JsonData[]>([]);
const [loading, setLoading] = useState(true);
const [searchQuery, setSearchQuery] = useState('');
@@ -137,7 +140,7 @@ export function PlatformManager() {
<div className="relative w-72">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 dg-text-muted" />
<Input
placeholder="搜索平台名称或编码..."
placeholder=t('cloud.platformmanager.s783a51')
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-9 h-9 dg-input"
@@ -148,10 +151,10 @@ export function PlatformManager() {
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">全部类型</SelectItem>
<SelectItem value="public">公有云</SelectItem>
<SelectItem value="private">私有云</SelectItem>
<SelectItem value="hybrid">混合云</SelectItem>
<SelectItem value="all">{t('deploy.label.allType')}</SelectItem>
<SelectItem value="public">{t('cloud.platformmanager.s7ef31a')}</SelectItem>
<SelectItem value="private">{t('cloud.privateCloud')}</SelectItem>
<SelectItem value="hybrid">{t('cloud.hybridCloud')}</SelectItem>
</SelectContent>
</Select>
</div>
@@ -160,16 +163,16 @@ export function PlatformManager() {
return (
<PageShell
variant="list"
title="云平台管理"
subtitle="管理公有云、私有云与混合云平台信息"
title=t('cloud.title')
subtitle=t('cloud.platformmanager.s701204')
icon={<CloudCog className="h-5 w-5" />}
metrics={
!loading ? (
<StatStrip
items={[
{ label: '总计', value: stats.total, color: 'blue' },
{ label: '启用', value: stats.active, color: 'green' },
{ label: '公有云', value: stats.public, color: 'cyan' },
{ label: t('common.total'), value: stats.total, color: 'blue' },
{ label: t('common.enable'), value: stats.active, color: 'green' },
{ label: t('cloud.platformmanager.s7ef31a'), value: stats.public, color: 'cyan' },
]}
/>
) : undefined
@@ -193,7 +196,7 @@ export function PlatformManager() {
) : filtered.length === 0 ? (
<PageEmpty
icon={<CloudCog className="h-12 w-12" />}
title="暂无云平台数据"
title=t('cloud.platformmanager.sb9f478')
action={(
<Button size="sm" className="dg-btn-primary" onClick={openCreate}>
<Plus className="h-4 w-4 mr-1" />
@@ -260,26 +263,26 @@ export function PlatformManager() {
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>{editingItem ? '编辑云平台' : '添加云平台'}</DialogTitle>
<DialogDescription className="sr-only">描述</DialogDescription>
<DialogTitle>{editingItem ? t('cloud.platformmanager.sf83734') : t('cloud.platformmanager.s3f6f0c')}</DialogTitle>
<DialogDescription className="sr-only">{t('common.description')}</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-2">
<div className="grid grid-cols-2 gap-4">
<div className="space-y-1.5">
<Label>平台名称 *</Label>
<Label>{t('cloud.platformmanager.seac7fc')}</Label>
<Input
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
placeholder="如:阿里云"
placeholder=t('cloud.platformmanager.sa39d96')
className="dg-input"
/>
</div>
<div className="space-y-1.5">
<Label>编码 *</Label>
<Label>{t('cloud.platformmanager.se21e27')}</Label>
<Input
value={formData.code}
onChange={(e) => setFormData({ ...formData, code: e.target.value })}
placeholder="如:aliyun"
placeholder=t('cloud.platformmanager.s7b09b0')
className="dg-input"
disabled={!!editingItem}
/>
@@ -287,33 +290,33 @@ export function PlatformManager() {
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-1.5">
<Label>类型</Label>
<Label>{t('common.type')}</Label>
<Select value={formData.type} onValueChange={(v) => setFormData({ ...formData, type: v })}>
<SelectTrigger className="dg-input">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="public">公有云</SelectItem>
<SelectItem value="private">私有云</SelectItem>
<SelectItem value="hybrid">混合云</SelectItem>
<SelectItem value="public">{t('cloud.platformmanager.s7ef31a')}</SelectItem>
<SelectItem value="private">{t('cloud.privateCloud')}</SelectItem>
<SelectItem value="hybrid">{t('cloud.hybridCloud')}</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-1.5">
<Label>状态</Label>
<Label>{t('common.status')}</Label>
<Select value={formData.status} onValueChange={(v) => setFormData({ ...formData, status: v })}>
<SelectTrigger className="dg-input">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="active">启用</SelectItem>
<SelectItem value="inactive">禁用</SelectItem>
<SelectItem value="active">{t('common.enable')}</SelectItem>
<SelectItem value="inactive">{t('common.disable')}</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="space-y-1.5">
<Label>官网地址</Label>
<Label>{t('cloud.platformmanager.saf587e')}</Label>
<Input
value={formData.website}
onChange={(e) => setFormData({ ...formData, website: e.target.value })}
@@ -322,20 +325,20 @@ export function PlatformManager() {
/>
</div>
<div className="space-y-1.5">
<Label>描述</Label>
<Label>{t('common.description')}</Label>
<Input
value={formData.description}
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
placeholder="平台描述..."
placeholder=t('cloud.platformmanager.sc44dd4')
className="dg-input"
/>
</div>
</div>
<DialogFooter>
<Button variant="ghost" onClick={() => setDialogOpen(false)}>取消</Button>
<Button variant="ghost" onClick={() => setDialogOpen(false)}>{t('common.cancel')}</Button>
<Button onClick={handleSubmit} disabled={submitting || !formData.name.trim() || !formData.code.trim()}>
{submitting && <Loader2 className="h-4 w-4 mr-1 animate-spin" />}
{editingItem ? '保存' : '创建'}
{editingItem ? t('common.save') : t('common.create')}
</Button>
</DialogFooter>
</DialogContent>
@@ -345,13 +348,13 @@ export function PlatformManager() {
<Dialog open={deleteId !== null} onOpenChange={(v) => !v && setDeleteId(null)}>
<DialogContent className="max-w-sm">
<DialogHeader>
<DialogTitle>确认删除</DialogTitle>
<DialogDescription className="sr-only">描述</DialogDescription>
<DialogTitle>{t('k8s.actionModal.confirmDelete')}</DialogTitle>
<DialogDescription className="sr-only">{t('common.description')}</DialogDescription>
</DialogHeader>
<p className="text-sm dg-text-secondary">确定删除此云平台?此操作不可撤销。</p>
<p className="text-sm dg-text-secondary">{t('cloud.platformmanager.s452abb')}</p>
<DialogFooter>
<Button variant="ghost" onClick={() => setDeleteId(null)}>取消</Button>
<Button variant="destructive" onClick={confirmDelete}>删除</Button>
<Button variant="ghost" onClick={() => setDeleteId(null)}>{t('common.cancel')}</Button>
<Button variant="destructive" onClick={confirmDelete}>{t('common.delete')}</Button>
</DialogFooter>
</DialogContent>
</Dialog>