uni-app基于vue-i18n和i18n-ally实现国际化方案

前端 潘老师 2周前 (04-08) 21 ℃ (0) 扫码查看

对于uni-app项目而言,实现国际化能显著拓宽市场,提升用户体验。据统计,全球超过70%的用户更倾向于使用母语浏览内容,支持多语言的应用在国际市场的转化率平均能提升40%。可以说,一个产品若想进军国际市场,获取更多收益,一套完善的国际化方案必不可少。接下来,本文将以基于Vue3和WotUI的uni-app cli框架项目为例,详细介绍如何借助vue-i18n和vscode插件i18n-ally实现国际化功能。

一、安装和配置vue-i18n

(一)安装依赖

首先要在项目中安装vue-i18n,根据你使用的包管理工具不同,安装命令也有所区别:

# 使用npm
npm install vue-i18n@9.1.9

# 或者使用yarn
yarn add vue-i18n@9.1.9

# 或者使用pnpm
pnpm add vue-i18n@9.1.9

需要注意的是,按照uni-app官方文档的建议,Vue3项目要安装vue-i18n的9.1.9版本,这样才能和uni-app内部使用的vue-i18n保持一致,避免出现兼容性问题。

(二)创建i18n实例

在项目里创建一个专门存放国际化相关文件的目录,比如src/locale。在这个目录下新建index.ts文件,内容如下:

// src/locale/index.ts
import { createI18n } from 'vue-i18n'
// 引入中文语言包
import zhCN from './zh-CN.json'
// 引入英文语言包
import enUS from './en-US.json'
// 引入WotUI组件库的语言模块
import Locale from 'wot-design-uni/locale'
import WotEnUS from 'wot-design-uni/locale/lang/en-US'

// 将WotUI的英文语言包添加到语言集合中
Locale.add({ 'en-US': WotEnUS })

// 整合语言包
const messages = {
  'zh-CN': {
   ...zhCN
  },
  'en-US': {
   ...enUS
  }
}

// 创建i18n实例
const i18n = createI18n({
  // 默认语言,优先从本地存储获取,没有则使用中文
  locale: uni.getStorageSync('currentLang') || 'zh-CN', 
  // 回退语言,当当前语言缺失翻译时使用
  fallbackLocale: 'zh-CN',   
  // 配置语言包
  messages,                  
  // 启用Composition API模式
  legacy: false              
})

// 同步组件库语言
Locale.use(i18n.global.locale.value)
// 设置uni-app的语言
uni.setLocale(i18n.global.locale.value)

export default i18n

这段代码的作用是创建一个i18n实例,将不同语言的翻译内容整合起来,并配置默认语言、回退语言等选项。同时,还同步了WotUI组件库的语言设置,确保组件库的语言和应用整体语言一致。

(三)在main.ts中注册i18n

接下来,在src/main.ts文件中注册刚刚创建的i18n实例:

// src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
import i18n from './locale'

const app = createApp(App)
app.use(i18n)
app.mount('#app')

这样,i18n就被集成到整个Vue应用中,后续就可以在项目的各个部分使用国际化功能了。

二、语言文件组织

(一)基本结构

语言文件一般采用JSON格式,每种语言对应一个文件,放在src/locale目录下,形成如下结构:

src/locale/
  ├── index.ts          # i18n配置和实例
  ├── zh-CN.json        # 中文语言包
  └── en-US.json        # 英文语言包

这种结构清晰明了,方便管理和维护不同语言的翻译内容。

(二)语言文件内容

语言文件由键值对组成,键作为唯一标识符,值则是对应语言的文本内容。例如:

// zh-CN.json
{
  "hello": "你好",
  "welcome": "欢迎使用",
  "button": "按钮"
}

// en-US.json
{
  "hello": "Hello",
  "welcome": "Welcome to use",
  "button": "Button"
}

通过这种方式,在代码中使用对应的键就能获取到相应语言的文本。

(三)嵌套结构

对于结构较为复杂的应用,可以采用嵌套结构来组织语言文件,让翻译内容的逻辑更加清晰。比如:

