uni-app动态import语言包白屏主因是跨端路径解析不一致:H5支持省略扩展名,小程序/App端必须显式写.json且禁用模板字符串;响应式失效则因$t非响应式,需computed封装或forceUpdate触发重绘。
uni-app 动态 import 语言包时路径报错或 404
常见现象是 import('@/locales/zh_cn.json') 在 h5 正常,但小程序或 app 端白屏,控制台报 cannot find module 或 404。根本原因是不同平台对动态 import 的路径解析规则不一致:h5 支持省略扩展名,而微信小程序、app 端必须显式带 .json;同时,路径不能是拼接字符串(如 `@/locales/${lang}.json`),否则 webpack 无法静态分析,导致构建时未打包该资源。
实操建议:
- 所有语言文件路径必须写死扩展名,例如
import('@/locales/zh-CN.json')✅,import('@/locales/zh-CN')❌ - 避免模板字符串动态路径;改用 if/else 或对象映射明确列出支持的语言项
- 确保
locales目录在uni-app构建上下文中可见——不要放在static下(不参与构建),也不要放在node_modules外的未引用路径中 - JSON 文件必须无 BOM、无注释,否则某些平台(尤其是 App 端)解析失败,静默返回
undefined
切换语言后 $t 不更新,页面文字不变
这不是语言包没加载成功,而是 Vue 响应式系统没“感知”到语言变更。uni-app 中 this.$i18n.locale = 'en-US' 只改了实例状态,但组件模板里的 {{ $t('home.title') }} 不会自动重求值——$t 是函数调用,不是响应式依赖。
实操建议:
- 别直接在模板里裸写
$t(),改用computed包一层,比如:lang() { return this.$t('') },然后模板用{{ lang.home.title }} - 如果用
@dcloudio/uni-i18n,它自带watch方法,可在组件内监听:this.$i18n.watch(() => this.$forceUpdate()) - App 端不支持
uni.setLocale,这个 API 调用无效且静默失败;必须手动赋值this.$i18n.locale并触发更新 - 切换前确保语言包已通过
setLocaleMessage注入,否则$t返回 key 本身或空字符串
按需加载 + 本地缓存语言选择的完整流程
用户选了英文,下次打开还是中文?问题出在初始化时机:语言包还没加载完,$t 就开始渲染了,导致 fallback 到空值或默认语言。
Gene6 FTP Server Professional v3.10.0.2 多语言特别版(集成了中文)
实操建议:
- 在
App.vue的onLaunch或onShow中读取缓存:const locale = uni.getStorageSync('locale') || 'zh-CN' - 立即设置
i18n.locale = locale,但此时 messages 为空 → 先设空 locale,再异步加载 - 加载完成后调用
i18n.setLocaleMessage(locale, langModule.default),并触发一次this.$forceUpdate()或通知全局状态变化 - 为防白屏,可在根组件加 loading 状态,或给
$t加 fallback,例如:{{ $t('home.title') || 'Loading...' }}
App 端 setLocaleMessage 后仍显示旧翻译
现象:iOS/Android 上调用 setLocaleMessage 成功,i18n.locale 已变,但页面文字纹丝不动。原因很隐蔽:uni-app 的 App 端对 i18n 实例的响应式劫持不完整,尤其当语言包是深层嵌套结构(如 { en: { user: { name: 'Name' } } })时,setLocaleMessage 替换整个 messages 对象,但 Vue 无法检测到内部属性变化。
实操建议:
- 加载语言包后,不要只调
setLocaleMessage,紧接着手动触发一次 locale 赋值:i18n.locale = i18n.locale(触发 setter 重置响应式依赖) - 更稳妥的做法:用
Object.assign({}, langModule.default)浅拷贝语言对象再传入,避免引用旧对象导致响应失效 - 避免在
beforeCreate或created钩子中 await import —— 会阻塞实例初始化,改为onLoad+nextTick后更新 - 调试时打印
i18n.messages[locale]确认是否真注入成功,而不是只改了 locale 字段









