uni-app 国际化

章节介绍

在全球化背景下,开发支持多语言的应用变得越来越重要。uni-app 提供了多种方式来实现国际化,使应用能够轻松适应不同语言和地区的用户需求。本章节将详细介绍 uni-app 中的国际化实现方法,包括语言包的创建、多语言切换、动态语言更新等功能。

核心知识点

1. 国际化基础概念

  • 国际化(i18n):使应用能够适应不同语言和地区的需求
  • 本地化(l10n):将应用适配到特定语言和地区
  • 语言包:存储不同语言翻译的文件
  • 语言标签:标识语言和地区的代码,如 zh-CN、en-US 等

2. uni-app 国际化方案

uni-app 支持多种国际化实现方案:

  • 使用 uni-app 官方推荐的 vue-i18n 插件
  • 使用自定义国际化工具
  • 利用条件编译实现多语言

3. vue-i18n 插件使用

  • 安装和配置 vue-i18n
  • 创建语言包文件
  • 在组件中使用国际化
  • 动态切换语言

4. 语言包管理

  • 语言包的组织方式
  • 翻译键的命名规范
  • 语言包的加载和缓存
  • 动态更新语言包

5. 国际化最佳实践

  • 日期、时间的国际化
  • 数字、货币的国际化
  • 文本方向的处理(从右到左的语言)
  • 占位符和复数形式的处理

实用案例分析

案例一:基本国际化实现

场景:创建一个支持中英文切换的应用。

实现步骤

  1. 安装 vue-i18n 插件
  2. 创建语言包文件
  3. 配置国际化实例
  4. 在组件中使用国际化
  5. 实现语言切换功能

代码示例

1. 安装 vue-i18n 插件

# 使用 npm 安装
npm install vue-i18n@8

# 或使用 yarn 安装
yarn add vue-i18n@8

2. 创建语言包文件

创建 src/lang/zh-CN.js

export default {
  common: {
    confirm: '确认',
    cancel: '取消',
    submit: '提交',
    reset: '重置'
  },
  login: {
    title: '用户登录',
    username: '用户名',
    password: '密码',
    login: '登录',
    forgetPassword: '忘记密码',
    register: '注册账号'
  },
  home: {
    welcome: '欢迎使用 uni-app',
    features: '核心功能',
    crossPlatform: '跨平台开发',
    performance: '高性能',
    ecosystem: '丰富的生态'
  }
}

创建 src/lang/en-US.js

export default {
  common: {
    confirm: 'Confirm',
    cancel: 'Cancel',
    submit: 'Submit',
    reset: 'Reset'
  },
  login: {
    title: 'User Login',
    username: 'Username',
    password: 'Password',
    login: 'Login',
    forgetPassword: 'Forgot Password',
    register: 'Register Account'
  },
  home: {
    welcome: 'Welcome to uni-app',
    features: 'Core Features',
    crossPlatform: 'Cross-platform Development',
    performance: 'High Performance',
    ecosystem: 'Rich Ecosystem'
  }
}

3. 配置国际化实例

创建 src/lang/index.js

import Vue from 'vue'
import VueI18n from 'vue-i18n'
import zhCN from './zh-CN'
import enUS from './en-US'

Vue.use(VueI18n)

// 从本地存储获取语言设置,默认使用中文
const lang = uni.getStorageSync('lang') || 'zh-CN'

const i18n = new VueI18n({
  locale: lang,
  messages: {
    'zh-CN': zhCN,
    'en-US': enUS
  }
})

export default i18n

main.js 中引入:

import Vue from 'vue'
import App from './App.vue'
import i18n from './lang'

Vue.config.productionTip = false

new Vue({
  i18n,
  render: h => h(App)
}).$mount('#app')

4. 在组件中使用国际化

在页面或组件中使用 $t() 方法:

<template>
  <view class="container">
    <view class="header">
      <text class="title">{{ $t('home.welcome') }}</text>
    </view>
    <view class="content">
      <text class="subtitle">{{ $t('home.features') }}</text>
      <view class="feature-list">
        <view class="feature-item">
          <text>{{ $t('home.crossPlatform') }}</text>
        </view>
        <view class="feature-item">
          <text>{{ $t('home.performance') }}</text>
        </view>
        <view class="feature-item">
          <text>{{ $t('home.ecosystem') }}</text>
        </view>
      </view>
    </view>
    <view class="footer">
      <button @click="switchLanguage('zh-CN')">中文</button>
      <button @click="switchLanguage('en-US')">English</button>
    </view>
  </view>
