fix(i18n): cost-predict.ts hash key 清理 + 硬编码中文清零 + 错误 key 引用修复

- 23 个 predictutilsts.s* hash-style 键重命名为 costPredict.* 语义化键
- 7 处 buildCostInsightPrompt 硬编码中文模板字面量→i18n.t() 插值调用
- 修复 costRecommendation 函数引用错误 key (predictMemLeak→costPredict.rec*)
- 修复 dimension 字段引用 predictDiskFull→costPredict.allProviders
- 修复 providerLabel 引用 predictIn24h→costPredict.allProviders
- 新增 34 个语义化翻译键 (aiops.costPredict.*), zh/en 8102→8113 对齐
- tsc -b=0, npm build✓ (19.29s), ESLint=0/43/any=0, go build=0
- 17/17 集成测试通过
This commit is contained in:
attacker committed 2026-07-17 04:05:32 +08:00
1 parent d6ed1978ef
commit 083f4477db
3 files changed
+117 -83

No files matched your search

+47 -35
View File
@@ -83,20 +83,20 @@ function costTrendFromSlope(slope: number): 'up' | 'down' | 'stable' {
/** 成本优化建议 */
export function costRecommendation(trend: 'up' | 'down' | 'stable', ratio: number): string {
if (trend === 'up' && ratio >= 0.85) {
return i18n.t('aiops.predictMemLeak');
return i18n.t('aiops.costPredict.recHighSpend');
}
if (trend === 'up') {
return i18n.t('aiops.predictMemLeak');
return i18n.t('aiops.costPredict.recRising');
}
if (trend === 'down') {
return i18n.t('aiops.predictMemLeak');
return i18n.t('aiops.costPredict.recDeclining');
}
return i18n.t('aiops.predictutilsts.sa3b5c7d9');
return i18n.t('aiops.costPredict.recStable');
}
/** 预算风险建议 */
function budgetRiskAction(metric: 'daily' | 'monthly', provider: string): string {
const scope = provider && provider !== i18n.t('aiops.predictutilsts.sg5a3d8e1') ? `${provider} ` : '';
const scope = provider && provider !== i18n.t('aiops.costPredict.allProviders') ? `${provider} ` : '';
if (metric === 'daily') {
return i18n.t('aiops.predictUtils.dailyCostHint', { scope });
}
@@ -139,7 +139,7 @@ function topServicesByCost(records: JsonData[], limit = 5): { service: string; c
records.forEach((r) => {
const date = String(r.billing_date || '');
if (date < start) return;
const svc = String(r.service || i18n.t('aiops.predictutilsts.sh7c2b9d6'));
const svc = String(r.service || i18n.t('aiops.costPredict.unknownService'));
map.set(svc, (map.get(svc) || 0) + (Number(r.cost) || 0));
});
return [...map.entries()]
@@ -256,7 +256,7 @@ export function computeCostPredictions(input: ComputeCostPredictionsInput): Comp
const allRatio =
summary.predictedMonthEnd > 0 ? summary.monthCost / summary.predictedMonthEnd : 0;
dimensionForecasts.push({
dimension: i18n.t('aiops.predictDiskFull'),
dimension: i18n.t('aiops.costPredict.allProviders'),
currentMonth: summary.monthCost,
predictedMonthEnd: summary.predictedMonthEnd,
dailyAvg: summary.dailyAvg,
@@ -302,7 +302,7 @@ export function computeCostPredictions(input: ComputeCostPredictionsInput): Comp
const metric = (rule.metric === 'daily' ? 'daily' : 'monthly') as 'daily' | 'monthly';
const ruleProvider = String(rule.provider || '');
const providerLabel = ruleProvider ? PROVIDER_LABELS[ruleProvider] || ruleProvider : i18n.t('aiops.predictIn24h');
const providerLabel = ruleProvider ? PROVIDER_LABELS[ruleProvider] || ruleProvider : i18n.t('aiops.costPredict.allProviders');
let current = 0;
let predicted = 0;
@@ -339,17 +339,17 @@ export function computeCostPredictions(input: ComputeCostPredictionsInput): Comp
if (current >= threshold) probability += 25;
probability = Math.max(5, Math.min(92, probability));
let timeframe = metric === 'daily' ? i18n.t('aiops.predictNext24h') : i18n.t('aiops.predictutilsts.sd4e5f6g7');
let timeframe = metric === 'daily' ? i18n.t('aiops.predictNext24h') : i18n.t('aiops.costPredict.monthEnd');
if (slope > 0.01 && threshold > current) {
const days = (threshold - current) / slope;
if (days < 1) timeframe = metric === 'daily' ? i18n.t('aiops.predictutilsts.se5f6g7h8') : i18n.t('aiops.predictutilsts.sf6g7h8i9');
else if (days <= 7) timeframe = i18n.t('aiops.predictutilsts.sg7h8i9j0', { days: Math.ceil(days) });
else timeframe = i18n.t('aiops.predictutilsts.sh8i9j0k1', { days: Math.ceil(days) });
if (days < 1) timeframe = metric === 'daily' ? i18n.t('aiops.costPredict.withinToday') : i18n.t('aiops.costPredict.withinOneDay');
else if (days <= 7) timeframe = i18n.t('aiops.costPredict.withinDays', { days: Math.ceil(days) });
else timeframe = i18n.t('aiops.costPredict.approxAfterDays', { days: Math.ceil(days) });
}
if (probability >= 30 || current >= threshold * 0.7) {
budgetRisks.push({
ruleName: String(rule.name || i18n.t('aiops.predictutilsts.si9j0k1l2')),
ruleName: String(rule.name || i18n.t('aiops.costPredict.budgetRule')),
provider: providerLabel,
metric,
threshold,
@@ -386,49 +386,61 @@ export function buildCostInsightPrompt(data: ComputedCostPredictions): string {
const patternHint =
billingPattern === 'front_loaded'
? i18n.t('aiops.predictutilsts.sj0k1l2m3')
? i18n.t('aiops.costPredict.frontLoadedHint')
: billingPattern === 'sparse'
? i18n.t('aiops.predictutilsts.sk1l2m3n4')
: i18n.t('aiops.predictutilsts.sl2m3n4o5');
? i18n.t('aiops.costPredict.sparseHint')
: i18n.t('aiops.costPredict.steadyHint');
const dimLines = dimensionForecasts.slice(0, 6).map(
(d) =>
`- ${d.dimension}:本月 ${d.currentMonth.toFixed(2)} 元 → 预测月末 ${d.predictedMonthEnd.toFixed(2)} 元(趋势 ${d.trend})`,
i18n.t('aiops.costPredict.dimLine', {
dim: d.dimension,
current: d.currentMonth.toFixed(2),
predicted: d.predictedMonthEnd.toFixed(2),
trend: d.trend,
}),
);
const riskLines =
budgetRisks.length > 0
? budgetRisks.slice(0, 5).map(
(r) =>
`- ${r.ruleName}(${r.provider}):当前 ${r.current} / 阈值 ${r.threshold},预测 ${r.predicted},触线概率 ${r.probability}%`,
i18n.t('aiops.costPredict.riskLine', {
rule: r.ruleName,
provider: r.provider,
current: r.current,
threshold: r.threshold,
predicted: r.predicted,
probability: r.probability,
}),
)
: [`- ${i18n.t('aiops.predictutilsts.sm3n4o5p6')}`];
: [`- ${i18n.t('aiops.costPredict.noBudgetRisk')}`];
const serviceLines =
topServices.length > 0
? topServices.map((s) => `- ${s.service}:${s.cost.toFixed(2)} 元`)
: [`- ${i18n.t('aiops.predictutilsts.sn4o5p6q7')}`];
? topServices.map((s) => i18n.t('aiops.costPredict.serviceLine', { service: s.service, cost: s.cost.toFixed(2) }))
: [`- ${i18n.t('aiops.costPredict.noServiceDetail')}`];
return [
i18n.t('aiops.predictutilsts.so5p6q7r8'),
i18n.t('aiops.costPredict.promptIntro'),
'',
i18n.t('aiops.predictutilsts.sp6q7r8s9'),
`- 本月已花:${summary.monthCost.toFixed(2)} 元`,
`- 预测月末:${summary.predictedMonthEnd.toFixed(2)} 元(置信 ${summary.confidence}%)`,
`- 全月日均:${summary.dailyAvg.toFixed(2)} 元;近7日日均:${recentDailyAvg.toFixed(2)} 元;剩余 ${summary.daysRemaining} 天`,
`- 账单形态:${patternHint}`,
i18n.t('aiops.costPredict.sectionOverview'),
i18n.t('aiops.costPredict.monthSpent', { amount: summary.monthCost.toFixed(2) }),
i18n.t('aiops.costPredict.predictedEnd', { amount: summary.predictedMonthEnd.toFixed(2), confidence: summary.confidence }),
i18n.t('aiops.costPredict.dailySummary', { dailyAvg: summary.dailyAvg.toFixed(2), recentAvg: recentDailyAvg.toFixed(2), daysRemaining: summary.daysRemaining }),
i18n.t('aiops.costPredict.billingPatternLabel', { hint: patternHint }),
'',
i18n.t('aiops.predictutilsts.sq7r8s9t0'),
i18n.t('aiops.costPredict.sectionTopServices'),
...serviceLines,
'',
i18n.t('aiops.predictutilsts.sr8s9t0u1'),
i18n.t('aiops.costPredict.sectionDimensionForecast'),
...dimLines,
'',
i18n.t('aiops.predictutilsts.ss9t0u1v2'),
i18n.t('aiops.costPredict.sectionBudgetRisk'),
...riskLines,
'',
i18n.t('aiops.predictutilsts.st0u1v2w3'),
i18n.t('aiops.predictutilsts.su1v2w3x4'),
i18n.t('aiops.predictutilsts.sv2w3x4y5'),
i18n.t('aiops.predictutilsts.sw3x4y5z6'),
i18n.t('aiops.predictutilsts.sx4y5z6a7'),
i18n.t('aiops.costPredict.promptRequirements'),
i18n.t('aiops.costPredict.promptReq1'),
i18n.t('aiops.costPredict.promptReq2'),
i18n.t('aiops.costPredict.promptReq3'),
i18n.t('aiops.costPredict.promptReq4'),
].join('\n');
}