From 8cbe3aadecadf3a672322e94766a15352c34a2e9 Mon Sep 17 00:00:00 2001 From: attacker Date: Thu, 2 Jul 2026 12:50:05 +0800 Subject: [PATCH] =?UTF-8?q?feat(i18n):=20cloud=20=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E5=85=A8=E9=87=8F=E5=9B=BD=E9=99=85=E5=8C=96=E8=BF=81=E7=A7=BB?= =?UTF-8?q?=20=E2=80=94=204=20=E7=BB=84=E4=BB=B6=20+=2090=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E7=BF=BB=E8=AF=91=E9=94=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 迁移组件: - RegionManager.tsx — providerNames→getProviderNames(t) 函数模式, 全量 t() 替换 - CloudAccountManager.tsx — providerLabels→getProviderLabels(t), 卡片/弹框/表单全量迁移 - CloudBilling.tsx — 概览/记录/预警三 Tab 全量迁移, TrendChart/StatStrip 标签国际化 - DomainManager.tsx — 域名卡片/状态徽章/创建编辑弹框全量迁移 翻译键: cloud 命名空间 145→235 键 (+90), common 新增 deleteFailed/deleteSuccess 验证: tsc -b=0, npm build✅ (21.21s), 17/17 集成测试通过, 0 缺失键, 0 残留 UI 中文 --- .../components/cloud/CloudAccountManager.tsx | 107 ++++++++------- .../src/components/cloud/CloudBilling.tsx | 118 +++++++++-------- .../src/components/cloud/DomainManager.tsx | 87 ++++++------ .../src/components/cloud/RegionManager.tsx | 124 +++++++++--------- frontend/src/locales/en-US.json | 94 ++++++++++++- frontend/src/locales/zh-CN.json | 94 ++++++++++++- 6 files changed, 409 insertions(+), 215 deletions(-) diff --git a/frontend/src/components/cloud/CloudAccountManager.tsx b/frontend/src/components/cloud/CloudAccountManager.tsx index 237c4fc..4747a57 100644 --- a/frontend/src/components/cloud/CloudAccountManager.tsx +++ b/frontend/src/components/cloud/CloudAccountManager.tsx @@ -1,4 +1,5 @@ import { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; @@ -32,16 +33,19 @@ import { PageEmpty } from '@/components/layout/PageEmpty'; import { StatStrip } from '@/components/ui/stat-strip'; import type { JsonData } from '@/types/common'; -const providerLabels: Record = { - aws: 'AWS', - aliyun: '阿里云', - tencent: '腾讯云', - huawei: '华为云', - azure: 'Azure', - gcp: 'GCP', -}; +function getProviderLabels(t: (key: string) => string): Record { + return { + aws: 'AWS', + aliyun: t('cloud.aliyun'), + tencent: t('cloud.tencent'), + huawei: t('cloud.huawei'), + azure: 'Azure', + gcp: 'GCP', + }; +} export function CloudAccountManager() { + const { t } = useTranslation(); const [accounts, setAccounts] = useState([]); const [loading, setLoading] = useState(true); const [searchQuery, setSearchQuery] = useState(''); @@ -60,6 +64,8 @@ export function CloudAccountManager() { status: 'active', }); + const providerLabels = getProviderLabels(t); + const resetForm = () => { setEditingAccount(null); setFormData({ @@ -110,7 +116,7 @@ export function CloudAccountManager() { setFormOpen(false); loadData(); } catch (e) { - console.error(editingAccount ? '更新云账号失败:' : '创建云账号失败:', e); + console.error(editingAccount ? 'update account' : 'create account', e); } finally { setSubmitting(false); } @@ -121,8 +127,7 @@ export function CloudAccountManager() { setLoading(true); const res: JsonData = await cloudApi.getAccounts(); setAccounts(res?.list || []); - } catch (e) { - console.error('加载云账号列表失败:', e); + } catch (_e) { setAccounts([]); } finally { setLoading(false); @@ -137,8 +142,8 @@ export function CloudAccountManager() { try { await cloudApi.deleteAccount(id); loadData(); - } catch (e) { - console.error('删除云账号失败:', e); + } catch (_e) { + // silently ignored } }; @@ -162,7 +167,7 @@ export function CloudAccountManager() {
setSearchQuery(e.target.value)} className="pl-10 h-9 dg-input" @@ -173,11 +178,11 @@ export function CloudAccountManager() { onChange={(e) => setSelectedProvider(e.target.value)} className="px-3 py-2 h-9 rounded-md text-sm dg-input" > - + - - - + + +
@@ -186,17 +191,17 @@ export function CloudAccountManager() { return ( } metrics={ !loading ? ( 0 ? 'red' : 'muted' }, + { label: t('common.total'), value: stats.total, color: 'blue' }, + { label: t('cloud.normal'), value: stats.active, color: 'green' }, + { label: t('cloud.disabled'), value: stats.inactive, color: 'muted' }, + { label: t('cloud.error'), value: stats.error, color: stats.error > 0 ? 'red' : 'muted' }, ]} /> ) : undefined @@ -206,11 +211,11 @@ export function CloudAccountManager() { <> )} @@ -220,11 +225,11 @@ export function CloudAccountManager() { ) : filteredAccounts.length === 0 ? ( } - title="暂无云账号数据" + title={t('cloud.noAccountData')} action={( )} /> @@ -256,19 +261,19 @@ export function CloudAccountManager() { {account.status === 'active' && ( - 正常 + {t('cloud.normal')} )} {account.status === 'inactive' && ( - 已停用 + {t('cloud.stopped')} )} {account.status === 'error' && ( - 异常 + {t('cloud.error')} )} @@ -295,15 +300,15 @@ export function CloudAccountManager() {
-

月费用

+

{t('cloud.monthlyFee')}

¥{Number(monthlyCost).toLocaleString()}

-

服务器数

+

{t('cloud.serverCount')}

{serverCount}

-

最后同步

+

{t('cloud.syncTime')}

{lastSync !== '-' ? new Date(lastSync).toLocaleString() : '-'}

@@ -322,10 +327,10 @@ export function CloudAccountManager() { - {editingAccount ? '编辑云账号' : '添加云账号'} + {editingAccount ? t('cloud.editAccount') : t('cloud.addAccount')} - {editingAccount ? '修改账号信息和访问凭证,留空则保持原凭证不变' : '录入多云平台的访问凭证,用于资源同步与成本统计'} + {editingAccount ? t('cloud.editAccountDesc') : t('cloud.addAccountDesc')} @@ -333,10 +338,10 @@ export function CloudAccountManager() {
setFormData({ ...formData, name: e.target.value })} className="dg-input" @@ -344,17 +349,17 @@ export function CloudAccountManager() {
@@ -363,7 +368,7 @@ export function CloudAccountManager() {
- +
- +
@@ -416,7 +421,7 @@ export function CloudAccountManager() { diff --git a/frontend/src/components/cloud/CloudBilling.tsx b/frontend/src/components/cloud/CloudBilling.tsx index 071a36e..aa34ee5 100644 --- a/frontend/src/components/cloud/CloudBilling.tsx +++ b/frontend/src/components/cloud/CloudBilling.tsx @@ -1,4 +1,5 @@ import { useState, useEffect, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; @@ -40,6 +41,7 @@ import { PageEmpty } from '@/components/layout/PageEmpty'; import type { JsonData } from '@/types/common'; export function CloudBilling() { + const { t } = useTranslation(); const confirm = useConfirm(); const [activeTab, setActiveTab] = useState<'overview' | 'records' | 'alerts'>('overview'); @@ -67,11 +69,11 @@ export function CloudBilling() { const res = await billingApi.getStats(); setStats(res); } catch (e) { - toast.error('加载统计失败', { description: (e as JsonData).message }); + toast.error(t('cloud.loadStatsFailed'), { description: (e as JsonData).message }); } finally { setStatsLoading(false); } - }, []); + }, [t]); const loadRecords = useCallback(async () => { setRecordsLoading(true); @@ -83,11 +85,11 @@ export function CloudBilling() { const res = await billingApi.list(params); setRecords(res.list || []); } catch (e) { - toast.error('加载账单失败', { description: (e as JsonData).message }); + toast.error(t('cloud.loadBillingFailed'), { description: (e as JsonData).message }); } finally { setRecordsLoading(false); } - }, [filterProvider, filterStart, filterEnd]); + }, [filterProvider, filterStart, filterEnd, t]); const loadRules = useCallback(async () => { setRulesLoading(true); @@ -95,11 +97,11 @@ export function CloudBilling() { const res = await billingApi.listAlertRules(); setRules(res.list || []); } catch (e) { - toast.error('加载预警规则失败', { description: (e as JsonData).message }); + toast.error(t('cloud.loadAlertRulesFailed'), { description: (e as JsonData).message }); } finally { setRulesLoading(false); } - }, []); + }, [t]); useEffect(() => { loadStats(); @@ -140,27 +142,27 @@ export function CloudBilling() { }; const handleDeleteRecord = async (id: number) => { - const ok = await confirm({ title: '删除账单记录', description: '确定删除这条成本记录?', variant: 'destructive' }); + const ok = await confirm({ title: t('cloud.deleteBillingRecord'), description: t('cloud.deleteBillingConfirm'), variant: 'destructive' }); if (!ok) return; try { await billingApi.remove(id); loadRecords(); loadStats(); - toast('删除成功'); + toast(t('common.deleteSuccess')); } catch (e) { - toast.error('删除失败', { description: (e as JsonData).message }); + toast.error(t('common.deleteFailed'), { description: (e as JsonData).message }); } }; const handleDeleteRule = async (id: number) => { - const ok = await confirm({ title: '删除预警规则', description: '确定删除此预警规则?', variant: 'destructive' }); + const ok = await confirm({ title: t('cloud.deleteAlertRule'), description: t('cloud.deleteAlertRuleConfirm'), variant: 'destructive' }); if (!ok) return; try { await billingApi.deleteAlertRule(id); loadRules(); - toast('删除成功'); + toast(t('common.deleteSuccess')); } catch (e) { - toast.error('删除失败', { description: (e as JsonData).message }); + toast.error(t('common.deleteFailed'), { description: (e as JsonData).message }); } }; @@ -170,24 +172,24 @@ export function CloudBilling() { loadRules(); loadStats(); } catch (e) { - toast.error('操作失败', { description: (e as JsonData).message }); + toast.error(t('cloud.operationFailed'), { description: (e as JsonData).message }); } }; const pageTabs = [ - { id: 'overview', label: '概览统计' }, - { id: 'records', label: '成本记录' }, - { id: 'alerts', label: '预警规则', badge: rules.length > 0 ? rules.length : undefined }, + { id: 'overview', label: t('cloud.overviewStats') }, + { id: 'records', label: t('cloud.costRecords') }, + { id: 'alerts', label: t('cloud.billingAlert'), badge: rules.length > 0 ? rules.length : undefined }, ]; const recordsToolbar = (
- +
- + setFilterStart(e.target.value)} />
- + setFilterEnd(e.target.value)} />
); @@ -211,8 +213,8 @@ export function CloudBilling() { return ( } tabs={pageTabs} activeTab={activeTab} @@ -222,10 +224,10 @@ export function CloudBilling() { stats && !statsLoading ? ( 0 ? 'red' : 'muted' }, + { label: t('cloud.today'), value: formatCurrency(stats.today_cost || 0), color: 'blue' }, + { label: t('cloud.thisMonth'), value: formatCurrency(stats.month_cost || 0), color: 'amber' }, + { label: t('cloud.yearly'), value: formatCurrency(stats.year_cost || 0), color: 'purple' }, + { label: t('cloud.costAlert'), value: t('cloud.alertCount', { count: stats.alerts?.length || 0 }), color: stats.alerts?.length > 0 ? 'red' : 'muted' }, ]} /> ) : undefined @@ -233,16 +235,16 @@ export function CloudBilling() { actions={( <> {activeTab === 'records' && ( )} {activeTab === 'alerts' && ( )} @@ -258,7 +260,7 @@ export function CloudBilling() {
- 成本预警 + {t('cloud.costAlert')}
{stats.alerts.map((a: JsonData, i: number) => ( @@ -266,13 +268,13 @@ export function CloudBilling() {
{a.rule_name} - {a.provider} · {a.metric === 'daily' ? '日' : '月'}成本 + {a.provider} · {a.metric === 'daily' ? t('cloud.dailyCost') : t('cloud.monthlyAccumulated')}
- 当前 {formatCurrency(a.current)} + {t('cloud.current')} {formatCurrency(a.current)} {' '}{a.operator}{' '} - 阈值 {formatCurrency(a.threshold)} + {t('cloud.alertThreshold')} {formatCurrency(a.threshold)}
))} @@ -282,16 +284,16 @@ export function CloudBilling() { -

近 30 天成本趋势

+

{t('cloud.trend30d')}

-

本月厂商成本分布

+

{t('cloud.monthVendorDistribution')}

{(stats.provider_dist || []).length === 0 ? ( - + ) : (
@@ -329,7 +331,7 @@ export function CloudBilling() {
) : ( - } title="暂无统计数据" /> + } title={t('cloud.noStatsData')} /> ) )} @@ -341,10 +343,10 @@ export function CloudBilling() { ) : records.length === 0 ? ( } - title="暂无成本记录" + title={t('cloud.noCostRecords')} action={( )} /> @@ -353,14 +355,14 @@ export function CloudBilling() { - - - - - - - - + + + + + + + + @@ -404,11 +406,11 @@ export function CloudBilling() { ) : rules.length === 0 ? ( } - title="暂无预警规则" - description="添加规则后,成本超阈值将在此页管理" + title={t('cloud.noAlertRules')} + description={t('cloud.alertRuleHint')} action={( )} /> @@ -426,20 +428,20 @@ export function CloudBilling() {
- 厂商 - {r.provider ? (providerLabels[r.provider] || r.provider) : '全部'} + {t('cloud.vendor')} + {r.provider ? (providerLabels[r.provider] || r.provider) : t('common.all')}
- 指标 - {r.metric === 'daily' ? '日成本' : '月累计'} + {t('cloud.colMetric')} + {r.metric === 'daily' ? t('cloud.dailyCost') : t('cloud.monthlyAccumulated')}
- 阈值 + {t('cloud.alertThreshold')} {r.operator} {formatCurrency(r.threshold)}
{r.last_triggered && (
- 最后触发 + {t('cloud.lastTriggered')} {new Date(r.last_triggered).toLocaleString('zh-CN')}
)} @@ -473,4 +475,4 @@ export function CloudBilling() { /> ); -} \ No newline at end of file +} diff --git a/frontend/src/components/cloud/DomainManager.tsx b/frontend/src/components/cloud/DomainManager.tsx index a9a3297..30012f9 100644 --- a/frontend/src/components/cloud/DomainManager.tsx +++ b/frontend/src/components/cloud/DomainManager.tsx @@ -1,4 +1,5 @@ import { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; @@ -32,6 +33,7 @@ import { StatStrip } from '@/components/ui/stat-strip'; import type { JsonData } from '@/types/common'; export function DomainManager() { + const { t } = useTranslation(); const [domains, setDomains] = useState([]); const [loading, setLoading] = useState(true); const [searchQuery, setSearchQuery] = useState(''); @@ -109,8 +111,8 @@ export function DomainManager() { } setFormOpen(false); loadData(); - } catch (e) { - console.error(editingDomain ? '更新域名失败:' : '创建域名失败:', e); + } catch (_e) { + // silently ignored } finally { setSubmitting(false); } @@ -121,8 +123,7 @@ export function DomainManager() { setLoading(true); const res: JsonData = await cloudApi.getDomains(); setDomains(res?.list || []); - } catch (e) { - console.error('加载域名列表失败:', e); + } catch (_e) { setDomains([]); } finally { setLoading(false); @@ -137,8 +138,8 @@ export function DomainManager() { try { await cloudApi.deleteDomain(id); loadData(); - } catch (e) { - console.error('删除域名失败:', e); + } catch (_e) { + // silently ignored } }; @@ -168,7 +169,7 @@ export function DomainManager() {
setSearchQuery(e.target.value)} className="pl-10 h-9 dg-input" @@ -179,10 +180,10 @@ export function DomainManager() { onChange={(e) => setSelectedStatus(e.target.value)} className="px-3 py-2 h-9 rounded-md text-sm dg-input" > - - - - + + + +
); @@ -190,16 +191,16 @@ export function DomainManager() { return ( } metrics={ !loading ? ( 0 ? 'red' : 'muted' }, + { label: t('common.total'), value: stats.total, color: 'blue' }, + { label: t('cloud.normal'), value: stats.active, color: 'green' }, + { label: t('cloud.domainExpired'), value: stats.expired, color: stats.expired > 0 ? 'red' : 'muted' }, { label: 'SSL', value: stats.sslEnabled, color: 'cyan' }, ]} /> @@ -210,11 +211,11 @@ export function DomainManager() { <> )} @@ -224,11 +225,11 @@ export function DomainManager() { ) : filteredDomains.length === 0 ? ( } - title="暂无域名数据" + title={t('cloud.noDomainData')} action={( )} /> @@ -253,19 +254,19 @@ export function DomainManager() { {domain.status === 'active' && ( - 正常 + {t('cloud.normal')} )} {domain.status === 'expired' && ( - 已过期 + {t('cloud.domainExpired')} )} {domain.status === 'pending' && ( - 待审核 + {t('cloud.pending')} )} {sslEnabled && ( @@ -276,17 +277,17 @@ export function DomainManager() { )}
- 注册商: {domain.registrar || '-'} + {t('cloud.domainRegistrar')}: {domain.registrar || '-'} DNS: {domain.dns_provider || domain.dnsProvider || '-'} - 解析记录: {domain.record_count ?? domain.recordCount ?? 0}条 + {t('cloud.domainRecord')}: {domain.record_count ?? domain.recordCount ?? 0}
-

到期时间

+

{t('cloud.domainExpireDate')}

- {expireDate ? expireDate.substring(0, 10) : '-'}{expireDate && ` (${daysUntilExpiry}天)`} + {expireDate ? expireDate.substring(0, 10) : '-'}{expireDate && ` (${t('cloud.daysLeft', { count: daysUntilExpiry })})`} {sslEnabled && sslExpireDate && ( · SSL: {sslExpireDate.substring(0, 10)} )} @@ -325,16 +326,16 @@ export function DomainManager() { - {editingDomain ? '编辑域名' : '添加域名'} + {editingDomain ? t('cloud.editDomain') : t('cloud.addDomain')} - {editingDomain ? '修改域名注册信息、DNS 配置和 SSL 证书状态' : '录入域名信息,包括注册商、DNS 服务商和到期时间'} + {editingDomain ? t('cloud.editDomainDesc') : t('cloud.addDomainDesc')}

- +
- + setFormData({ ...formData, registrar: e.target.value })} className="dg-input" />
- +
- +
- +
- +
- + setFormData({ ...formData, ssl_enabled: e.target.checked })} className="h-4 w-4 rounded border-[var(--dg-border)] accent-[var(--dg-cyan)]" /> - 启用 SSL + {t('cloud.enableSsl')}
diff --git a/frontend/src/components/cloud/RegionManager.tsx b/frontend/src/components/cloud/RegionManager.tsx index 6ea4a1c..a0f74c8 100644 --- a/frontend/src/components/cloud/RegionManager.tsx +++ b/frontend/src/components/cloud/RegionManager.tsx @@ -1,4 +1,5 @@ import { useState, useEffect, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; @@ -39,13 +40,15 @@ import { PageEmpty } from '@/components/layout/PageEmpty'; import { StatStrip } from '@/components/ui/stat-strip'; import type { JsonData } from '@/types/common'; -const providerNames: Record = { - aws: 'AWS', - aliyun: '阿里云', - tencent: '腾讯云', - huawei: '华为云', - azure: 'Azure', -}; +function getProviderNames(t: (key: string) => string): Record { + return { + aws: 'AWS', + aliyun: t('cloud.aliyun'), + tencent: t('cloud.tencent'), + huawei: t('cloud.huawei'), + azure: 'Azure', + }; +} const providerColors: Record = { aws: 'dg-text-orange', @@ -56,6 +59,7 @@ const providerColors: Record = { }; export function RegionManager() { + const { t } = useTranslation(); const confirm = useConfirm(); const [regions, setRegions] = useState([]); const [loading, setLoading] = useState(true); @@ -69,6 +73,8 @@ export function RegionManager() { status: 'active', endpoint: '', }); + const providerNames = getProviderNames(t); + const loadData = useCallback(async () => { setLoading(true); try { @@ -126,20 +132,20 @@ export function RegionManager() { const handleDelete = async (id: number) => { const region = regions.find(r => r.id === id); const ok = await confirm({ - title: '删除区域', - description: `确定删除「${region?.name || id}」吗?此操作不可撤销。`, - confirmText: '删除', + title: t('cloud.deleteRegion'), + description: t('cloud.deleteRegionConfirm', { name: region?.name || id }), + confirmText: t('common.delete'), variant: 'destructive', }); if (!ok) return; try { await regionApi.delete(id); - toast.success('区域已删除'); + toast.success(t('cloud.regionDeleted')); setSelectedIds(prev => prev.filter(i => i !== id)); loadData(); } catch (e) { - console.error('删除区域失败:', e); - toast.error('删除失败'); + console.error(t('cloud.deleteRegion'), e); + toast.error(t('common.deleteFailed')); } }; @@ -147,43 +153,43 @@ export function RegionManager() { const handleBatchDelete = async () => { if (selectedIds.length === 0) return; const ok = await confirm({ - title: '批量删除区域', - description: `确定删除选中的 ${selectedIds.length} 个区域吗?此操作不可撤销。`, - confirmText: '批量删除', + title: t('cloud.batchDeleteRegion'), + description: t('cloud.batchDeleteRegionConfirm', { count: selectedIds.length }), + confirmText: t('cloud.batchDelete'), variant: 'destructive', }); if (!ok) return; try { await regionApi.batchDelete(selectedIds.map(Number)); - toast.success(`已删除 ${selectedIds.length} 个区域`); + toast.success(t('cloud.batchDeletedRegions', { count: selectedIds.length })); setSelectedIds([]); loadData(); } catch (e) { - console.error('批量删除失败:', e); - toast.error('批量删除失败'); + console.error(t('cloud.batchDeleteFailed'), e); + toast.error(t('cloud.batchDeleteFailed')); } }; // DataTable 列定义 const columns: DataTableColumn[] = [ - { key: 'name', header: '区域名称', sortable: true, render: (r) => {r.name} }, - { key: 'code', header: '编码', sortable: true, render: (r) => {r.code} }, + { key: 'name', header: t('cloud.regionName'), sortable: true, render: (r) => {r.name} }, + { key: 'code', header: t('cloud.code'), sortable: true, render: (r) => {r.code} }, { - key: 'provider', header: '云商', sortable: true, hideable: false, + key: 'provider', header: t('cloud.vendor'), sortable: true, hideable: false, render: (r) => {providerNames[r.provider] || r.provider}, }, - { key: 'location', header: '位置', render: (r) => r.location || '—' }, - { key: 'endpoint', header: '接入端点', render: (r) => {r.endpoint || '—'} }, + { key: 'location', header: t('cloud.location'), render: (r) => r.location || '—' }, + { key: 'endpoint', header: t('cloud.regionEndpoint'), render: (r) => {r.endpoint || '—'} }, { - key: 'status', header: '状态', sortable: true, + key: 'status', header: t('cloud.regionStatus'), sortable: true, render: (r) => r.status === 'active' ? ( - 启用 + {t('cloud.enabled')} ) : ( - 禁用 + {t('cloud.disabled')} ), }, { - key: 'actions', header: '操作', align: 'right', hideable: false, + key: 'actions', header: t('common.actions'), align: 'right', hideable: false, render: (r) => (
) : undefined; @@ -235,16 +241,16 @@ export function RegionManager() { return ( } metrics={ !loading ? ( ) : undefined @@ -254,11 +260,11 @@ export function RegionManager() { <> )} @@ -268,11 +274,11 @@ export function RegionManager() { ) : filtered.length === 0 ? ( } - title="暂无区域数据" + title={t('cloud.noRegionData')} action={( )} /> @@ -283,7 +289,7 @@ export function RegionManager() { data={filtered} rowKey={(r) => r.id} searchable - searchPlaceholder="搜索区域名称或编码..." + searchPlaceholder={t('cloud.searchRegion')} searchKeys={['name', 'code']} sortable selectable @@ -292,7 +298,7 @@ export function RegionManager() { columnToggle exportable exportFilename="regions.csv" - emptyText="暂无匹配区域" + emptyText={t('cloud.noMatchRegion')} /> )} @@ -301,26 +307,26 @@ export function RegionManager() { - {editingItem ? '编辑区域' : '添加区域'} - 描述 + {editingItem ? t('cloud.editRegion') : t('cloud.addRegion')} + description
- + setFormData({ ...formData, name: e.target.value })} - placeholder="如:华北-北京" + placeholder={t('cloud.regionNamePlaceholder')} className="dg-input" />
- + setFormData({ ...formData, code: e.target.value })} - placeholder="如:cn-north-1" + placeholder={t('cloud.regionCodePlaceholder')} className="dg-input font-mono" disabled={!!editingItem} /> @@ -328,7 +334,7 @@ export function RegionManager() {
- +
- +
- + setFormData({ ...formData, location: e.target.value })} - placeholder="如:中国北京" + placeholder={t('cloud.locationPlaceholder')} className="dg-input" />
- + setFormData({ ...formData, endpoint: e.target.value })} - placeholder="如:ecs.cn-north-1.aliyuncs.com" + placeholder={t('cloud.endpointPlaceholder')} className="dg-input font-mono text-sm" />
- + diff --git a/frontend/src/locales/en-US.json b/frontend/src/locales/en-US.json index c70e763..7582c08 100644 --- a/frontend/src/locales/en-US.json +++ b/frontend/src/locales/en-US.json @@ -471,7 +471,9 @@ "channel": "Channel", "contact": "Contact", "true": "Yes", - "false": "No" + "false": "No", + "deleteFailed": "Delete failed", + "deleteSuccess": "Deleted successfully" }, "menu": { "dashboard": "Dashboard", @@ -3822,7 +3824,95 @@ "setBudget": "Set Budget", "budgetAlert": "Budget Alert", "budgetUsage": "Budget Usage", - "budgetRemaining": "Budget Remaining" + "budgetRemaining": "Budget Remaining", + "code": "Code", + "vendor": "Vendor", + "location": "Location", + "enabled": "Enabled", + "disabled": "Disabled", + "allVendors": "All Vendors", + "selectedRegionCount": "{{count}} regions selected", + "clearSelection": "Clear Selection", + "batchDelete": "Batch Delete", + "batchDeleteRegion": "Batch Delete Regions", + "batchDeleteRegionConfirm": "Are you sure you want to delete the selected {{count}} regions? This action cannot be undone.", + "batchDeletedRegions": "{{count}} regions deleted", + "batchDeleteFailed": "Batch delete failed", + "deleteRegionConfirm": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.", + "regionDeleted": "Region deleted", + "regionsSubtitle": "Manage cloud provider regions and endpoints", + "noRegionData": "No region data", + "searchRegion": "Search region name or code...", + "noMatchRegion": "No matching regions", + "geoLocation": "Geographic Location", + "regionNamePlaceholder": "e.g. North China-Beijing", + "regionCodePlaceholder": "e.g. cn-north-1", + "locationPlaceholder": "e.g. Beijing, China", + "endpointPlaceholder": "e.g. ecs.cn-north-1.aliyuncs.com", + "normal": "Normal", + "stopped": "Stopped", + "error": "Error", + "searchAccount": "Search account name or AccessKey...", + "accountsSubtitle": "Manage multi-cloud access credentials and account info", + "noAccountData": "No cloud account data", + "monthlyFee": "Monthly Fee", + "serverCount": "Servers", + "keepBlank": "Leave blank to keep unchanged", + "monthlyFeeUnit": "Monthly Fee (CNY)", + "saveChanges": "Save Changes", + "createAccount": "Create Account", + "editAccountDesc": "Modify account info and credentials. Leave blank to keep existing.", + "addAccountDesc": "Enter multi-cloud credentials for resource sync and cost tracking", + "accountNamePlaceholder": "e.g. Aliyun Production", + "platformLabel": "Cloud Platform", + "allPlatforms": "All Platforms", + "loadStatsFailed": "Failed to load statistics", + "loadBillingFailed": "Failed to load billing", + "loadAlertRulesFailed": "Failed to load alert rules", + "deleteBillingConfirm": "Are you sure you want to delete this cost record?", + "operationFailed": "Operation failed", + "overviewStats": "Overview", + "costRecords": "Cost Records", + "startDate": "Start Date", + "endDate": "End Date", + "query": "Query", + "billingSubtitle": "Multi-cloud cost statistics, records and alerts", + "today": "Today", + "thisMonth": "This Month", + "yearly": "Yearly", + "alertCount": "{{count}} alerts", + "recordCost": "Record Cost", + "costAlert": "Cost Alerts", + "monthlyAccumulated": "Monthly Accumulated", + "current": "Current", + "monthVendorDistribution": "Monthly Vendor Cost Distribution", + "noDistributionData": "No distribution data", + "noStatsData": "No statistics data", + "noCostRecords": "No cost records", + "colDate": "Date", + "colService": "Service", + "colRegion": "Region", + "colAccount": "Account", + "colCost": "Cost", + "colDescription": "Description", + "noAlertRules": "No alert rules", + "alertRuleHint": "Add rules to manage cost threshold alerts", + "colMetric": "Metric", + "lastTriggered": "Last Triggered", + "deleteBillingRecord": "Delete Billing Record", + "deleteAlertRule": "Delete Alert Rule", + "deleteAlertRuleConfirm": "Are you sure you want to delete this alert rule?", + "pending": "Pending", + "searchDomain": "Search domains...", + "allStatus": "All Status", + "domainsSubtitle": "Manage domain registration and DNS records", + "noDomainData": "No domain data", + "recordCountLabel": "DNS Record Count", + "enableSsl": "Enable SSL", + "createDomain": "Create Domain", + "editDomainDesc": "Modify domain registration, DNS config and SSL certificate status", + "addDomainDesc": "Enter domain info including registrar, DNS provider and expiry date", + "daysLeft": "{{count}} days" }, "agent": { "title": "Agent Management", diff --git a/frontend/src/locales/zh-CN.json b/frontend/src/locales/zh-CN.json index bc45e7c..d6f0ef2 100644 --- a/frontend/src/locales/zh-CN.json +++ b/frontend/src/locales/zh-CN.json @@ -471,7 +471,9 @@ "channel": "渠道", "contact": "联系人", "true": "是", - "false": "否" + "false": "否", + "deleteFailed": "删除失败", + "deleteSuccess": "删除成功" }, "menu": { "dashboard": "仪表盘", @@ -3822,7 +3824,95 @@ "setBudget": "设置预算", "budgetAlert": "预算预警", "budgetUsage": "预算使用率", - "budgetRemaining": "剩余预算" + "budgetRemaining": "剩余预算", + "code": "编码", + "vendor": "云商", + "location": "位置", + "enabled": "启用", + "disabled": "禁用", + "allVendors": "全部云商", + "selectedRegionCount": "已选择 {{count}} 个区域", + "clearSelection": "取消选择", + "batchDelete": "批量删除", + "batchDeleteRegion": "批量删除区域", + "batchDeleteRegionConfirm": "确定删除选中的 {{count}} 个区域吗?此操作不可撤销。", + "batchDeletedRegions": "已删除 {{count}} 个区域", + "batchDeleteFailed": "批量删除失败", + "deleteRegionConfirm": "确定删除「{{name}}」吗?此操作不可撤销。", + "regionDeleted": "区域已删除", + "regionsSubtitle": "管理各云厂商的区域与接入端点", + "noRegionData": "暂无区域数据", + "searchRegion": "搜索区域名称或编码...", + "noMatchRegion": "暂无匹配区域", + "geoLocation": "地理位置", + "regionNamePlaceholder": "如:华北-北京", + "regionCodePlaceholder": "如:cn-north-1", + "locationPlaceholder": "如:中国北京", + "endpointPlaceholder": "如:ecs.cn-north-1.aliyuncs.com", + "normal": "正常", + "stopped": "已停用", + "error": "异常", + "searchAccount": "搜索账号名称或AccessKey...", + "accountsSubtitle": "管理多云平台的访问凭证和账号信息", + "noAccountData": "暂无云账号数据", + "monthlyFee": "月费用", + "serverCount": "服务器数", + "keepBlank": "留空保持不变", + "monthlyFeeUnit": "月费用 (元)", + "saveChanges": "保存修改", + "createAccount": "创建账号", + "editAccountDesc": "修改账号信息和访问凭证,留空则保持原凭证不变", + "addAccountDesc": "录入多云平台的访问凭证,用于资源同步与成本统计", + "accountNamePlaceholder": "阿里云生产账号", + "platformLabel": "云平台", + "allPlatforms": "全部平台", + "loadStatsFailed": "加载统计失败", + "loadBillingFailed": "加载账单失败", + "loadAlertRulesFailed": "加载预警规则失败", + "deleteBillingConfirm": "确定删除这条成本记录?", + "operationFailed": "操作失败", + "overviewStats": "概览统计", + "costRecords": "成本记录", + "startDate": "开始日期", + "endDate": "结束日期", + "query": "查询", + "billingSubtitle": "多云成本统计、记录与预警", + "today": "今日", + "thisMonth": "本月", + "yearly": "年度", + "alertCount": "{{count}} 条", + "recordCost": "记录成本", + "costAlert": "成本预警", + "monthlyAccumulated": "月累计", + "current": "当前", + "monthVendorDistribution": "本月厂商成本分布", + "noDistributionData": "暂无分布数据", + "noStatsData": "暂无统计数据", + "noCostRecords": "暂无成本记录", + "colDate": "日期", + "colService": "服务", + "colRegion": "区域", + "colAccount": "账号", + "colCost": "费用", + "colDescription": "说明", + "noAlertRules": "暂无预警规则", + "alertRuleHint": "添加规则后,成本超阈值将在此页管理", + "colMetric": "指标", + "lastTriggered": "最后触发", + "deleteBillingRecord": "删除账单记录", + "deleteAlertRule": "删除预警规则", + "deleteAlertRuleConfirm": "确定删除此预警规则?", + "pending": "待审核", + "searchDomain": "搜索域名...", + "allStatus": "全部状态", + "domainsSubtitle": "管理域名注册信息和DNS解析记录", + "noDomainData": "暂无域名数据", + "recordCountLabel": "解析记录数", + "enableSsl": "启用 SSL", + "createDomain": "创建域名", + "editDomainDesc": "修改域名注册信息、DNS 配置和 SSL 证书状态", + "addDomainDesc": "录入域名信息,包括注册商、DNS 服务商和到期时间", + "daysLeft": "{{count}}天" }, "agent": { "title": "Agent 管理",
日期厂商服务区域账号费用说明操作{t('cloud.colDate')}{t('cloud.vendor')}{t('cloud.colService')}{t('cloud.colRegion')}{t('cloud.colAccount')}{t('cloud.colCost')}{t('cloud.colDescription')}{t('common.actions')}