// zh-CN.json
{
  "common": {
    "confirm": "确认",
    "cancel": "取消"
  },
  "home": {
    "title": "首页",
    "welcome": "欢迎回来"
  }
}

在使用时,通过类似$t('common.confirm')的方式来获取“确认”这个翻译文本。

三、多语言切换

在项目中,我们通过创建useI18nSync钩子函数来实现应用和组件库语言设置的同步。

(一)useI18nSync钩子函数实现

useI18nSync钩子函数的代码如下:

// src/hooks/useI18nSync.ts
import { computed, onBeforeMount } from 'vue'
import { Locale } from 'wot-design-uni/locale'
import i18n from '../locale'

// 定义支持的语言列表
const SUPPORTED_LOCALES = [
  'zh-CN',
  'en-US',
]

// 设置语言的函数
function setLocale(locale: string, syncComponentLib: boolean = true) {
  // 检查语言是否在支持列表中,不在则使用默认语言
  if (!SUPPORTED_LOCALES.includes(locale)) {
    console.warn(`不支持的语言: ${locale},将使用默认语言 zh-CN`)
    locale = 'zh-CN'
  }
  // 设置uni-app的语言
  uni.setLocale(locale)
  // 设置i18n实例的语言
  i18n.global.locale.value = locale
  // 将当前语言存储到本地
  uni.setStorageSync('currentLang', locale)
  // 同步组件库语言
  if (syncComponentLib) {
    Locale.use(locale)
  }
  return locale
}

// 初始化语言的函数
function initLocale(defaultLocale: string, syncComponentLib: boolean) {
  // 从本地存储获取语言,没有则使用默认语言
  const storedLocale = uni.getStorageSync('currentLang') || defaultLocale
  setLocale(storedLocale, syncComponentLib)
}

// 定义配置选项接口
interface I18nSyncOptions {
  /** 是否同步组件库语言设置 */
  syncComponentLib?: boolean
  /** 默认语言 */
  defaultLocale?: string
}

/**
 * 国际化同步hook
 * @param options 配置选项
 * @returns 国际化相关方法和状态
 */
export function useI18nSync(options?: I18nSyncOptions) {
  const { syncComponentLib = true, defaultLocale = 'zh-CN' } = options || {}
  // 获取当前语言的计算属性
  const currentLang = computed(() => i18n.global.locale.value)
  // 在组件挂载前初始化语言
  onBeforeMount(() => {
    initLocale(defaultLocale, syncComponentLib)
  })

  return {
    currentLang,
    // 设置语言的方法
    setLocale: (locale: string) => setLocale(locale, syncComponentLib),
    // 支持的语言列表
    supportedLocales: SUPPORTED_LOCALES
  }
}

这个钩子函数实现了语言的设置、初始化以及同步组件库语言的功能。

(二)在App.vue中初始化语言

App.vue中引入并使用useI18nSync钩子函数来初始化语言:

<script setup lang="ts">
import { useI18nSync } from './hooks/useI18nSync'

// 初始化国际化设置
const { currentLang, setLocale } = useI18nSync()
</script>

通过这段代码,在应用启动时就完成了语言的初始化工作。

(三)实现语言切换功能

在模板中添加语言切换按钮,实现语言切换的交互功能:

<template>
  <view class="language-switcher">
    <view class="current-lang">{{ $t('dangQianYuYan') }}: {{ currentLang }}</view>
    <wd-button @click="switchLanguage('zh-CN')">中文</wd-button>
    <wd-button @click="switchLanguage('en-US')">English</wd-button>
  </view>
</template>

<script setup lang="ts">
import { useI18nSync } from '../hooks/useI18nSync'

const { currentLang, setLocale } = useI18nSync()

function switchLanguage(locale: string) {
  setLocale(locale)
}
</script>

当用户点击按钮时,就会调用setLocale方法切换语言。

四、组件国际化

(一)使用Composition API

在Vue3的Composition API中,可以这样使用i18n:

