uni-app 国际化多语言实战:vue-i18n 集成与动态切换完全指南

2026-06-10 0 575

随着应用出海和多元化用户需求增长,国际化(i18n)已成为现代移动应用的标配。uni-app 作为跨平台框架,天然需要一套能同时运行在 H5、小程序和 App 上的多语言方案。本文将手把手带你集成 vue-i18n,通过一个完整的商城应用案例,从语言包设计、动态切换、状态持久化到各平台兼容处理,构建可维护的国际化体系。

uni-app 国际化方案选型分析

在 uni-app 中实现国际化,有几种可选路径:

  • 纯手工方案:在组件中通过计算属性根据当前语言返回不同文本。适合非常小的项目,但维护成本高,无法复用。
  • uni-app 内置的 uni.getLocale()可以获取系统语言,但没有提供翻译函数和模板语法支持,需自行封装。
  • vue-i18n:Vue 生态中最成熟的国际化库,提供 $t() 翻译函数、指令、复数处理、日期格式化等完整功能,与 Vue 3 组合式 API 完美配合。

本文将采用 vue-i18n v9+(适配 Vue 3),并针对 uni-app 的多平台特性进行适配封装。这个方案已在 H5、微信小程序和 App 端验证通过。

vue-i18n 安装与初始化配置

首先安装 vue-i18n(确保使用 v9 及以上版本以支持 Vue 3):

                
npm install vue-i18n@9
                
            

在项目根目录创建 locale/ 文件夹,结构如下:

                
locale/
    index.js          # 主入口,创建 i18n 实例
    messages/
        zh-CN.js      # 中文语言包
        en.js         # 英文语言包
    utils.js          # 工具函数(获取系统语言、持久化等)
                
            

先编写中文和英文语言包。以商城场景为例,locale/messages/zh-CN.js

                
export default {
    app: {
        name: '优选商城',
        slogan: '精选好物,品质生活'
    },
    tabbar: {
        home: '首页',
        category: '分类',
        cart: '购物车',
        profile: '我的'
    },
    product: {
        addToCart: '加入购物车',
        buyNow: '立即购买',
        price: '价格',
        stock: '库存'
    },
    user: {
        login: '登录',
        register: '注册',
        settings: '设置',
        language: '语言'
    },
    common: {
        confirm: '确定',
        cancel: '取消',
        loading: '加载中...'
    }
}
                
            

locale/messages/en.js

                
export default {
    app: {
        name: 'BestMart',
        slogan: 'Quality Goods, Better Life'
    },
    tabbar: {
        home: 'Home',
        category: 'Category',
        cart: 'Cart',
        profile: 'Me'
    },
    product: {
        addToCart: 'Add to Cart',
        buyNow: 'Buy Now',
        price: 'Price',
        stock: 'Stock'
    },
    user: {
        login: 'Login',
        register: 'Register',
        settings: 'Settings',
        language: 'Language'
    },
    common: {
        confirm: 'Confirm',
        cancel: 'Cancel',
        loading: 'Loading...'
    }
}
                
            

然后在 locale/index.js 中创建 i18n 实例:

                
// locale/index.js
import { createI18n } from 'vue-i18n'
import zhCN from './messages/zh-CN'
import en from './messages/en'

// 获取初始语言
function getInitialLocale() {
    // 1. 优先读取用户手动选择的语言
    const stored = uni.getStorageSync('app_language')
    if (stored && (stored === 'zh-CN' || stored === 'en')) {
        return stored
    }
    // 2. 其次根据系统语言推断
    const systemLocale = uni.getLocale()
    if (systemLocale && systemLocale.startsWith('en')) {
        return 'en'
    }
    // 3. 默认中文
    return 'zh-CN'
}

const i18n = createI18n({
    legacy: false,          // 使用组合式 API 模式
    globalInjection: true,  // 全局注入 $t
    locale: getInitialLocale(),
    fallbackLocale: 'zh-CN',
    messages: {
        'zh-CN': zhCN,
        'en': en
    }
})

export default i18n
                
            

注意 legacy: false 是关键配置,它让 vue-i18n 工作在 Vue 3 的组合式 API 模式下,支持 useI18n() 钩子。

最后在 main.js 中注册:

                
// main.js
import App from './App.vue'
import { createSSRApp } from 'vue'
import i18n from './locale/index'

export function createApp() {
    const app = createSSRApp(App)
    app.use(i18n)
    return { app }
}
                
            

