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:
1 parent
d6ed1978ef
commit
083f4477db
3 files changed
+117
-83
No files matched your search
@@ -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');
|
||||
}
|
||||
Reference in new issue
Block a user