<template>
  <view class="page">
    <view class="title">{{ t('hello') }}</view>
    <view class="content">{{ t('welcome') }}</view>
    <wd-button>{{ t('button') }}</wd-button>
  </view>
</template>

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

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

通过useI18n函数获取t函数,在模板中使用{{ t('xxx') }}的方式来展示国际化文本。

(二)使用模板语法

也可以直接在模板中使用$t函数实现国际化:

<template>
  <view class="page">
    <view class="title">{{ $t('hello') }}</view>
    <view class="content">{{ $t('welcome') }}</view>
    <wd-button>{{ $t('button') }}</wd-button>
  </view>
</template>

这种方式更加简洁直观,适用于简单的组件。

(三)动态计算属性

如果希望某些内容能根据语言变化动态更新,可以使用computed来实现:

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

const { t } = useI18n()

// 使用computed使list响应语言变化
const list = computed(() => [
  {
    id: 'widget',
    name: t('ji-chu'),
    pages: [
      {
        id: 'button',
        name: t('button-an-niu')
      },
      // 其他项...
    ]
  }
])
</script>

这样,当语言切换时,list中的内容也会相应更新。

五、动态内容处理

(一)平台限制与解决方案

根据uni-app官方文档,小程序和App端不支持插值方式定义国际化,像下面这种方式在这两个平台无法正常工作:

// 这种方式在小程序和App端不支持
t('hello', { name: '小明' }) // 使用命名参数
t('hello', ['小明']) // 使用数组参数

为了解决这个问题,需要采用自定义的插值方法来处理带参数的翻译。

(二)带参数的翻译

在小程序和App端,使用特殊的占位符格式和自定义插值方法来处理包含变量的文本。首先在语言文件中定义带占位符的文本:

// zh-CN.json
{
  "greeting": "你好,{0}!",
  "welcome": "欢迎{0}来到{1}"
}

使用时传入参数:

t('greeting', ['小明'])
// 输出:你好,小明!

t('welcome', ['小明', 'wot-design-uni'])
// 输出:欢迎小明来到wot-design-uni

这里使用{0}{1}这样的数字索引占位符,而不是命名参数,因为小程序和App端不支持命名参数的插值方式。

(三)实现插值工具函数

为了实现占位符的替换,创建一个插值工具函数:

// src/locale/utils.ts
/**
 * 替换字符串中的占位符
 * @param template 模板字符串,如 "Hello {0}, welcome to {1}"
 * @param values 要替换的值数组
 * @returns 替换后的字符串
 */
export function interpolateTemplate(template: string, values: any[]): string {
  return template.replace(/{(\d+)}/g, (_, index) => values[index] ?? '')
}

这个函数会根据传入的模板字符串和值数组,将占位符替换为实际的值。

(四)扩展t函数支持数组参数

由于小程序和App端的限制,需要扩展vue-i18n的t函数,使其能够处理数组参数并应用插值方法:

// src/locale/index.ts
import { createI18n } from 'vue-i18n'
import zhCN from './zh-CN.json'
import enUS from './en-US.json'
import { interpolateTemplate } from './utils'

// 创建i18n实例
const i18n = createI18n({
  locale: 'zh-CN',
  fallbackLocale: 'zh-CN',
  messages: {
    'zh-CN': zhCN,
    'en-US': enUS
  },
  legacy: false
})

// 扩展t函数,支持数组参数插值
// 这是解决小程序和App端不支持插值方式的关键步骤
const originalT = i18n.global.t
i18n.global.t = ((key: string | number, param1?: any, param2?: any) => {
  const result = originalT(key, param1, param2)
  // 检测是否传入了数组参数,如果是则使用我们的插值方法处理
  if (Array.isArray(param1)) {
    return interpolateTemplate(result, param1)
  }
  return result
}) as typeof i18n.global.t

export default i18n

通过这种扩展,保持了与vue-i18n原有API的兼容性,同时在小程序和App端也能实现带参数的翻译。

(五)使用示例

在模板和代码中使用扩展后的t函数:

