feat: 新增多语言国际化页面配置,修复部分页面硬编码文案

1. 新增了zh-cn和en-us两套语言下的大量页面国际化配置文件,覆盖商品、订单、会员、营销活动等多个业务模块
2. 修复了分销页面、VR页面、支付页面、商品不存在页面的硬编码提示文案,替换为国际化调用
3. 删除了原有的i18n检查脚本文件
This commit is contained in:
2026-06-25 11:00:50 +08:00
parent 5f41febd70
commit 8e218bb663
509 changed files with 49155 additions and 55313 deletions

View File

@@ -182,91 +182,20 @@ function processRoutePath(route) {
}
// 加载语言包(同步方式)
// 2026-06-24 升级为多文件结构:lang/<locale>/index.js 聚合各业务模块
// 同时保留旧单文件 lang/<locale>.js 作为过渡,新结构优先,旧结构兜底
// path 参数保留仅为兼容旧调用签名(忽略值)。
function loadLangPackSync(lang, path) {
try {
const cacheKey = `${lang}__single`;
if (loadedLangPacks[cacheKey]) {
return loadedLangPacks[cacheKey];
if (loadedLangPacks[`${lang}_${path}`]) {
return loadedLangPacks[`${lang}_${path}`];
}
// 1. 先加载旧单文件(兜底)
let legacyData = {};
try {
legacyData = require(`@/lang/${lang}.js`).lang || {};
} catch (e) {
// 旧文件不存在也没关系
}
// 2. 再加载新多文件聚合(优先)
let modularData = {};
try {
modularData = require(`@/lang/${lang}/index.js`).lang || {};
} catch (e) {
// 新文件不存在也没关系
}
// 3. 深度合并:新结构覆盖旧结构
const langData = deepMergeLang(legacyData, modularData);
loadedLangPacks[cacheKey] = langData;
const langData = require(`@/lang/${lang}/${path}.js`).lang;
loadedLangPacks[`${lang}_${path}`] = langData;
return langData;
} catch (error) {
// console.error(`加载语言包 ${lang} 失败:`, error);
// console.error(`加载语言包 ${lang}/${path} 失败:`, error);
return {};
}
}
// 深度合并语言包对象,新值覆盖旧值
function deepMergeLang(target, source) {
const result = { ...target };
for (const key of Object.keys(source)) {
const srcVal = source[key];
const tgtVal = result[key];
if (srcVal && typeof srcVal === 'object' && !Array.isArray(srcVal)
&& tgtVal && typeof tgtVal === 'object' && !Array.isArray(tgtVal)) {
result[key] = deepMergeLang(tgtVal, srcVal);
} else {
result[key] = srcVal;
}
}
return result;
}
// 检测 page module 是否覆盖 common 已有 key。
// 自 2026-06-23 起强制:page 覆盖 common 直接 throw 阻断,防止 AI 默默改文案。
// 历史上 commit f258987 / 47098bd 都曾让 page 覆盖 common 引发翻译错乱。
function detectShadowing(commonLang, pageLang, langPath) {
if (!pageLang || typeof pageLang !== 'object') return;
if (!commonLang || typeof commonLang !== 'object') return;
const conflicts = [];
const walk = (pageNode, commonNode, prefix) => {
if (!pageNode || typeof pageNode !== 'object') return;
if (!commonNode || typeof commonNode !== 'object') return;
for (const k of Object.keys(pageNode)) {
const fullKey = prefix ? `${prefix}.${k}` : k;
if (Object.prototype.hasOwnProperty.call(commonNode, k)) {
const pageVal = pageNode[k];
const commonVal = commonNode[k];
// 仅当 page 想覆盖 common 的"叶子"值(字符串)时报错
if (typeof pageVal === 'string' && typeof commonVal === 'string' && pageVal !== commonVal) {
conflicts.push({ key: fullKey, commonVal, pageVal });
} else if (typeof pageVal === 'object' && pageVal !== null && typeof commonVal === 'object' && commonVal !== null) {
// 嵌套结构继续递归
walk(pageVal, commonVal, fullKey);
}
}
}
};
walk(pageLang, commonLang, '');
if (conflicts.length > 0) {
const list = conflicts.map(c => ` - "${c.key}": common="${c.commonVal}" vs page="${c.pageVal}"`).join('\n');
throw new Error(
`[i18n] page module "${langPath || 'unknown'}" overrides common.js keys:\n${list}\n` +
`Fix: rename the page key with a business namespace prefix, e.g. "<namespace>.${conflicts[0].key.split('.').pop()}"\n` +
`See docs/i18n-rules.md for the rule.`
);
}
}
/**
* 获得当前本地语言
* @returns
@@ -634,20 +563,12 @@ export default {
let mergedLang = _mergedLangCache[mergeCacheKey];
if (!mergedLang) {
try {
// 自 2026-06-23 起:只加载单一权威 lang/<locale>.js
// page module 已废弃,因此不再读取 lang/<locale>/<page>.js
const lang = loadLangPackSync(locale, langPath);
const currentPageLang = {}; // 兼容旧调用;旧 page module 已废弃
// detectShadowing 在 page 部分为空时直接返回,不抛错
detectShadowing(lang, currentPageLang, langPath);
const lang = loadLangPackSync(locale, 'common');
const currentPageLang = langPath ? loadLangPackSync(locale, langPath) : {};
mergedLang = Object.assign(deepCloneLang(lang), deepCloneLang(currentPageLang));
const ovr = overrideCache[getOverrideMemKey(locale)];
if (ovr && ovr.map) applyOverrides(mergedLang, ovr.map);
} catch (e) {
// detectShadowing 抛错时不应该被静默吞掉——直接重抛
if (e && e.message && e.message.startsWith('[i18n]')) {
throw e;
}
console.error('解析语言包失败:', e, { langPath, field, locale });
mergedLang = {};
}
@@ -759,15 +680,7 @@ export default {
_refreshInProgress = true;
try {
let _page = getCurrentPages()[getCurrentPages().length - 1];
if (_page) {
// 自 2026-06-23 起:每个 page 的 title 集中到 pageTitles.<route> 命名空间
// processRoutePath() 已经把 route 映射成 langPath(如 'order/list')
const _langPath = processRoutePath(_page.route || (_page.$page && _page.$page.path) || '');
const _titleKey = _langPath ? `pageTitles.${_langPath.replace(/\//g, '.')}` : 'pageTitles';
const _title = this.lang(_titleKey);
// 兜底:旧 page module 仍可能在 lang 包外自定义 title,没匹配时用通用 title
this.title(_title && _title !== _titleKey ? _title : this.lang('title'));
}
if (_page) this.title(this.lang("title"));
if (emit) {
// 远端 i18n 翻译更新后,推送重渲染信号;