Skip to content

国际化(i18n)

23:52

概述

项目集成了 vue-i18n 库,提供了完整的多语言国际化解决方案。支持在运行时动态切换语言,所有的文本内容都能实时更新。

项目特性

  • 🌍 多语言支持 - 支持中文、英文等多语言
  • 🔄 动态切换 - 运行时即时切换语言无需重新加载
  • 📦 JSON 配置 - 使用 JSON 文件管理语言包
  • 💬 组件级别 - 支持在组件中使用翻译
  • 🎯 类型安全 - TypeScript 支持完整的类型检查

初始化配置

vue-i18n 配置

src/locale/index.ts 中:

typescript
import { createI18n } from 'vue-i18n'

import enUS from './lang/en-US.json'          // 英文
import zhCN from './lang/zh-CN.json' // 简体中文

const messages = {
  'en': enUS,
  'zh-Hans': zhCN,
}

// 安全获取 locale,避免在 SSR 环境下出现问题
function getSafeLocale() {
  try {
    return uni.getLocale() || 'zh-Hans'
  }
  catch {
    return 'zh-Hans'
  }
}

const i18n = createI18n({
  locale: getSafeLocale(),           // 初始语言
  fallbackLocale: 'zh-Hans',         // 回退语言
  messages,                          // 语言包
  allowComposition: true,            // 允许在 Composition API 中使用
  legacy: false,                     // 使用 Composition API 模式
  globalInjection: true,             // 全局注入
})

export default i18n

在 main.ts 中初始化

typescript
import i18n from '@/locale'
import uViewPro from 'uview-pro'

const app = createSSRApp(App)
// 引入 i18n
app.use(i18n)
// 引入 uView Pro 组件并初始化中文语言
app.use(uViewPro, {
  locale: 'zh-CN'
})

语言包结构

简体中文语言包

创建 src/locale/lang/zh-CN.json

json
{
  "common": {
    "appName": "uView Pro 启动项目",
    "home": "首页",
    "about": "关于",
    "settings": "设置",
    "logout": "退出登录",
    "confirm": "确定",
    "cancel": "取消",
    "loading": "加载中...",
    "noData": "暂无数据"
  },
  "home": {
    "welcome": "欢迎使用 uView Pro",
    "description": "这是一个快速启动项目"
  },
  "about": {
    "title": "关于我们",
    "version": "版本"
  }
}

英文语言包

创建 src/locale/lang/en-US.json

json
{
  "common": {
    "appName": "uView Pro Starter",
    "home": "Home",
    "about": "About",
    "settings": "Settings",
    "logout": "Logout",
    "confirm": "OK",
    "cancel": "Cancel",
    "loading": "Loading...",
    "noData": "No Data"
  },
  "home": {
    "welcome": "Welcome to uView Pro",
    "description": "This is a quick start project"
  },
  "about": {
    "title": "About Us",
    "version": "Version"
  }
}

基本使用

在模板中使用

vue
<template>
  <view class="container">
    <!-- 使用 $t 翻译文本 -->
    <text>{{ $t('common.appName') }}</text>
    <text>{{ $t('home.welcome') }}</text>
  </view>
</template>
<script setup lang="ts">
import { useI18n } from 'vue-i18n'

const { t } = useI18n()
</script>

在 Script 中使用

vue
<script setup lang="ts">
import { useI18n } from 'vue-i18n'

const { t } = useI18n()

const appName = t('common.appName')
const message = t('home.welcome')

const getMessage = () => {
  return t('common.loading')
}
</script>

带参数的翻译

语言包配置

json
{
  "messages": {
    "hello": "你好,{name}",
    "count": "你有 {count} 条消息"
  }
}

使用翻译

vue
<script setup lang="ts">
import { useI18n } from 'vue-i18n'

const { t } = useI18n()

// 简单参数
const greeting = t('messages.hello', { name: '张三' })
// 输出: 你好,张三

// 复数形式
const message = t('messages.count', { count: 5 })
// 输出: 你有 5 条消息
</script>

<template>
  <text>{{ greeting }}</text>
  <text>{{ message }}</text>
</template>

高级用法

动态切换语言

创建 src/hooks/useLang.ts

typescript
import { useLocale } from 'uview-pro'
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'