<template>
  <!-- 在模板中使用 -->
  <view>{{ $t('greeting', [username]) }}</view>
  <view>{{ $t('welcome', [username, 'wot-design-uni']) }}</view>
</template>

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

const { t } = useI18n()
const username = ref('小明')

// 在JS/TS代码中使用
const message = t('greeting', [username.value])
</script>

这样,在所有平台上都能正常处理带参数的翻译内容。

六、组件库集成

WotUI组件库本身支持国际化,借助前面提到的useI18nSync钩子函数,就能轻松同步应用和组件库的语言设置:

// 同步组件库语言设置
if (syncComponentLib) {
  Locale.use(locale)
}

通过这行代码,确保了组件库的语言和应用整体语言保持一致。

七、pages.json的国际化

(一)页面标题国际化

pages.json文件中,可以通过占位符实现页面标题的国际化:

// pages.json
{
  "pages": [
    {
      "path": "pages/index/index",
      "style": {
        "navigationBarTitleText": "%index-title%"
      }
    }
  ]
}

(二)语言文件配置

在对应的语言文件中添加翻译内容:

// zh-CN.json
{
  "index-title": "首页"
}

// en-US.json
{
  "index-title": "Home"
}

(三)平台差异处理

H5平台直接支持这种占位符方式;而对于其他平台,比如小程序,可以使用设置tabbar和navigationbar的API来设置文字,或者废弃原生的tabbar和navigationbar,采用自定义方式来实现国际化,具体可参考uni-app官方文档。

八、i18n-ally插件

(一)插件概述

i18n-ally是一款非常实用的VSCode国际化插件,它具备自动检测项目中的硬编码文本、一键提取待翻译内容、多语言文件管理以及翻译辅助工具集成等核心功能,能大大提高国际化开发的效率。

(二)安装与配置

在VS Code扩展商店中搜索“i18n-ally”,点击安装并重启VS Code,即可启用该插件。安装完成后,需对插件进行配置,在项目的.vscode/settings.json文件中,添加如下配置:

{
  "i18n-ally.localesPaths": ["src/locale"],
  "i18n-ally.enabledParsers": ["json"]
}

"i18n-ally.localesPaths"用于指定语言文件所在的目录,这里是src/locale"i18n-ally.enabledParsers"则用于设置要解析的文件后缀,这里只解析json文件。

(三)插件功能详解

  1. 实时预览:安装并配置好插件后,在Vue组件中使用$t('xxx')语法的地方,会自动显示对应语言文件中的实际文本。比如,在App.vue文件中,如果代码写的是{{ $t('hello') }},插件会自动去对应的语言文件(如en-US.jsonzh-CN.json)中查找hello这个键的值,并将其显示在代码旁边,方便开发者直观地查看和理解。当鼠标定位到这一行代码时,又会恢复显示$t('hello'),不影响代码编辑。
  2. 自动检测未翻译文本:i18n-ally能智能检测项目中使用了$t('xxx')语法,但在语言文件中却未找到对应key的情况。这样可以有效避免出现漏翻译的问题,确保项目的国际化内容完整。
  3. 翻译辅助:该插件支持直接在VS Code中进行翻译操作。选中需要翻译的文本,点击插件提供的翻译按钮,它会调用外部翻译服务(如谷歌翻译,需科学上网)对文本进行翻译,并自动生成对应的key名添加到语言文件中。而且,它不仅支持单个词语的翻译,还能对整个文件甚至文件夹进行批量翻译,大大提高了翻译效率。
  4. 翻译进度统计:在插件的界面中,可以清晰地看到每种语言的翻译进度,方便了解项目国际化的完成情况,以便及时跟进和补充未完成的翻译内容。

九、国际化方案优化

(一)性能优化

在大型项目中,语言文件可能会变得非常庞大,影响加载性能。可以采用按需加载的方式,比如使用vue-routerdynamic import功能,在用户切换语言或者访问特定页面时,再加载对应的语言文件,而不是在项目启动时就全部加载。示例代码如下:

