feat: 新增多语言国际化页面配置,修复部分页面硬编码文案
1. 新增了zh-cn和en-us两套语言下的大量页面国际化配置文件,覆盖商品、订单、会员、营销活动等多个业务模块 2. 修复了分销页面、VR页面、支付页面、商品不存在页面的硬编码提示文案,替换为国际化调用 3. 删除了原有的i18n检查脚本文件
This commit is contained in:
@@ -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 翻译更新后,推送重渲染信号;
|
||||
|
||||
Reference in New Issue
Block a user