</template>

<script>
export default {
  methods: {
    switchLanguage(lang) {
      this.$i18n.locale = lang
      uni.setStorageSync('lang', lang)
      uni.showToast({
        title: this.$t('common.confirm'),
        icon: 'success'
      })
    }
  }
}
</script>

<style>
.container {
  padding: 20rpx;
}

.header {
  margin-bottom: 40rpx;
}

.title {
  font-size: 36rpx;
  font-weight: bold;
  color: #333;
}

.content {
  margin-bottom: 40rpx;
}

.subtitle {
  font-size: 28rpx;
  font-weight: bold;
  color: #666;
  margin-bottom: 20rpx;
}

.feature-list {
  margin-top: 20rpx;
}

.feature-item {
  padding: 20rpx;
  background-color: #f5f5f5;
  margin-bottom: 10rpx;
  border-radius: 8rpx;
}

.footer {
  display: flex;
  justify-content: space-around;
  margin-top: 40rpx;
}

button {
  padding: 20rpx 40rpx;
  border-radius: 8rpx;
}
</style>

案例二:动态加载语言包

场景:从服务器获取语言包,实现动态更新。

实现步骤

  1. 创建语言包加载函数
  2. 从服务器获取语言包
  3. 动态更新 i18n 实例
  4. 处理加载状态和错误

代码示例

// src/lang/index.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import zhCN from './zh-CN'
import enUS from './en-US'

Vue.use(VueI18n)

const lang = uni.getStorageSync('lang') || 'zh-CN'

const i18n = new VueI18n({
  locale: lang,
  messages: {
    'zh-CN': zhCN,
    'en-US': enUS
  }
})

// 动态加载语言包
export function loadLanguagePack(lang) {
  return new Promise((resolve, reject) => {
    // 先检查本地是否已有该语言包
    if (i18n.messages[lang]) {
      i18n.locale = lang
      uni.setStorageSync('lang', lang)
      resolve()
      return
    }
    
    // 从服务器获取语言包
    uni.request({
      url: `https://api.example.com/lang/${lang}`,
      method: 'GET',
      success: (res) => {
        if (res.statusCode === 200 && res.data) {
          // 更新语言包
          i18n.setLocaleMessage(lang, res.data)
          i18n.locale = lang
          uni.setStorageSync('lang', lang)
          resolve()
        } else {
          reject(new Error('Failed to load language pack'))
        }
      },
      fail: (err) => {
        reject(err)
      }
    })
  })
}

export default i18n

在组件中使用:

<template>
  <view class="container">
    <view v-if="loading">加载语言包中...</view>
    <view v-else>
      <text>{{ $t('home.welcome') }}</text>
      <button @click="loadLanguage('zh-CN')">加载中文语言包</button>
      <button @click="loadLanguage('en-US')">加载英文语言包</button>
    </view>
  </view>
</template>

<script>
import { loadLanguagePack } from '@/lang'

export default {
  data() {
    return {
      loading: false
    }
  },
  methods: {
    async loadLanguage(lang) {
      this.loading = true
      try {
        await loadLanguagePack(lang)
        uni.showToast({
          title: this.$t('common.confirm'),
          icon: 'success'
        })
      } catch (error) {
        uni.showToast({
          title: '加载语言包失败',
          icon: 'none'
        })
      } finally {
        this.loading = false
      }
    }
  }
}
</script>

案例三:日期和数字的国际化

场景:根据不同语言和地区格式化日期、时间和数字。

实现步骤

  1. 使用 JavaScript 的 Intl 对象
  2. 创建日期和数字格式化工具
  3. 在组件中使用格式化方法

代码示例

创建 src/utils/format.js

// 日期格式化
export function formatDate(date, lang = 'zh-CN') {
  return new Intl.DateTimeFormat(lang, {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
    weekday: 'long'
  }).format(date)
}

// 时间格式化
export function formatTime(date, lang = 'zh-CN') {
  return new Intl.DateTimeFormat(lang, {
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit'
  }).format(date)
}

