章
目
录
对于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
文件。
(三)插件功能详解
- 实时预览:安装并配置好插件后,在Vue组件中使用
$t('xxx')
语法的地方,会自动显示对应语言文件中的实际文本。比如,在App.vue
文件中,如果代码写的是{{ $t('hello') }}
,插件会自动去对应的语言文件(如en-US.json
或zh-CN.json
)中查找hello
这个键的值,并将其显示在代码旁边,方便开发者直观地查看和理解。当鼠标定位到这一行代码时,又会恢复显示$t('hello')
,不影响代码编辑。 - 自动检测未翻译文本:i18n-ally能智能检测项目中使用了
$t('xxx')
语法,但在语言文件中却未找到对应key
的情况。这样可以有效避免出现漏翻译的问题,确保项目的国际化内容完整。 - 翻译辅助:该插件支持直接在VS Code中进行翻译操作。选中需要翻译的文本,点击插件提供的翻译按钮,它会调用外部翻译服务(如谷歌翻译,需科学上网)对文本进行翻译,并自动生成对应的
key
名添加到语言文件中。而且,它不仅支持单个词语的翻译,还能对整个文件甚至文件夹进行批量翻译,大大提高了翻译效率。 - 翻译进度统计:在插件的界面中,可以清晰地看到每种语言的翻译进度,方便了解项目国际化的完成情况,以便及时跟进和补充未完成的翻译内容。
九、国际化方案优化
(一)性能优化
在大型项目中,语言文件可能会变得非常庞大,影响加载性能。可以采用按需加载的方式,比如使用vue-router
的dynamic 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
}
]
}
]
});
(三)测试与维护
- 多语言测试:在项目开发过程中,要进行全面的多语言测试。可以使用工具模拟不同语言环境,检查页面布局、文本显示、功能操作等是否正常。例如,使用
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', '欢迎');
});
});
- 维护与更新:随着项目的迭代,可能会添加新的页面、功能或修改现有文本,这就需要及时更新语言文件。可以定期检查代码中是否有新的未翻译文本,利用i18n-ally插件的自动检测功能能快速定位这些内容。同时,要确保翻译的准确性和一致性,对于重要的文本,可以邀请专业翻译人员进行审核。
十、总结与展望
通过vue-i18n、i18n-ally以及一系列优化策略,我们为uni-app项目构建了一个较为完善的国际化方案。从基础的语言文件配置、多语言切换,到组件国际化、动态内容处理,再到插件集成、性能优化和测试维护,每个环节都紧密相扣,共同提升了应用的国际化能力。
在未来,随着移动应用市场的进一步全球化,国际化需求将变得更加复杂和多样化。一方面,可能需要支持更多的语言和地区,这就要求我们的国际化方案具备更好的扩展性;另一方面,随着人工智能技术的发展,或许可以探索利用AI翻译来提高翻译效率和质量,实现更加智能化的国际化功能。希望本文能为大家在uni-app国际化开发道路上提供参考。