语言包模块化设计与热加载

当应用规模增长,单一语言文件会变得难以维护。推荐按业务模块拆分语言包,然后在入口文件中合并。修改 locale/messages/zh-CN.js 为聚合文件:

                
// locale/messages/zh-CN.js
import home from './modules/zh-CN/home'
import category from './modules/zh-CN/category'
import cart from './modules/zh-CN/cart'
import user from './modules/zh-CN/user'
import common from './modules/zh-CN/common'

export default {
    ...home,
    ...category,
    ...cart,
    ...user,
    ...common
}
                
            

同理处理英文包。这种模块化结构便于团队协作,每个业务模块独立维护自己的翻译。

对于更大型的项目,还可以实现按需加载语言包。例如,当用户切换语言时才异步加载对应的语言文件,减少初始包体积。以下是简单的异步加载实现:

                
// locale/asyncLoader.js
export async function loadLocaleMessages(locale) {
    switch (locale) {
        case 'zh-CN':
            return (await import('./messages/zh-CN.js')).default
        case 'en':
            return (await import('./messages/en.js')).default
        default:
            return (await import('./messages/zh-CN.js')).default
    }
}

// 在切换语言时调用
async function setLocaleAsync(locale) {
    const messages = await loadLocaleMessages(locale)
    i18n.global.setLocaleMessage(locale, messages)
    i18n.global.locale.value = locale
    uni.setStorageSync('app_language', locale)
}
                
            

这种方式对小程序尤其友好,因为小程序对包体积有严格限制。不过对于大多数中小型应用,直接全量加载语言包也完全没问题。

动态语言切换与全局响应

vue-i18n 的核心优势在于切换语言后,所有使用 $t() 的地方会自动更新,无需手动刷新页面。我们需要一个语言切换的入口页面,并在切换后持久化用户选择。

创建语言切换页面 pages/settings/language.vue

                
<template>
    <view class="language-page">
        <view 
            v-for="lang in languages" 
            :key="lang.code"
            class="language-item"
            @click="changeLanguage(lang.code)"
        >
            <text>{{ lang.nativeName }}</text>
            <text class="lang-sub">{{ lang.name }}</text>
            <checkbox :checked="currentLocale === lang.code" />
        </view>
    </view>
</template>

<script setup>
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'

const { locale } = useI18n()
const currentLocale = ref(locale.value)

const languages = [
    { code: 'zh-CN', name: 'Chinese', nativeName: '中文' },
    { code: 'en', name: 'English', nativeName: 'English' }
]

function changeLanguage(code) {
    locale.value = code
    currentLocale.value = code
    uni.setStorageSync('app_language', code)
    uni.showToast({
        title: code === 'zh-CN' ? '已切换为中文' : 'Switched to English',
        icon: 'success'
    })
}
</script>
                
            

切换完成后,所有页面的翻译文本会立即更新。这是因为 locale.value 是响应式的,vue-i18n 内部会重新计算所有 $t() 调用的返回值。

在页面的 navigationBarTitleText 等配置中也希望使用多语言,可以在 pages.json 中使用运行时标题设置:

                
// 在页面 onShow 或 setup 中
import { useI18n } from 'vue-i18n'

const { t } = useI18n()
uni.setNavigationBarTitle({
    title: t('tabbar.home')
})
                
            

语言偏好持久化与初始化加载

我们已经在前面的 getInitialLocale() 函数中处理了启动时的语言恢复。但还有一个细节:当用户首次打开应用时,如何优雅地跟随系统语言?

扩展 locale/utils.js

                
// locale/utils.js
export function getSystemLocale() {
    const locale = uni.getLocale()
    // uni.getLocale() 在不同平台返回值可能不同
    // 小程序返回 'zh_CN' 格式,需转换
    if (locale) {
        const normalized = locale.replace('_', '-')
        if (normalized.startsWith('zh')) return 'zh-CN'
        if (normalized.startsWith('en')) return 'en'
    }
    return 'zh-CN'
}

export function setUserLanguage(locale) {
    uni.setStorageSync('app_language', locale)
}

export function getUserLanguage() {
    return uni.getStorageSync('app_language') || null
}
                
            

另外,uni-app 的 tabBarnavigationBar 中的文本也需要国际化。由于这些配置在 pages.json 中是静态的,推荐做法是使用 自定义导航栏自定义 tabBar,这样就能在组件中使用 $t() 了。

