你是否遇到过在 uni-app 中切换语言时,App 突然重启的糟糕体验?本文将带你彻底解决这个问题,实现流畅无感知的多语言切换。
一、问题背景:为什么切换语言会重启?
很多刚接触 uni-app 国际化的同学,会习惯性地使用官方提供的 uni.setLocale 方法来切换语言。代码很简单:
javascript
// 切换语言(会导致 Android App 重启)
uni.setLocale('en-US')
然而,当你把这个代码运行到 Android App 上时,会发现应用直接重启了!这显然不是我们想要的效果。
为什么 Android 上会重启?
这背后是 Android 系统的设计机制:当应用的"语言区域(Locale)"配置发生变化时,系统会销毁当前所有 Activity 并重新创建 ,以确保界面元素(文字方向、日期格式、资源文件等)能完全遵循新语言规则。uni.setLocale 正是触发了这一系统级行为。
💡 官方文档也明确说明了这一点:
uni.setLocale在 Android 平台会导致应用重启。
二、解决方案:使用 vue-i18n 实现无重启切换
既然不能直接调用系统 API,那我们就"绕过"它。核心思路是:
-
放弃
uni.setLocale -
使用
vue-i18n来管理应用内的所有文本翻译 -
只更新
vue-i18n的语言状态,不通知 Android 系统 -
手动更新 原生组件(如 TabBar、导航栏)的文字
这样,应用内的文本会瞬间切换,而不会触发系统层面的 Activity 重建。
三、实战:从零搭建无重启国际化
1. 安装依赖
bash
npm install vue-i18n@9.1.9
注意:请使用
9.1.9版本,这是与 uni-app Vue3 兼容性最好的版本。
2. 准备语言包
在 src/locale/lang/ 目录下创建两个语言文件。
中文语言包 zh-CN.js:
javascript
export default {
common: {
welcome: '欢迎使用本应用',
switchLang: '切换语言',
confirm: '确定',
cancel: '取消',
tip: '提示',
success: '操作成功'
},
tabbar: {
home: '首页',
profile: '我的'
}
}
英文语言包 en-US.js:
javascript
export default {
common: {
welcome: 'Welcome to our App',
switchLang: 'Switch Language',
confirm: 'Confirm',
cancel: 'Cancel',
tip: 'Tip',
success: 'Success'
},
tabbar: {
home: 'Home',
profile: 'Profile'
}
}
3. 创建 i18n 实例
在 src/locale/index.js 中配置 vue-i18n:
javascript
import { createI18n } from 'vue-i18n'
import zhCN from './lang/zh-CN'
import enUS from './lang/en-US'
// 获取初始语言(优先读取本地存储,否则使用系统语言)
function getInitialLocale() {
const saved = uni.getStorageSync('app_lang')
if (saved === 'zh-CN' || saved === 'en-US') return saved
const systemLang = uni.getLocale()
return systemLang.startsWith('en') ? 'en-US' : 'zh-CN'
}
const i18n = createI18n({
legacy: false, // 使用 Composition API 模式
locale: getInitialLocale(),
fallbackLocale: 'zh-CN',
messages: { 'zh-CN': zhCN, 'en-US': enUS },
globalInjection: true // 让模板中可以直接使用 $t
})
export default i18n
4. 创建 JS 文件翻译辅助函数
由于 JS 文件(如工具函数、请求拦截器)中无法直接使用 $t,我们需要导出一个辅助函数。
src/locale/helper.js:
javascript
import i18n from './index.js'
export function $t(key, options) {
return i18n.global.t(key, options)
}
5. 创建 Store 管理语言状态(可选,但推荐)
用 Pinia 来管理当前语言和切换逻辑,保持代码清晰。
src/store/locale.js:
javascript
import { defineStore } from 'pinia'
import { ref } from 'vue'
import i18n from '@/locale/index.js'
export const useLocaleStore = defineStore('locale', () => {
const currentLang = ref(i18n.global.locale.value)
function setLanguage(lang) {
if (lang !== 'zh-CN' && lang !== 'en-US') return
// 1. 更新 i18n 实例的语言(这会触发模板重新渲染)
i18n.global.locale.value = lang
// 2. 更新 store 状态
currentLang.value = lang
// 3. 持久化存储
uni.setStorageSync('app_lang', lang)
// 4. 手动更新原生组件(TabBar 等)
updateNativeComponents(lang)
// 5. 触发全局事件(供其他非 Vue 环境监听)
uni.$emit('language-changed', lang)
}
function updateNativeComponents(lang) {
const tabBarText = {
'zh-CN': { home: '首页', profile: '我的' },
'en-US': { home: 'Home', profile: 'Profile' }
}
// 假设你的 tabBar 有两个选项,索引从 0 开始
uni.setTabBarItem({ index: 0, text: tabBarText[lang].home })
uni.setTabBarItem({ index: 1, text: tabBarText[lang].profile })
}
return { currentLang, setLanguage }
})
6. 在 main.js 中注册
javascript
import { createSSRApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import i18n from './locale'
export function createApp() {
const app = createSSRApp(App)
const pinia = createPinia()
app.use(pinia)
app.use(i18n) // 注册 i18n,让模板中可以使用 $t
return { app }
}
7. 在页面中使用
pages/index/index.vue:
html
<template>
<view>
<view class="welcome">{{ $t('common.welcome') }}</view>
<button @click="handleSwitch">{{ $t('common.switchLang') }}</button>
</view>
</template>
<script setup>
import { useLocaleStore } from '@/store/locale.js'
const localeStore = useLocaleStore()
function handleSwitch() {
const newLang = localeStore.currentLang === 'zh-CN' ? 'en-US' : 'zh-CN'
localeStore.setLanguage(newLang)
}
</script>
8. 在 JS 文件中使用翻译
javascript
// utils/request.js
import { $t } from '@/locale/helper.js'
uni.showToast({
title: $t('common.success'),
icon: 'success'
})
9. 处理原生导航栏标题
在每个页面的 onShow 中动态设置标题:
javascript
<script setup>
import { onShow } from '@dcloudio/uni-app'
import { $t } from '@/locale/helper.js'
onShow(() => {
uni.setNavigationBarTitle({
title: $t('tabbar.home')
})
})
</script>
10. 在 App.vue 中初始化 TabBar
javascript
<script setup>
import { onLaunch } from '@dcloudio/uni-app'
onLaunch(() => {
const savedLang = uni.getStorageSync('app_lang') || 'zh-CN'
const tabBarText = {
'zh-CN': { home: '首页', profile: '我的' },
'en-US': { home: 'Home', profile: 'Profile' }
}
uni.setTabBarItem({ index: 0, text: tabBarText[savedLang].home })
uni.setTabBarItem({ index: 1, text: tabBarText[savedLang].profile })
})
</script>
四、方案总结与对比
| 对比项 | uni.setLocale |
vue-i18n (本方案) |
|---|---|---|
| Android 是否重启 | ✅ 会重启 | ❌ 不重启 |
| iOS 是否重启 | ❌ 不重启 | ❌ 不重启 |
| 原生组件更新 | 自动更新 | 需手动更新 (TabBar等) |
| 实现复杂度 | 简单 | 中等 |
| 用户体验 | 差(重启) | 好(流畅) |
五、常见问题 FAQ
Q1:切换语言后,TabBar 文字没变?
A :因为 TabBar 是原生组件,vue-i18n 无法自动更新它。需要在 setLanguage 方法中手动调用 uni.setTabBarItem 更新文字(已在上面代码中实现)。
Q2:JS 文件中如何获取当前语言?
A :导入 i18n 实例,读取 i18n.global.locale.value;或者从 Store 中读取 currentLang。
Q3:后端返回的多语言数据如何处理?
A :建议后端返回类似 { name: { "zh-CN": "苹果", "en-US": "Apple" } } 的结构,前端根据当前语言动态取值。语言切换后,重新调用接口获取数据或在前端用 computed 转换。
Q4:vue-i18n 版本必须用 9.1.9 吗?
A:推荐使用这个版本,因为它是经过社区验证、与 uni-app 兼容性最好的版本。使用更高版本可能会出现未知问题。
六、结语
通过本文的方案,你可以在 uni-app Vue3 项目中实现无重启、流畅切换 的多语言功能。核心就是放弃 uni.setLocale,拥抱 vue-i18n,并对原生组件做额外处理。
希望这篇文章能帮你避开坑点,让你的 App 国际化体验更上一层楼!
如果你在使用过程中遇到任何问题,欢迎在评论区留言交流。