From 30ff643ea1aa5ec0820383b6a8003f9d8877196b Mon Sep 17 00:00:00 2001 From: DaxPay Dev Date: Tue, 14 Jul 2026 12:32:20 +0800 Subject: [PATCH] =?UTF-8?q?fix(i18n):=20=E8=AF=AD=E8=A8=80=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E5=90=8E=E4=BF=9D=E6=8C=81=E8=8F=9C=E5=8D=95=E6=A0=87?= =?UTF-8?q?=E9=A2=98=E8=AF=AD=E7=A7=8D=E6=AD=A3=E7=A1=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit loadMessages 并入对应 menu-titles;inject 不再把简体挂到短码 zh,避免 zh-TW/zh-HK 回退成简体。 --- apps/daxpay-admin/src/api/core/menu.api.ts | 13 +++- apps/daxpay-admin/src/locales/index.ts | 69 +++++++++++++++++++++- apps/daxpay-admin/src/utils/perm-i18n.ts | 13 +++- 3 files changed, 89 insertions(+), 6 deletions(-) diff --git a/apps/daxpay-admin/src/api/core/menu.api.ts b/apps/daxpay-admin/src/api/core/menu.api.ts index 892169012..af2164b33 100644 --- a/apps/daxpay-admin/src/api/core/menu.api.ts +++ b/apps/daxpay-admin/src/api/core/menu.api.ts @@ -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,文案真相源在语言包 * - * 写入完整 locale(zh-CN/en-US/zh-TW/zh-HK)与短码(zh/en),避免 fallback 差异导致侧栏标题未命中 + * 仅写入完整 locale(zh-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); } /** diff --git a/apps/daxpay-admin/src/locales/index.ts b/apps/daxpay-admin/src/locales/index.ts index c3705c2f9..a17895839 100644 --- a/apps/daxpay-admin/src/locales/index.ts +++ b/apps/daxpay-admin/src/locales/index.ts @@ -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(antdDefaultLocale); const modules = import.meta.glob('./langs/**/*.json'); const localesMap = loadLocalesMapFromDir(/\.\/langs\/([^/]+)\/(.*)\.json$/, modules); +/** 按完整 locale 取菜单标题(flat key),不注入短码 zh,防止繁体回退简体 */ +function getMenuTitlesFor(lang: SupportedLanguagesType): Record { + switch (lang) { + case 'en-US': { + return enUsMenuTitles as Record; + } + case 'zh-TW': { + return zhTwMenuTitles as Record; + } + case 'zh-HK': { + return zhHkMenuTitles as Record; + } + case 'ja-JP': { + return jaJpMenuTitles as Record; + } + case 'ko-KR': { + return koKrMenuTitles as Record; + } + case 'zh-CN': + default: { + return zhCnMenuTitles as Record; + } + } +} + /** * 加载应用特有的语言包 - * 这里也可以改造为从服务端获取翻译数据 + * 每次语言切换都会走这里:业务 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; + // 菜单标题真相源: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; + } } } diff --git a/apps/daxpay-admin/src/utils/perm-i18n.ts b/apps/daxpay-admin/src/utils/perm-i18n.ts index 7fe706a39..4e13f64c1 100644 --- a/apps/daxpay-admin/src/utils/perm-i18n.ts +++ b/apps/daxpay-admin/src/utils/perm-i18n.ts @@ -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): Record /** * 注入权限码国际化文案(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); const tw = toFlatPermMessages(zhTwPerm as Record); const hk = toFlatPermMessages(zhHkPerm as Record); + const ja = toFlatPermMessages(jaJpPerm as Record); + const ko = toFlatPermMessages(koKrPerm as Record); 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); } /**