如果必须使用原生导航栏,可以在 App.vueonLaunch 中动态设置 tabBar 文本:

                
// App.vue
import i18n from '@/locale/index'

onLaunch(() => {
    const t = i18n.global.t
    uni.setTabBarItem({
        index: 0,
        text: t('tabbar.home')
    })
    uni.setTabBarItem({
        index: 1,
        text: t('tabbar.category')
    })
    // ... 其他 tab
})
                
            

模板与脚本中的多语言使用技巧

vue-i18n 提供了多种使用方式:

模板中使用 $t()

                
<template>
    <view>
        <text>{{ $t('app.name') }}</text>
        <text>{{ $t('product.price') }}: ¥99.00</text>
    </view>
</template>
                
            

脚本中使用 useI18n()

                
<script setup>
import { useI18n } from 'vue-i18n'

const { t, locale } = useI18n()

function showConfirm() {
    uni.showModal({
        title: t('common.confirm'),
        content: '确定要删除该商品吗?',
        confirmText: t('common.confirm'),
        cancelText: t('common.cancel')
    })
}
</script>
                
            

带参数的翻译(插值)

语言包中可以使用 {name} 占位符:

                
// 语言包
export default {
    product: {
        stockRemaining: '剩余 {count} 件'
    }
}

// 使用
<text>{{ $t('product.stockRemaining', { count: 5 }) }}</text>
// 输出: 剩余 5 件
                
            

复数处理

vue-i18n 支持复数规则,但中文没有复数变化,英文可以使用管道符:

                
// 英语语言包
messages: {
    cart: {
        itemCount: 'no items | one item | {count} items'
    }
}

// 使用
$t('cart.itemCount', { count: 0 })   // "no items"
$t('cart.itemCount', { count: 1 })   // "one item"
$t('cart.itemCount', { count: 5 })   // "5 items"
                
            

小程序端兼容处理与常见坑点

微信小程序在支持 vue-i18n 时有一些特殊之处:

  • 存储限制:单个 key 最大 1MB,语言包通常不会超出,但要注意不要在语言包中存储大段 HTML 文本。
  • uni.getLocale() 返回值:小程序返回的是 'zh_CN'(下划线格式),需要转换为 'zh-CN'(横线格式)才能与 vue-i18n 的语言代码匹配。
  • 异步加载:小程序不支持动态 require,但支持 import() 动态导入,因此按需加载方案可行。
  • this 指向:在选项式 API 中使用 this.$t() 正常;在 setup 中要使用 useI18n()

另外,支付宝和百度小程序对 ES Module 的支持略有差异。如果你的项目需要兼容这些平台,建议将语言包文件改为 CommonJS 格式(module.exports),并在 vue.config.js 中配置条件编译。

一个通用适配技巧:在 locale/index.js 中使用条件编译:

                
// #ifdef MP-WEIXIN || MP-BAIDU || MP-ALIPAY
// 小程序平台特殊逻辑
const isMiniProgram = true
// #endif

// #ifdef H5 || APP-PLUS
const isMiniProgram = false
// #endif
                
            

总结

本文通过一个完整的商城国际化案例,从 vue-i18n 安装配置、语言包模块化设计、动态切换、状态持久化到各平台兼容处理,系统讲解了 uni-app 的多语言实现方案。关键要点回顾:

  • 使用 vue-i18n v9 配合 Vue 3 组合式 API 是最优解。
  • 语言包按业务模块拆分,支持按需加载以优化包体积。
  • 通过 useI18n() 钩子在脚本中获取 t()locale,实现动态切换。
  • 利用 uni.getStorageSync 持久化语言偏好,启动时自动恢复。
  • 注意小程序平台的语言代码格式和存储限制。

现在,你可以将这套方案应用到自己的 uni-app 项目中,让应用轻松走向国际化。

uni-app 国际化多语言实战:vue-i18n 集成与动态切换完全指南
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

版权声明:
本站资源有的来自互联网收集整理,本站纯免费分享提供学习使用,如果侵犯了您的合法权益,请联系本站我们会及时删除。
本站资源仅供研究、学习交流之用,免费开源项目不代表完全可商用,若商业用途请先咨询开发企业能否商用,否则产生的一切后果将由下载用户自行承担。
原创板块未经允许不得转载,否则将追究法律责任。

淘吗网 uniapp uni-app 国际化多语言实战:vue-i18n 集成与动态切换完全指南 https://www.taomawang.com/web/uniapp/2121.html

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务