export function useLang() {
  const { locale } = useI18n()
  const { setLocale, currentLocale, locales } = useLocale()

  // 派生当前语言(基于 uView Pro 的 currentLocale),确保模板自动响应更新
  const currentLang = computed(() => {
    return currentLocale.value?.name || currentLocale.value || ''
  })
  // 当前语言标签
  const currentLangLabel = computed(() => {
    return getLangLabel(currentLang.value)
  })

  // 可用语言列表
  const availableLangs = computed(() => {
    return locales.value.map(locale => ({
      name: locale.name,
      label: getLangLabel(locale.name),
    }))
  })
  // 辅助函数:根据不同的语言代码格式,返回i18n使用的语言代码
  function getI18nLocale(value: string) {
    switch (value) {
      case 'zh-CN':
      case 'zh-Hans':
        return 'zh-Hans'
      case 'en-US':
      case 'en':
        return 'en'
      default:
        return 'zh-Hans'
    }
  }

  // 辅助函数:根据不同的语言代码格式,返回uView Pro使用的语言代码
  function getUProLocale(value: string) {
    switch (value) {
      case 'zh-Hans':
      case 'zh-CN':
        return 'zh-CN'
      case 'en':
      case 'en-US':
        return 'en-US'
      default:
        return 'zh-CN'
    }
  }

  // 根据语言代码返回对应的语言文字标签
  function getLangLabel(localeName: string) {
    const normalized = getUProLocale(localeName)
    if (normalized === 'zh-CN')
      return '简体中文'
    if (normalized === 'en-US')
      return 'English'
    return normalized
  }

  // 切换语言
  function switchLang(lang: string) {
    const i18nLocale = getI18nLocale(lang)
    const uProLocale = getUProLocale(lang)
    // 切换uniapp语言
    uni.setLocale(i18nLocale)
    // 切换vue-i18n语言
    locale.value = i18nLocale
    // 切换uView Pro语言
    setLocale(uProLocale)
  }

  return {
    currentLang,
    currentLangLabel,
    availableLangs,
    getLangLabel,
    switchLang,
  }
}

在组件中使用:

vue
<script setup lang="ts">
import { useLang } from '@/composables/useLocale'

const { switchLang, currentLangLabel, currentLang, availableLangs } = useLang()

const handleLocaleChange = (locale: string) => {
  switchLang(locale)
}
</script>

<template>
  <view class="locale-selector">
    <text>当前语言:{{ currentLocale }}</text>
    <view v-for="loc in availableLocales" :key="loc.name">
      <u-button
        @click="handleLocaleChange(loc.code)"
        :type="currentLang === loc.name ? 'primary' : 'default'"
      >
        {{ loc.label }}
      </u-button>
    </view>
  </view>
</template>

在 Store 中使用国际化

typescript
import { useI18n } from 'vue-i18n'
import { defineStore } from 'pinia'

export const useAppStore = defineStore('app', () => {
  const { t } = useI18n()

  function showNotification() {
    // 使用翻译的文本
    const message = t('common.noData')
    console.log(message)
  }

  return { showNotification }
})

常见场景

条件渲染

vue
<script setup lang="ts">
import { useLang } from '@/composables/useLang'

const { currentLang } = useLang()
</script>

<template>
  <view v-if="currentLang === 'zh-CN'" class="zh-only">
    仅中文显示
  </view>
  <view v-else class="en-only">
    English only
  </view>
</template>

复数形式

语言包配置:

json
{
  "items": "你有 {count} 个项目 | 你有 {count} 个项目"
}

使用:

typescript
// 当 count = 1 时,使用第一个,count > 1 时,使用第二个
const text = t('items', { count: 5 })

嵌套翻译

json
{
  "user": {
    "profile": {
      "name": "姓名",
      "email": "邮箱"
    }
  }
}

使用:

vue
<template>
  <text>{{ $t('user.profile.name') }}</text>
</template>

动态翻译

typescript
const keys = ['home.welcome', 'home.description', 'about.title']
const translations = keys.map(key => t(key))

从 uni.getLocale() 获取系统语言

typescript
// 获取系统语言
const systemLocale = uni.getLocale()

// 映射到支持的语言
const localeMap: Record<string, string> = {
  'zh-Hans': 'zh-CN',  // 简体中文
  'zh-Hant': 'zh-CN',  // 繁体中文 → 简体中文
  'en': 'en-US',
  'en-US': 'en-US',
}

const mappedLocale = localeMap[systemLocale] || 'zh-CN'

组织大型项目的语言包

对于大型项目,建议分文件组织:

src/locale/
├── index.ts
└── lang/
    ├── zh-CN/
    │   ├── index.ts
    │   ├── common.json
    │   ├── home.json
    │   ├── about.json
    │   └── user.json
    └── en-US/
        ├── index.ts
        ├── common.json
        ├── home.json
        ├── about.json
        └── user.json

src/locale/lang/zh-CN/index.ts

typescript
import common from './common.json'
import home from './home.json'
import about from './about.json'
import user from './user.json'

export default {
  common,
  home,
  about,
  user,
}

最佳实践

  1. 统一的 key 命名规范 - 使用 module.feature.name 的命名方式
  2. 避免硬编码 - 所有可见文本都应该使用翻译
  3. 为默认语言优化 - 确保默认语言的翻译质量最高
  4. 使用回退语言 - 配置回退语言以处理缺失的翻译
  5. 注意文本长度 - 不同语言的文本长度可能差异很大,要在 UI 设计时考虑
  6. 测试多语言 - 在所有支持的语言环境下进行测试
  7. 定期审查 - 定期检查和更新翻译内容

故障排除

翻译不显示

  1. 检查语言包文件是否存在
  2. 检查 key 是否拼写正确
  3. 确保 i18n 已正确初始化
  4. 在浏览器控制台检查是否有错误信息

语言不切换

typescript
// 确保使用正确的方式修改 locale
import { useI18n } from 'vue-i18n'

const { locale } = useI18n()

// 正确
locale.value = 'en'

// 错误
locale = 'en'

相关文档