// router.js
const router = new VueRouter({
  routes: [
    {
      path: '/home',
      name: 'home',
      component: () => import('@/views/Home.vue'),
      children: [
        {
          path: 'details',
          name: 'details',
          component: () => import('@/views/Details.vue'),
          meta: {
            // 按需加载的语言文件路径
            i18n: () => import('@/locale/home/details.json') 
          }
        }
      ]
    }
  ]
});

在组件中,可以通过beforeEnter守卫来加载对应的语言文件:

// 在路由守卫中按需加载语言文件
router.beforeEach((to, from, next) => {
  if (to.meta.i18n) {
    to.meta.i18n().then((module) => {
      // 将加载的语言文件合并到i18n实例中
      const messages = module.default;
      const currentLocale = i18n.global.locale.value;
      i18n.global.setLocaleMessage(currentLocale, {
      ...i18n.global.getLocaleMessage(currentLocale),
      ...messages
      });
      next();
    });
  } else {
    next();
  }
});

(二)SEO优化

对于面向国际市场的应用,SEO优化至关重要。在HTML页面中,要正确设置语言相关的元标签,比如:

<html lang="en">
<head>
  <meta name="language" content="en">
  <meta http-equiv="Content-Language" content="en">
</head>
<body>
  <!-- 页面内容 -->
</body>
</html>

同时,对于不同语言版本的页面,要设置合适的URL结构,方便搜索引擎抓取和区分。例如,使用/en/home表示英文的首页,/zh/home表示中文的首页。可以通过vue-router的动态路由配置来实现这一点:

const router = new VueRouter({
  mode: 'history',
  routes: [
    {
      path: '/:lang/home',
      name: 'home',
      component: Home,
      children: [
        {
          path: 'details',
          name: 'details',
          component: Details
        }
      ]
    }
  ]
});

(三)测试与维护

  1. 多语言测试:在项目开发过程中,要进行全面的多语言测试。可以使用工具模拟不同语言环境,检查页面布局、文本显示、功能操作等是否正常。例如,使用cypress结合cypress-i18n插件,可以方便地进行多语言的自动化测试。首先安装相关依赖:
npm install cypress cypress-i18n --save-dev

然后在cypress.json文件中进行配置:

{
  "baseUrl": "http://localhost:8080",
  "screenshotsFolder": "cypress/screenshots/i18n",
  "video": false,
  "pluginsFile": "cypress/plugins/index.js",
  "integrationFolder": "cypress/integration/i18n",
  "env": {
    "locales": ["en-US", "zh-CN"],
    "defaultLocale": "en-US"
  }
}

在测试用例中,可以通过切换语言来验证页面的正确性:

describe('Home page i18n test', () => {
  it('should display correct text in English', () => {
    cy.visit('/en/home');
    cy.get('h1').should('contain', 'Welcome');
  });
  it('should display correct text in Chinese', () => {
    cy.visit('/zh-CN/home');
    cy.get('h1').should('contain', '欢迎');
  });
});
  1. 维护与更新:随着项目的迭代,可能会添加新的页面、功能或修改现有文本,这就需要及时更新语言文件。可以定期检查代码中是否有新的未翻译文本,利用i18n-ally插件的自动检测功能能快速定位这些内容。同时,要确保翻译的准确性和一致性,对于重要的文本,可以邀请专业翻译人员进行审核。

十、总结与展望

通过vue-i18n、i18n-ally以及一系列优化策略,我们为uni-app项目构建了一个较为完善的国际化方案。从基础的语言文件配置、多语言切换,到组件国际化、动态内容处理,再到插件集成、性能优化和测试维护,每个环节都紧密相扣,共同提升了应用的国际化能力。

在未来,随着移动应用市场的进一步全球化,国际化需求将变得更加复杂和多样化。一方面,可能需要支持更多的语言和地区,这就要求我们的国际化方案具备更好的扩展性;另一方面,随着人工智能技术的发展,或许可以探索利用AI翻译来提高翻译效率和质量,实现更加智能化的国际化功能。希望本文能为大家在uni-app国际化开发道路上提供参考。


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/front/16966.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】