Vue 3 与 i18n 深度集成
概述
Vue 3 与 vue-i18n@9.x 的深度集成提供了强大的国际化能力,支持复杂应用场景下的多语言需求。本教程将深入探讨 vue-i18n 的高级功能,包括动态翻译加载、自定义格式化器、插件扩展、TypeScript 支持等,帮助你构建更加灵活和可维护的国际化应用。
核心知识
1. 动态翻译加载
在大型应用中,我们可能需要根据用户的操作或权限动态加载翻译内容。vue-i18n 支持动态添加和更新翻译消息:
// src/i18n/index.js
import { createI18n } from 'vue-i18n'
const i18n = createI18n({
legacy: false,
locale: 'zh',
fallbackLocale: 'en',
messages: {
zh: {
welcome: '欢迎使用 Vue 3 i18n'
},
en: {
welcome: 'Welcome to Vue 3 i18n'
}
}
})
export default i18n<script setup>
import { useI18n } from 'vue-i18n'
import i18n from '../i18n'
const { t, locale } = useI18n()
// 动态加载翻译
const loadDynamicTranslations = async () => {
try {
// 从 API 或其他来源加载翻译
const dynamicMessages = await fetch('/api/translations', {
params: {
lang: locale.value
}
}).then(res => res.json())
// 更新翻译消息
i18n.global.mergeLocaleMessage(locale.value, dynamicMessages)
} catch (error) {
console.error('Failed to load dynamic translations:', error)
}
}
</script>
<template>
<div>
<h1>{{ t('welcome') }}</h1>
<h2>{{ t('dynamic.content') }}</h2> <!-- 动态加载的翻译 -->
<button @click="loadDynamicTranslations">加载动态翻译</button>
</div>
</template>2. 自定义格式化器
vue-i18n 允许你创建自定义格式化器,用于处理特定类型的翻译内容:
// src/i18n/formatters.js
export const customFormatter = {
// 自定义日期格式化
formatDate(value, format, lng) {
// 自定义日期格式化逻辑
const date = new Date(value)
return date.toLocaleDateString(lng, {
year: 'numeric',
month: 'long',
day: 'numeric'
})
},
// 自定义数字格式化
formatNumber(value, format, lng) {
// 自定义数字格式化逻辑
return new Intl.NumberFormat(lng, {
style: 'decimal',
minimumFractionDigits: 2
}).format(value)
}
}// src/i18n/index.js
import { createI18n } from 'vue-i18n'
import { customFormatter } from './formatters'
const i18n = createI18n({
legacy: false,
locale: 'zh',
fallbackLocale: 'en',
formatters: {
custom: customFormatter
},
messages: {
// ...
}
})<script setup>
import { useI18n } from 'vue-i18n'
const { f } = useI18n()
const today = new Date()
</script>
<template>
<p>{{ f(today, 'custom', 'formatDate') }}</p>
<p>{{ f(1234.56, 'custom', 'formatNumber') }}</p>
</template>3. 插件扩展
vue-i18n 支持通过插件扩展功能,允许你添加自定义的翻译逻辑:
// src/i18n/plugins/uppercase.js
export const uppercasePlugin = {
install(i18n) {
i18n.global.t = new Proxy(i18n.global.t, {
apply(target, thisArg, args) {
const result = Reflect.apply(target, thisArg, args)
// 添加自定义逻辑:如果翻译键包含 "_uppercase",则返回大写
if (args[0].includes('_uppercase')) {
return result.toUpperCase()
}
return result
}
})
}
}// src/i18n/index.js
import { createI18n } from 'vue-i18n'
import { uppercasePlugin } from './plugins/uppercase'
const i18n = createI18n({
legacy: false,
locale: 'zh',
fallbackLocale: 'en',
messages: {
// ...
}
})
// 使用插件
i18n.use(uppercasePlugin)4. TypeScript 支持
vue-i18n 提供了完整的 TypeScript 支持,包括类型定义和类型推断:
// src/i18n/index.ts
import { createI18n, DefineLocaleMessage, DefineDateTimeFormat, DefineNumberFormat } from 'vue-i18n'
// 定义翻译消息类型
type MessageSchema = {
welcome: string
greeting: (name: string) => string
menu: {
home: string
about: string
}
}
// 定义日期时间格式类型
type DateTimeSchema = {
short: DefineDateTimeFormat
long: DefineDateTimeFormat
}
// 定义数字格式类型
type NumberSchema = {
currency: DefineNumberFormat
decimal: DefineNumberFormat
}
// 创建 i18n 实例
const i18n = createI18n<
MessageSchema,
DateTimeSchema,
NumberSchema
>({
legacy: false,
locale: 'zh',
fallbackLocale: 'en',
messages: {
// ...
},
datetimeFormats: {
// ...
},
numberFormats: {
// ...
}
})
export default i18n5. 组件范围的 i18n
vue-i18n 支持组件范围的 i18n 配置,允许你为特定组件定义翻译:
<!-- src/components/HelloWorld.vue -->
<script setup>
import { useI18n } from 'vue-i18n'
const { t } = useI18n({
inheritLocale: true,
messages: {
zh: {
component: '组件范围的翻译'
},
en: {
component: 'Component-scoped translation'
}
}
})
</script>
<template>
<p>{{ t('component') }}</p>
</template>6. 翻译缓存
为了提高性能,vue-i18n 支持翻译结果缓存。你可以配置缓存策略:
// src/i18n/index.js
import { createI18n } from 'vue-i18n'
const i18n = createI18n({
legacy: false,
locale: 'zh',
fallbackLocale: 'en',
// 启用缓存
enableCache: true,
// 自定义缓存键生成器
getCacheKey: (key, locale, options) => {
return `${key}-${locale}-${JSON.stringify(options)}`
},
messages: {
// ...
}
})7. 错误处理
vue-i18n 提供了灵活的错误处理机制,允许你处理缺失的翻译键:
// src/i18n/index.js
import { createI18n } from 'vue-i18n'
const i18n = createI18n({
legacy: false,
locale: 'zh',
fallbackLocale: 'en',
// 处理缺失的翻译键
missing: (locale, key) => {
console.warn(`Missing translation for key "${key}" in locale "${locale}"`)
return `[${key}]`
},
// 处理回退
fallbackRoot: true,
messages: {
// ...
}
})最佳实践
1. 模块化翻译管理
将翻译按功能模块组织,便于维护和扩展:
src/
├── i18n/
│ ├── index.js # i18n 配置
│ ├── locales/ # 语言包目录
│ │ ├── en/ # 英文语言包
│ │ │ ├── common.json
│ │ │ ├── home.json
│ │ │ └── about.json
│ │ └── zh/ # 中文语言包
│ │ ├── common.json
│ │ ├── home.json
│ │ └── about.json
│ └── plugins/ # 插件目录// src/i18n/index.js
import { createI18n } from 'vue-i18n'
// 动态导入所有语言包
const loadLocaleMessages = () => {
const locales = require.context('./locales', true, /[A-Za-z0-9-_,\s]+\.json$/i)
const messages = {}
locales.keys().forEach(key => {
const matched = key.match(/([A-Za-z0-9-_]+)\/([A-Za-z0-9-_]+)\.json$/)
if (matched && matched.length > 2) {
const locale = matched[1]
const module = matched[2]
if (!messages[locale]) {
messages[locale] = {}
}
messages[locale][module] = locales(key)
}
})
return messages
}
const i18n = createI18n({
legacy: false,
locale: 'zh',
fallbackLocale: 'en',
messages: loadLocaleMessages()
})
export default i18n2. 使用 i18n 组件
vue-i18n 提供了 <i18n> 组件,用于处理复杂的翻译场景:
<template>
<div>
<i18n path="greeting" tag="h1">
<template #name>
<span class="highlight">{{ userName }}</span>
</template>
</i18n>
<i18n path="message.count" tag="p">
<template #count>
<strong>{{ messageCount }}</strong>
</template>
</i18n>
</div>
</template>3. 与状态管理结合
将 i18n 状态与 Pinia 或 Vuex 结合,实现更复杂的国际化逻辑:
// src/stores/i18n.js
import { defineStore } from 'pinia'
import i18n from '../i18n'
export const useI18nStore = defineStore('i18n', {
state: () => ({
availableLocales: [
{ code: 'en', name: 'English' },
{ code: 'zh', name: '中文' }
],
currentLocale: i18n.global.locale.value
}),
actions: {
changeLocale(locale) {
this.currentLocale = locale
i18n.global.locale.value = locale
localStorage.setItem('locale', locale)
},
async loadModuleTranslations(moduleName) {
try {
const translations = await import(`../i18n/locales/${this.currentLocale}/${moduleName}.json`)
i18n.global.mergeLocaleMessage(this.currentLocale, {
[moduleName]: translations.default
})
} catch (error) {
console.error(`Failed to load ${moduleName} translations:`, error)
}
}
}
})4. 性能优化
- 懒加载语言包:只加载当前语言的翻译内容
- 按需加载模块:根据路由或组件按需加载翻译模块
- 使用缓存:启用翻译结果缓存,减少重复计算
- 避免在模板中使用复杂逻辑:将复杂的翻译逻辑移到 JavaScript 中
5. 测试策略
为国际化应用编写测试,确保翻译功能正常工作:
// src/tests/i18n.spec.js
import { mount } from '@vue/test-utils'
import { createI18n } from 'vue-i18n'
import HelloWorld from '../components/HelloWorld.vue'
const i18n = createI18n({
legacy: false,
locale: 'en',
messages: {
en: {
welcome: 'Welcome'
},
zh: {
welcome: '欢迎'
}
}
})
describe('i18n tests', () => {
test('renders correct English translation', () => {
const wrapper = mount(HelloWorld, {
global: {
plugins: [i18n]
}
})
expect(wrapper.text()).toContain('Welcome')
})
test('renders correct Chinese translation', () => {
i18n.global.locale.value = 'zh'
const wrapper = mount(HelloWorld, {
global: {
plugins: [i18n]
}
})
expect(wrapper.text()).toContain('欢迎')
})
})常见问题与解决方案
1. 问题:TypeScript 类型推断不准确
解决方案:
- 显式定义翻译消息类型
- 使用
createI18n的泛型参数 - 为组件范围的 i18n 提供类型
2. 问题:动态加载翻译不生效
解决方案:
- 确保使用
mergeLocaleMessage方法更新翻译 - 检查语言包格式是否正确
- 验证当前语言是否与加载的翻译匹配
3. 问题:性能问题(大型语言包)
解决方案:
- 实现懒加载机制
- 按模块拆分语言包
- 启用翻译缓存
- 使用动态导入
4. 问题:翻译键冲突
解决方案:
- 使用模块化命名空间
- 为相同键添加不同上下文
- 实现自定义的键解析逻辑
5. 问题:SSR 环境下的 i18n 配置
解决方案:
- 使用
createSSRApp替代createApp - 确保 i18n 配置在服务器和客户端保持一致
- 使用
useI18n的useScope选项
// src/i18n/index.js
import { createI18n } from 'vue-i18n'
export function createI18nInstance() {
return createI18n({
legacy: false,
locale: 'zh',
fallbackLocale: 'en',
messages: {
// ...
}
})
}高级学习资源
1. 官方文档
2. 工具和库
- i18n-ally:VS Code 插件
- vue-i18n-loader:Vue 单文件组件加载器
- intlify/eslint-plugin-vue-i18n:ESLint 插件
3. 最佳实践指南
实践练习
练习 1:动态翻译加载
- 创建一个 Vue 3 项目并配置 vue-i18n
- 实现动态加载翻译的功能
- 从模拟 API 加载翻译内容
- 测试动态翻译的效果
练习 2:自定义格式化器
- 创建自定义日期和时间格式化器
- 实现自定义数字和货币格式化器
- 在组件中使用自定义格式化器
练习 3:TypeScript 支持
- 为翻译消息添加 TypeScript 类型定义
- 配置 i18n 实例的泛型参数
- 编写类型安全的翻译代码
- 运行 TypeScript 检查确保类型正确
练习 4:模块化翻译管理
- 按功能模块组织翻译文件
- 实现动态导入所有语言包的功能
- 测试不同模块的翻译加载
- 优化语言包的加载性能
练习 5:插件扩展
- 创建一个自定义 i18n 插件
- 实现插件的 install 方法
- 在插件中扩展翻译功能
- 测试插件的效果
总结
Vue 3 与 vue-i18n@9.x 的深度集成提供了强大的国际化能力,支持动态翻译加载、自定义格式化、插件扩展等高级功能。通过合理的架构设计和最佳实践,你可以构建出灵活、可维护的国际化应用。
本教程介绍了 vue-i18n 的高级特性和最佳实践,包括动态翻译加载、自定义格式化器、TypeScript 支持、组件范围的 i18n 等。通过学习这些内容,你可以更好地应对复杂应用的国际化需求,为全球用户提供更好的体验。
在实际开发中,你需要根据应用的规模和需求选择合适的国际化策略,结合性能优化和测试,确保国际化功能的正常运行和良好性能。