// 数字格式化
export function formatNumber(num, lang = 'zh-CN') {
  return new Intl.NumberFormat(lang).format(num)
}

// 货币格式化
export function formatCurrency(amount, currency = 'CNY', lang = 'zh-CN') {
  return new Intl.NumberFormat(lang, {
    style: 'currency',
    currency: currency
  }).format(amount)
}

在组件中使用:

<template>
  <view class="container">
    <view class="format-item">
      <text class="label">日期:</text>
      <text class="value">{{ formattedDate }}</text>
    </view>
    <view class="format-item">
      <text class="label">时间:</text>
      <text class="value">{{ formattedTime }}</text>
    </view>
    <view class="format-item">
      <text class="label">数字:</text>
      <text class="value">{{ formattedNumber }}</text>
    </view>
    <view class="format-item">
      <text class="label">货币:</text>
      <text class="value">{{ formattedCurrency }}</text>
    </view>
  </view>
</template>

<script>
import { formatDate, formatTime, formatNumber, formatCurrency } from '@/utils/format'

export default {
  computed: {
    formattedDate() {
      return formatDate(new Date(), this.$i18n.locale)
    },
    formattedTime() {
      return formatTime(new Date(), this.$i18n.locale)
    },
    formattedNumber() {
      return formatNumber(1234567.89, this.$i18n.locale)
    },
    formattedCurrency() {
      const currency = this.$i18n.locale === 'zh-CN' ? 'CNY' : 'USD'
      return formatCurrency(1234.56, currency, this.$i18n.locale)
    }
  }
}
</script>

<style>
.container {
  padding: 20rpx;
}

.format-item {
  display: flex;
  margin-bottom: 20rpx;
}

.label {
  width: 100rpx;
  font-weight: bold;
}

.value {
  flex: 1;
}
</style>

国际化最佳实践

1. 语言包组织

  • 按模块划分:将语言包按功能模块组织,如 login、home、user 等
  • 使用命名空间:使用嵌套对象结构,避免翻译键冲突
  • 统一命名规范:使用小写字母和下划线,保持命名一致性

2. 性能优化

  • 懒加载语言包:只加载当前需要的语言包
  • 缓存语言包:将语言包缓存到本地存储
  • 避免频繁切换:减少不必要的语言切换操作

3. 用户体验

  • 自动检测语言:根据设备语言设置自动选择应用语言
  • 提供语言切换选项:在应用设置中提供语言切换功能
  • 保存语言偏好:将用户选择的语言保存到本地存储

4. 特殊场景处理

  • 文本方向:对于从右到左的语言(如阿拉伯语),需要调整布局方向
  • 日期格式:不同地区的日期格式可能不同,需要使用国际化的日期格式化
  • 货币符号:不同国家的货币符号位置可能不同,需要使用国际化的货币格式化

5. 开发工具

  • 使用国际化插件:如 VS Code 的 i18n Ally 插件,提高开发效率
  • 自动提取翻译键:使用工具自动从代码中提取需要翻译的文本
  • 翻译管理系统:使用专业的翻译管理系统管理多语言内容

总结回顾

本章节介绍了 uni-app 中的国际化实现方法,包括:

  1. 国际化基础概念:了解国际化和本地化的区别,以及语言包的基本概念
  2. vue-i18n 插件使用:安装和配置 vue-i18n,创建语言包文件,在组件中使用国际化
  3. 动态语言切换:实现语言的动态切换和保存用户语言偏好
  4. 动态加载语言包:从服务器获取语言包,实现动态更新
  5. 日期和数字格式化:使用 Intl 对象格式化日期、时间和数字
  6. 国际化最佳实践:语言包组织、性能优化、用户体验和特殊场景处理

通过本章节的学习,您应该能够:

  • 在 uni-app 中实现基本的国际化功能
  • 创建和管理多语言语言包
  • 实现动态语言切换和保存
  • 处理日期、时间和数字的国际化
  • 遵循国际化开发的最佳实践

国际化是现代应用开发的重要组成部分,通过合理的国际化实现,可以大大提升应用的用户体验和全球竞争力。在实际开发中,应根据应用的具体需求选择合适的国际化方案,并不断优化和完善国际化实现。

« 上一篇 uni-app 数据缓存策略 下一篇 » uni-app 无障碍访问