fix(i18n): 语言切换后保持菜单标题语种正确

loadMessages 并入对应 menu-titles;inject 不再把简体挂到短码 zh,避免 zh-TW/zh-HK 回退成简体。
This commit is contained in:
DaxPay Dev
2026-07-14 12:32:20 +08:00
parent 7378707c25
commit 30ff643ea1
3 changed files with 89 additions and 6 deletions

View File

@@ -8,21 +8,30 @@ import zhCnMenuTitles from '../../locales/menu-titles/zh-CN.json';
import enUsMenuTitles from '../../locales/menu-titles/en-US.json';
import zhHkMenuTitles from '../../locales/menu-titles/zh-HK.json';
import zhTwMenuTitles from '../../locales/menu-titles/zh-TW.json';
import jaJpMenuTitles from '../../locales/menu-titles/ja-JP.json';
import koKrMenuTitles from '../../locales/menu-titles/ko-KR.json';
/**
* 注入菜单标题国际化文案
* 数据源为静态语言包文件menu-titles/*.json不依赖 DB title_cn/title_en 列
* DB 仅存 i18n_key文案真相源在语言包
*
* 写入完整 localezh-CN/en-US/zh-TW/zh-HK与短码zh/en避免 fallback 差异导致侧栏标题未命中
* 写入完整 localezh-CN/en-US/zh-TW/zh-HK/ja-JP/ko-KR
* 禁止把简体挂到短码 `zh`vue-i18n 对 zh-TW/zh-HK 会回退 zh会导致繁体界面菜单标题变简体。
* en/ja/ko 短码可保留(与完整 locale 文案同语种,回退无害)。
* 语言切换时由 locales/loadMessages 再次并入对应 menu-titles与本函数双保险。
*/
export function injectMenuI18n() {
i18n.global.mergeLocaleMessage('zh-CN', zhCnMenuTitles);
i18n.global.mergeLocaleMessage('zh', zhCnMenuTitles);
// 不注入短码 zh避免 zh-TW/zh-HK 回退到简体
i18n.global.mergeLocaleMessage('en-US', enUsMenuTitles);
i18n.global.mergeLocaleMessage('en', enUsMenuTitles);
i18n.global.mergeLocaleMessage('zh-TW', zhTwMenuTitles);
i18n.global.mergeLocaleMessage('zh-HK', zhHkMenuTitles);
i18n.global.mergeLocaleMessage('ja-JP', jaJpMenuTitles);
i18n.global.mergeLocaleMessage('ja', jaJpMenuTitles);
i18n.global.mergeLocaleMessage('ko-KR', koKrMenuTitles);
i18n.global.mergeLocaleMessage('ko', koKrMenuTitles);
}
/**

View File

@@ -11,26 +11,67 @@ import { $t, setupI18n as coreSetup, loadLocalesMapFromDir } from '@vben/locales
import { preferences } from '@vben/preferences';
import antdEnLocale from 'antdv-next/dist/locale/en_US';
import antdJaLocale from 'antdv-next/dist/locale/ja_JP';
import antdKoLocale from 'antdv-next/dist/locale/ko_KR';
import antdHkLocale from 'antdv-next/dist/locale/zh_HK';
import antdTwLocale from 'antdv-next/dist/locale/zh_TW';
import antdDefaultLocale from 'antdv-next/dist/locale/zh_CN';
import dayjs from 'dayjs';
import { VxeUI } from 'vxe-table';
// 菜单标题包:在 loadMessages 内并入,避免与 menu.api 循环依赖;与 injectMenuI18n 同源
import enUsMenuTitles from './menu-titles/en-US.json';
import jaJpMenuTitles from './menu-titles/ja-JP.json';
import koKrMenuTitles from './menu-titles/ko-KR.json';
import zhCnMenuTitles from './menu-titles/zh-CN.json';
import zhHkMenuTitles from './menu-titles/zh-HK.json';
import zhTwMenuTitles from './menu-titles/zh-TW.json';
const antdLocale = ref<Locale>(antdDefaultLocale);
const modules = import.meta.glob('./langs/**/*.json');
const localesMap = loadLocalesMapFromDir(/\.\/langs\/([^/]+)\/(.*)\.json$/, modules);
/** 按完整 locale 取菜单标题flat key不注入短码 zh防止繁体回退简体 */
function getMenuTitlesFor(lang: SupportedLanguagesType): Record<string, string> {
switch (lang) {
case 'en-US': {
return enUsMenuTitles as Record<string, string>;
}
case 'zh-TW': {
return zhTwMenuTitles as Record<string, string>;
}
case 'zh-HK': {
return zhHkMenuTitles as Record<string, string>;
}
case 'ja-JP': {
return jaJpMenuTitles as Record<string, string>;
}
case 'ko-KR': {
return koKrMenuTitles as Record<string, string>;
}
case 'zh-CN':
default: {
return zhCnMenuTitles as Record<string, string>;
}
}
}
/**
* 加载应用特有的语言包
* 这里也可以改造为从服务端获取翻译数据
* 每次语言切换都会走这里:业务 langs + menu-titles 一并 merge
* 避免 setLocaleMessage 冲掉登录时 inject 的菜单标题后回退到错误语种
* @param lang
*/
async function loadMessages(lang: SupportedLanguagesType) {
const [appLocaleMessages] = await Promise.all([localesMap[lang]?.(), loadThirdPartyMessage(lang)]);
return appLocaleMessages?.default;
const base = (appLocaleMessages?.default ?? {}) as Record<string, unknown>;
// 菜单标题真相源menu-titles/{lang}.json与 injectMenuI18n 同源
return {
...base,
...getMenuTitlesFor(lang),
};
}
/**
@@ -64,6 +105,14 @@ async function loadDayjsLocale(lang: SupportedLanguagesType) {
locale = await import('dayjs/locale/zh-hk');
break;
}
case 'ja-JP': {
locale = await import('dayjs/locale/ja');
break;
}
case 'ko-KR': {
locale = await import('dayjs/locale/ko');
break;
}
default: {
locale = await import('dayjs/locale/zh-cn');
}
@@ -97,6 +146,14 @@ async function loadAntdLocale(lang: SupportedLanguagesType) {
antdLocale.value = antdHkLocale;
break;
}
case 'ja-JP': {
antdLocale.value = antdJaLocale;
break;
}
case 'ko-KR': {
antdLocale.value = antdKoLocale;
break;
}
}
}
@@ -122,6 +179,14 @@ async function loadVxeLocale(lang: SupportedLanguagesType) {
VxeUI.setLanguage('zh-HK');
break;
}
case 'ja-JP': {
VxeUI.setLanguage('ja-JP');
break;
}
case 'ko-KR': {
VxeUI.setLanguage('ko-KR');
break;
}
}
}

View File

@@ -1,5 +1,7 @@
import { $t, i18n } from '#/locales';
import enUsPerm from '../locales/langs/en-US/perm.json';
import jaJpPerm from '../locales/langs/ja-JP/perm.json';
import koKrPerm from '../locales/langs/ko-KR/perm.json';
import zhCnPerm from '../locales/langs/zh-CN/perm.json';
import zhHkPerm from '../locales/langs/zh-HK/perm.json';
import zhTwPerm from '../locales/langs/zh-TW/perm.json';
@@ -17,7 +19,8 @@ function toFlatPermMessages(bag: Record<string, string>): Record<string, string>
/**
* 注入权限码国际化文案flat key
* 写入 zh-CN/en-US/zh-TW/zh-HK 与短码 zh/en
* 写入完整 locale zh-CN/en-US/zh-TW/zh-HK/ja-JP/ko-KR以及短码 en/ja/ko
* 禁止把简体挂到短码 `zh`vue-i18n 对 zh-TW/zh-HK 会回退 zh繁体界面会串成简体
* 可重复调用:语言切换会 setLocaleMessage 覆盖整包,需在翻译前再次 merge
*/
export function injectPermI18n() {
@@ -25,12 +28,18 @@ export function injectPermI18n() {
const en = toFlatPermMessages(enUsPerm as Record<string, string>);
const tw = toFlatPermMessages(zhTwPerm as Record<string, string>);
const hk = toFlatPermMessages(zhHkPerm as Record<string, string>);
const ja = toFlatPermMessages(jaJpPerm as Record<string, string>);
const ko = toFlatPermMessages(koKrPerm as Record<string, string>);
i18n.global.mergeLocaleMessage('zh-CN', zh);
i18n.global.mergeLocaleMessage('zh', zh);
// 不注入短码 zh避免 zh-TW/zh-HK 回退到简体
i18n.global.mergeLocaleMessage('en-US', en);
i18n.global.mergeLocaleMessage('en', en);
i18n.global.mergeLocaleMessage('zh-TW', tw);
i18n.global.mergeLocaleMessage('zh-HK', hk);
i18n.global.mergeLocaleMessage('ja-JP', ja);
i18n.global.mergeLocaleMessage('ja', ja);
i18n.global.mergeLocaleMessage('ko-KR', ko);
i18n.global.mergeLocaleMessage('ko', ko);
}
/**