结论:uni.setLocale 不能用于 App 端实时多语言切换,因其仅冷启动生效且 Android 会强制重启;应使用 vue-i18n@9.1.9(Vue3)或 @dcloudio/uni-i18n(Vue2),配合 storage 持久化、i18n.locale 更新与页面重载三步同步。
直接说结论:在 uni-app 中实现多语言切换,uni.setlocale 不能用于实时生效的 app 端语言切换——它只在冷启动时读取,且 android 上会强制重启应用;真正可行的方案是用 vue-i18n@9.1.9(vue3 项目)或 @dcloudio/uni-i18n(vue2 项目),配合手动持久化、页面重载与原生组件文案同步更新。
为什么 uni.setLocale 切换语言会重启 App?
Android 系统在检测到应用 Locale 变更时,会销毁并重建所有 Activity,这是系统级行为。而 uni.setLocale 正是触发了这一机制。iOS 虽不重启,但也不响应热更新——$t('hello') 仍返回旧语言文本,因为该 API 只改全局标识,不联动 i18n 实例或 Vue 响应式系统。
- 调用
uni.setLocale('en-US')后,uni.showToast的按钮文字可能还是中文,因为内置组件不自动刷新 -
uni.getLocale()返回的是“上次设置值”,不是当前生效语言,不可靠 - H5 和小程序端表现不一致:H5 部分响应,App 端完全不响应
i18n.locale变更
Vue3 项目必须用 vue-i18n@9.1.9,别装新版
vue-i18n v10+ 在 uni-app App 端会报错 TypeError: i18n.__VUE_I18N_BRIDGE__ is not defined,导致白屏;v9.1.9 是官方验证兼容性最好的版本,且必须启用 Composition API 模式(legacy: false)。
- 初始化时要写
createI18n({ legacy: false, locale: savedLang }),否则useI18n()在子组件里拿不到最新 locale -
locale.value = 'ja'是有效的,但仅限setup()内;模板中仍依赖{{ $t('key') }}自动响应 - 不要在
onLoad生命周期里调用useI18n()——此时 i18n 实例尚未初始化,会拿到空对象
切换语言必须三步同步:storage + i18n.locale + 页面重载
漏掉任意一环,就会出现“界面上切了,但弹窗按钮还是中文”这类问题。
- 先调用
uni.setStorageSync('lang', 'en-US'),确保退出重进后语言不丢失 - 再设
i18n.locale.value = 'en-US',让$t、useI18n()等立刻响应 - 最后执行
uni.reLaunch({ url: '/pages/index/index' })或uni.redirectTo,强制页面重建——App 端无其他办法让原生 TabBar、导航栏等同步更新 - TabBar 文案需在
pages.json中通过tabBar.list[0].text动态写死,或使用uni.setTabBarItem手动更新
语言包结构和加载方式容易踩坑
把所有翻译塞进一个大 JSON 文件,App 分包加载时容易卡顿甚至白屏;键名不统一会导致 fallback 到 key 字符串本身,界面直接显示 user.name。
- 按模块拆分:如
locales/zh-CN/user.js、locales/en-US/user.js,避免单文件 >200KB - 所有语言包必须用完全相同的 key 结构,比如都叫
common.welcome,不能中文写欢迎、英文写welcomeText - 推荐用
i18n.mergeLocaleMessage('zh-Hans', { home: {} })动态合并,减少首屏体积 - 日期/数字格式不随语言变?因为
IntlAPI 在 App 端不自动继承系统 locale,必须显式传参:date.toLocaleDateString(useI18n().locale.value)
最常被忽略的一点:原生组件(如 uni.showModal、uni.showToast)的按钮文字,只认 uni.setLocale 设置的值,不认 i18n.locale ——所以你必须同步调用两者,并确保 storage 持久化,否则用户重启 App 后,弹窗还是中文,但页面是英文,体验割裂。


















