|
@@ -1,105 +1,143 @@
|
|
|
import { createI18n } from 'vue-i18n'
|
|
import { createI18n } from 'vue-i18n'
|
|
|
import { Locale } from 'vant'
|
|
import { Locale } from 'vant'
|
|
|
|
|
|
|
|
-// 1. 静态引入:确保路径正确 (根据你的截图 ./lang/ 是对的)
|
|
|
|
|
-import userZhCN from './lang/zh-CN.js'
|
|
|
|
|
-import vantZhCN from 'vant/es/locale/lang/zh-CN'
|
|
|
|
|
-
|
|
|
|
|
-// 2. 数据清洗:确保拿到的是纯净的对象,而不是 Module
|
|
|
|
|
-const userData = userZhCN.default || userZhCN
|
|
|
|
|
-const vantData = vantZhCN.default || vantZhCN
|
|
|
|
|
-
|
|
|
|
|
-// 3. 深度合并:将 Vant 和 User 数据合并
|
|
|
|
|
-// ⚠️ 注意顺序:userData 在后,确保用户自定义的优先级更高
|
|
|
|
|
-const finalZhCN = { ...vantData, ...userData }
|
|
|
|
|
-
|
|
|
|
|
-// 🔍 再次确认日志
|
|
|
|
|
-console.log('🚀 i18n Data Ready:', {
|
|
|
|
|
- hasCommon: !!finalZhCN.common,
|
|
|
|
|
- switchLang: finalZhCN.common?.switchLang
|
|
|
|
|
-})
|
|
|
|
|
|
|
+// --- 1. 静态引入默认语言 (兜底保障) ---
|
|
|
|
|
+// 使用相对路径,确保构建工具能正确解析
|
|
|
|
|
+import defaultZhCN from './lang/zh-CN.js'
|
|
|
|
|
+import defaultVantZhCN from 'vant/es/locale/lang/zh-CN'
|
|
|
|
|
+
|
|
|
|
|
+// --- 2. 工具函数:模块标准化 ---
|
|
|
|
|
+// 解决 import() 导入后可能是 Module 对象也可能是直接对象的问题
|
|
|
|
|
+const unwrap = (module) => (module && module.default ? module.default : module) || {}
|
|
|
|
|
+
|
|
|
|
|
+// --- 3. 初始数据准备 ---
|
|
|
|
|
+const userMessages = unwrap(defaultZhCN)
|
|
|
|
|
+const vantMessages = unwrap(defaultVantZhCN)
|
|
|
|
|
+
|
|
|
|
|
+// 初始加载时仅包含中文,其他语言按需加载
|
|
|
|
|
+const messages = {
|
|
|
|
|
+ 'zh-CN': userMessages
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 记录已加载的语言,避免重复网络请求
|
|
|
|
|
+const loadedLanguages = ['zh-CN']
|
|
|
|
|
|
|
|
-const savedLang = localStorage.getItem('app-lang') || 'zh-CN'
|
|
|
|
|
|
|
+// --- 4. Vant 语言包映射表 (配置中心) ---
|
|
|
|
|
+// 将 Vant 的语言包路径映射到你的 app-lang 代码
|
|
|
|
|
+// 生产环境建议按需配置,不要全部引入
|
|
|
|
|
+const vantLocalesMap = {
|
|
|
|
|
+ 'zh-CN': () => Promise.resolve({ default: defaultVantZhCN }), // 静态
|
|
|
|
|
+ 'en-US': () => import('vant/es/locale/lang/en-US'),
|
|
|
|
|
+ 'de-DE': () => import('vant/es/locale/lang/de-DE'),
|
|
|
|
|
+ 'es-ES': () => import('vant/es/locale/lang/es-ES'),
|
|
|
|
|
+ 'fr-FR': () => import('vant/es/locale/lang/fr-FR'),
|
|
|
|
|
+ 'pt-PT': () => import('vant/es/locale/lang/pt-BR'), // Vant 可能只有 pt-BR
|
|
|
|
|
+ 'id-ID': () => import('vant/es/locale/lang/id-ID'),
|
|
|
|
|
+ 'ru-RU': () => import('vant/es/locale/lang/ru-RU'),
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-const i18n = createI18n({
|
|
|
|
|
- legacy: false,
|
|
|
|
|
- locale: savedLang,
|
|
|
|
|
- fallbackLocale: 'zh-CN',
|
|
|
|
|
- globalInjection: true,
|
|
|
|
|
- // 4. 初始化消息:这里是关键,确保 zh-CN 永远有值
|
|
|
|
|
- messages: {
|
|
|
|
|
- 'zh-CN': finalZhCN
|
|
|
|
|
|
|
+// --- 5. 初始化 i18n 实例 ---
|
|
|
|
|
+// 优先从 sessionStorage 读取,无则回退到 zh-CN
|
|
|
|
|
+const getInitialLang = () => {
|
|
|
|
|
+ const cached = sessionStorage.getItem('app-lang')
|
|
|
|
|
+ if (cached && cached !== 'null' && cached !== 'undefined') {
|
|
|
|
|
+ return cached
|
|
|
}
|
|
}
|
|
|
|
|
+ return 'zh-CN'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const savedLang = getInitialLang()
|
|
|
|
|
+
|
|
|
|
|
+const i18n = createI18n({
|
|
|
|
|
+ legacy: false, // 组合式 API 模式
|
|
|
|
|
+ locale: savedLang, // 初始语言
|
|
|
|
|
+ fallbackLocale: 'zh-CN', // 翻译缺失时回退
|
|
|
|
|
+ globalInjection: true, // 全局注入 $t
|
|
|
|
|
+ messages // 初始消息池
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
-// 记录已加载语言
|
|
|
|
|
-const loadedLanguages = ['zh-CN']
|
|
|
|
|
|
|
+// 初始应用 Vant 配置 (如果是中文直接应用,其他语言由 main.js 调用 loadLanguageAsync 触发)
|
|
|
|
|
+if (savedLang === 'zh-CN') {
|
|
|
|
|
+ Locale.use('zh-CN', vantMessages)
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-// 5. 设置 Vant 语言的辅助函数
|
|
|
|
|
-function setVantLanguage(lang, messages) {
|
|
|
|
|
- if (lang === 'zh-CN') {
|
|
|
|
|
- Locale.use('zh-CN', vantData)
|
|
|
|
|
- } else {
|
|
|
|
|
- Locale.use(lang, messages)
|
|
|
|
|
|
|
+// --- 6. 核心逻辑:设置语言环境 (副作用管理) ---
|
|
|
|
|
+function setI18nLanguage(lang, vantMsg = null) {
|
|
|
|
|
+ // 1. 切换 Vue I18n
|
|
|
|
|
+ i18n.global.locale.value = lang
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 切换 HTML 属性 (SEO & 浏览器识别)
|
|
|
|
|
+ if (typeof document !== 'undefined') {
|
|
|
|
|
+ document.querySelector('html').setAttribute('lang', lang)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 持久化存储
|
|
|
|
|
+ sessionStorage.setItem('app-lang', lang)
|
|
|
|
|
+
|
|
|
|
|
+ // 4. 切换 Vant 组件语言
|
|
|
|
|
+ // 如果没传 vantMsg,尝试去内存找,或者回退到默认
|
|
|
|
|
+ if (vantMsg) {
|
|
|
|
|
+ Locale.use(lang, vantMsg)
|
|
|
|
|
+ } else if (lang === 'zh-CN') {
|
|
|
|
|
+ Locale.use('zh-CN', vantMessages)
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ return lang
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// --- 7. 导出:异步加载函数 ---
|
|
|
export async function loadLanguageAsync(lang) {
|
|
export async function loadLanguageAsync(lang) {
|
|
|
- // 空值检查
|
|
|
|
|
- if (!lang || lang === 'undefined' || lang === 'null') {
|
|
|
|
|
|
|
+ // 参数校验
|
|
|
|
|
+ if (!lang || typeof lang !== 'string') {
|
|
|
|
|
+ // console.warn(`[i18n] Invalid lang: ${lang}, fallback to zh-CN`)
|
|
|
return loadLanguageAsync('zh-CN')
|
|
return loadLanguageAsync('zh-CN')
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // A. 如果是中文:直接切过去,千万不要重新加载,防止覆盖
|
|
|
|
|
- if (lang === 'zh-CN') {
|
|
|
|
|
- i18n.global.locale.value = 'zh-CN'
|
|
|
|
|
- setVantLanguage('zh-CN')
|
|
|
|
|
- document.querySelector('html').setAttribute('lang', 'zh-CN')
|
|
|
|
|
- localStorage.setItem('app-lang', 'zh-CN')
|
|
|
|
|
- return 'zh-CN'
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // B. 如果已加载:直接切
|
|
|
|
|
|
|
+ // 情况 A: 语言已加载 (包含静态的 zh-CN)
|
|
|
if (loadedLanguages.includes(lang)) {
|
|
if (loadedLanguages.includes(lang)) {
|
|
|
- i18n.global.locale.value = lang
|
|
|
|
|
- setVantLanguage(lang, i18n.global.getLocaleMessage(lang)) // 尝试从 i18n 实例获取 vant 数据有点难,这里简化处理
|
|
|
|
|
- document.querySelector('html').setAttribute('lang', lang)
|
|
|
|
|
- localStorage.setItem('app-lang', lang)
|
|
|
|
|
|
|
+ // 如果还没切换过去,执行切换
|
|
|
|
|
+ if (i18n.global.locale.value !== lang) {
|
|
|
|
|
+ // 尝试重新获取 Vant 语言配置 (通常不需要,因为 Vant 是全局单例,但为了保险)
|
|
|
|
|
+ return setI18nLanguage(lang)
|
|
|
|
|
+ }
|
|
|
return lang
|
|
return lang
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // C. 动态加载新语言
|
|
|
|
|
|
|
+ // 情况 B: 新语言,需要动态加载
|
|
|
try {
|
|
try {
|
|
|
- // 动态导入映射
|
|
|
|
|
- // ⚠️ 确保这些文件在 src/locales/lang/ 目录下存在
|
|
|
|
|
- const userImport = import(`./lang/${lang}.js`)
|
|
|
|
|
|
|
+ // 并行加载:用户业务翻译 + Vant 组件翻译
|
|
|
|
|
+ // 这里的 import 路径必须能被 Vite/Webpack 静态分析到
|
|
|
|
|
+ const userMsgPromise = import(`./lang/${lang}.js`)
|
|
|
|
|
|
|
|
- // Vant 映射
|
|
|
|
|
- let vantImportPromise = Promise.resolve({ default: {} })
|
|
|
|
|
- if (lang === 'en-US') vantImportPromise = import('vant/es/locale/lang/en-US')
|
|
|
|
|
- // ... 可以按需添加其他 Vant 语言 ...
|
|
|
|
|
|
|
+ // 查找 Vant 映射,如果没有则返回空对象防止报错
|
|
|
|
|
+ const vantMsgPromise = vantLocalesMap[lang]
|
|
|
|
|
+ ? vantLocalesMap[lang]()
|
|
|
|
|
+ : Promise.resolve({ default: {} })
|
|
|
|
|
|
|
|
- const [userMod, vantMod] = await Promise.all([userImport, vantImportPromise])
|
|
|
|
|
|
|
+ const [userMsgModule, vantMsgModule] = await Promise.all([
|
|
|
|
|
+ userMsgPromise,
|
|
|
|
|
+ vantMsgPromise
|
|
|
|
|
+ ])
|
|
|
|
|
|
|
|
- const userMsg = userMod.default || userMod
|
|
|
|
|
- const vantMsg = vantMod.default || vantMod
|
|
|
|
|
|
|
+ const finalUserMsg = unwrap(userMsgModule)
|
|
|
|
|
+ const finalVantMsg = unwrap(vantMsgModule)
|
|
|
|
|
|
|
|
- const finalMsg = { ...vantMsg, ...userMsg }
|
|
|
|
|
|
|
+ // 将用户翻译注入 i18n
|
|
|
|
|
+ i18n.global.setLocaleMessage(lang, finalUserMsg)
|
|
|
|
|
|
|
|
- i18n.global.setLocaleMessage(lang, finalMsg)
|
|
|
|
|
|
|
+ // 标记已加载
|
|
|
loadedLanguages.push(lang)
|
|
loadedLanguages.push(lang)
|
|
|
|
|
|
|
|
- // 切换
|
|
|
|
|
- i18n.global.locale.value = lang
|
|
|
|
|
- setVantLanguage(lang, vantMsg)
|
|
|
|
|
- document.querySelector('html').setAttribute('lang', lang)
|
|
|
|
|
- localStorage.setItem('app-lang', lang)
|
|
|
|
|
|
|
+ // 应用切换
|
|
|
|
|
+ return setI18nLanguage(lang, finalVantMsg)
|
|
|
|
|
|
|
|
- return lang
|
|
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
|
- console.error(`❌ Load lang ${lang} failed:`, e)
|
|
|
|
|
- // 失败回退到中文
|
|
|
|
|
- return loadLanguageAsync('zh-CN')
|
|
|
|
|
|
|
+ // console.error(`[i18n] Failed to load language: ${lang}`, e)
|
|
|
|
|
+ // 加载失败时,如果目标不是中文,则回退到中文
|
|
|
|
|
+ if (lang !== 'zh-CN') {
|
|
|
|
|
+ return loadLanguageAsync('zh-CN')
|
|
|
|
|
+ }
|
|
|
|
|
+ throw e // 如果中文也挂了,抛出异常让 main.js 处理
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|