Vue.js 国际化利器:vue-i18n 详细介绍与使用指南
随着互联网应用的全球化发展,让你的应用能够支持多种语言已经成为一项必备能力。国际化(Internationalization,简称 i18n,因为 I 和 N 之间有18个字母)是指在设计和实现应用时,使其可以适应不同国家、地区和语言的本地化需求。本地化(Localization,简称 l10n)则是针对特定地区和语言,对国际化后的应用进行翻译和调整。
在 Vue.js 生态中,vue-i18n
是最流行和强大的国际化库,它提供了简洁易用的 API,帮助开发者轻松地为 Vue 应用添加多语言支持。本文将深入探讨 vue-i18n
的各个方面,从基础概念到高级用法,带你全面掌握这个工具。
1. 为什么需要国际化?
在开始学习 vue-i18n
之前,我们先来思考为什么要在应用中引入国际化:
- 扩大用户群体: 支持多种语言能让你的应用触达更广泛的全球用户。
- 提升用户体验: 用户更愿意使用他们母语的应用,这能显著提高用户满意度。
- 商业需求: 对于面向全球市场的企业来说,多语言支持是 필수 (必须的)。
- 增强品牌形象: 提供多语言版本显示了对全球用户的重视,提升了品牌专业度。
简单的字符串替换或者手动维护多语言对象的方式在小型应用中可能可行,但随着应用规模的增长,维护会变得异常困难且容易出错。vue-i18n
提供了一套完整的解决方案,帮助我们高效、优雅地处理多语言问题。
2. vue-i18n 简介
vue-i18n
是一个为 Vue.js 量身定制的国际化插件,它无缝集成了 Vue 的响应式系统,提供了以下核心功能:
- 声明式翻译: 在模板中使用简洁的语法调用翻译函数。
- 程序化翻译: 在 JavaScript 逻辑中进行翻译。
- 支持复数: 根据数量自动选择正确的复数字符串。
- 日期/时间格式化: 根据语言环境格式化日期和时间。
- 数字格式化: 根据语言环境格式化数字和货币。
- 列表和命名参数插值: 方便地将变量插入到翻译字符串中。
- 组件级本地化: 将翻译消息定义在单个组件内部,提高代码的可维护性。
- SFC
<i18n>
块支持: 在.vue
文件中使用<i18n>
块定义组件消息。 - 异步加载语言包: 按需加载翻译文件,优化应用性能。
- 与 Vue Devtools 集成: 方便调试和查看当前语言环境及消息。
它支持 Vue 2 和 Vue 3,提供了 Composition API 和 Options API 的支持。
3. 安装与基本配置
3.1 安装
使用 npm 或 yarn 进行安装:
“`bash
npm install vue-i18n@next # 对于 Vue 3
或者
yarn add vue-i18n@next # 对于 Vue 3
如果是 Vue 2
npm install vue-i18n@8
or
yarn add vue-i18n@8
“`
本文主要基于 vue-i18n@next
(v9+) 版本,兼容 Vue 3。
3.2 基本配置
在 Vue 3 应用中,你需要在应用入口文件(通常是 main.js
或 main.ts
)中创建 i18n
实例并将其提供给 Vue 应用。
首先,创建语言包文件。通常将语言包放在一个单独的目录下,例如 src/locales
。
json
// src/locales/en.json
{
"welcome": "Welcome!",
"greeting": "Hello, {name}!",
"messages": {
"hello": "Hello world!"
}
}
json
// src/locales/zh.json
{
"welcome": "欢迎!",
"greeting": "你好,{name}!",
"messages": {
"hello": "你好,世界!"
}
}
然后,在 main.js
或 main.ts
中配置 vue-i18n
:
“`javascript
// src/main.js
import { createApp } from ‘vue’
import { createI18n } from ‘vue-i18n’
import App from ‘./App.vue’
// 导入语言包
import en from ‘./locales/en.json’
import zh from ‘./locales/zh.json’
// 语言包消息对象
const messages = {
en: en,
zh: zh
}
// 创建 i18n 实例
const i18n = createI18n({
// legacy: false, // 如果你使用 Composition API,设置为 false
locale: ‘en’, // 设置默认语言
fallbackLocale: ‘en’, // 设置备用语言
messages, // 设置语言包消息
// runtimeOnly: false, // 仅在完整构建中需要,如果在运行时构建,则设置为 true
// globalInjection: true // 如果希望在全局范围内使用 $t 等 API,设置为 true (Vue 3 v9.3+ 默认是 false)
})
const app = createApp(App)
// 将 i18n 实例挂载到 Vue 应用
app.use(i18n)
app.mount(‘#app’)
“`
注意 globalInjection
选项: 在 vue-i18n
v9.3+ 中,globalInjection
默认为 false
,这意味着你不能直接在模板中使用 $t
等全局属性。如果你希望像 Vue 2 那样在模板中无处不在地使用 $t
,可以将 globalInjection
设置为 true
。或者,推荐的方式是在需要使用翻译的组件中,通过 Composition API 的 useI18n
导入 t
函数。本文示例默认使用 globalInjection: true
以简化模板示例。
4. 核心概念与使用
配置完成后,就可以在 Vue 组件中使用 vue-i18n
提供的功能了。
4.1 定义翻译消息 (Messages)
翻译消息是以键值对形式组织的。键(Key)是你用来引用翻译文本的标识符,值(Value)是对应语言的实际文本。消息可以是一个简单的对象,也可以是嵌套的对象,用于更好地组织和分类。
json
// messages 结构示例
{
"en": {
"appTitle": "My Awesome App",
"nav": {
"home": "Home",
"about": "About",
"contact": "Contact"
},
"errors": {
"notFound": "Page not found",
"serverError": "Server error occurred"
},
"user": {
"profile": {
"title": "User Profile",
"saveButton": "Save"
}
}
},
"zh": {
"appTitle": "我的超赞应用",
"nav": {
"home": "首页",
"about": "关于",
"contact": "联系我们"
},
"errors": {
"notFound": "页面未找到",
"serverError": "服务器发生错误"
},
"user": {
"profile": {
"title": "用户资料",
"saveButton": "保存"
}
}
}
}
通过使用点 .
操作符,你可以访问嵌套的消息,例如 nav.home
或 user.profile.title
。
4.2 获取和设置当前语言环境 (Locale)
vue-i18n
实例有一个 locale
属性,用来表示当前使用的语言。你可以通过修改这个属性来切换语言。
“`javascript
// main.js 或其他地方获取 i18n 实例
const i18n = createI18n({ … }); // 假设这是你的 i18n 实例
// 获取当前语言
console.log(i18n.global.locale.value); // 对于 Composition API
// 设置语言为中文
i18n.global.locale.value = ‘zh’; // 对于 Composition API
“`
在 Options API 组件中,可以通过 this.$i18n.locale
来获取和设置。
“`vue
{{ $t(‘welcome’) }}
“`
在 Composition API 组件中,可以通过 useI18n
获取 locale
的 ref。
“`vue
{{ t(‘welcome’) }}
“`
4.3 在模板中使用翻译
vue-i18n
提供了 $t
方法(或 Composition API 中的 t
函数)用于在模板中进行翻译。
“`vue
{{ $t(‘appTitle’) }}
{{ $t(‘messages.hello’) }}
{{ $t(‘greeting’, { name: ‘Vue User’ }) }}
{{ t(‘nav.home’) }}
“`
4.4 在 JavaScript 逻辑中使用翻译
在组件的 <script>
块中,你也可以使用 $t
或 t
进行程序化翻译。
“`vue
“`
4.5 带有参数的翻译 (Interpolation)
翻译消息中经常需要插入动态数据。vue-i18n
支持使用花括号 {}
定义命名参数。
json
{
"en": {
"welcomeUser": "Welcome, {userName}!"
},
"zh": {
"welcomeUser": "欢迎,{userName}!"
}
}
在模板或脚本中传递参数:
“`vue
{{ $t(‘welcomeUser’, { userName: currentUser.name }) }}
“`
如果参数是一个对象,vue-i18n
会自动匹配键名。如果参数是一个数组,它会按索引 {0}
, {1}
等匹配。
5. 高级用法
5.1 复数处理 (Pluralization)
不同语言的复数规则不同。vue-i18n
支持复数规则,通常通过在翻译消息中使用管道符 |
分隔不同形式的文本来实现。
例如,英文的复数规则相对简单:0个、1个、多个。
json
{
"en": {
"car": "no cars | one car | {count} cars"
},
"zh": {
"car": "{count} 辆车" // 中文没有复数形式变化,但可以通过插值显示数量
}
}
使用 $t
或 t
时,传递一个表示数量的参数:
“`vue
{{ $t(‘car’, { count: 0 }) }}
{{ $t(‘car’, { count: 1 }) }}
{{ $t(‘car’, { count: 5 }) }}
{{ $t(‘car’, { count: 5 }, 5) }}
“`
对于更复杂的复数规则(如俄语有 4 种或更多形式),vue-i18n
依赖于 Intl.PluralRules
API。你可以通过 pluralRules
选项在创建实例时配置自定义规则,或者利用库内置的支持(如果环境支持 Intl.PluralRules
)。
使用 plural
参数是更推荐的方式,它会自动根据数量和当前语言选择正确的形式:
json
{
"en": {
"apple": "no apples | one apple | {count} apples"
},
"zh": {
"apple": "{count} 个苹果"
}
}
“`vue
{{ $t(‘apple’, 0) }}
{{ $t(‘apple’, 1) }}
{{ $t(‘apple’, 10) }}
“`
5.2 日期/时间格式化 (DateTime Formatting)
vue-i18n
可以格式化日期和时间,同样根据不同的语言环境显示不同的格式。这需要你在创建 i18n
实例时提供 dateTimeFormats
选项。
“`javascript
// main.js
import { createI18n } from ‘vue-i18n’;
const i18n = createI18n({
locale: ‘en’,
// … other options
dateTimeFormats: {
en: {
short: {
year: ‘numeric’, month: ‘short’, day: ‘numeric’
},
long: {
year: ‘numeric’, month: ‘long’, day: ‘numeric’,
weekday: ‘long’, hour: ‘numeric’, minute: ‘numeric’
}
},
zh: {
short: {
year: ‘numeric’, month: ‘short’, day: ‘numeric’
},
long: {
year: ‘numeric’, month: ‘long’, day: ‘numeric’,
weekday: ‘long’, hour: ‘numeric’, minute: ‘numeric’, hour12: false
}
}
}
});
“`
在模板或脚本中使用 $d
或 d
:
“`vue
{{ $d(now, ‘short’) }}
{{ $d(now, ‘long’) }}
“`
$d
的第一个参数是 Date
对象或时间戳,第二个参数是你在 dateTimeFormats
中定义的格式名称。
5.3 数字格式化 (Number Formatting)
类似日期格式化,你可以在 numberFormats
选项中定义数字格式,然后在模板或脚本中使用 $n
或 n
进行格式化。
“`javascript
// main.js
import { createI18n } from ‘vue-i18n’;
const i18n = createI18n({
locale: ‘en’,
// … other options
numberFormats: {
en: {
currency: {
style: ‘currency’, currency: ‘USD’
},
decimal: {
style: ‘decimal’, minimumFractionDigits: 2, maximumFractionDigits: 2
}
},
zh: {
currency: {
style: ‘currency’, currency: ‘CNY’, currencyDisplay: ‘symbol’
},
decimal: {
style: ‘decimal’, minimumFractionDigits: 2, maximumFractionDigits: 2
}
}
}
});
“`
在模板或脚本中使用 $n
或 n
:
“`vue
{{ $n(1000.50, ‘currency’) }}
{{ $n(0.12345, ‘decimal’) }}
“`
$n
的第一个参数是数字,第二个参数是你在 numberFormats
中定义的格式名称。
5.4 组件内本地化 (Component Localication)
除了在全局定义消息,你还可以在单个组件中定义自己的消息。这提高了组件的独立性和可维护性。vue-i18n
支持两种方式实现组件内本地化:
方式一:在 Options API 组件中使用 messages
选项
“`vue
{{ $t(‘componentTitle’) }}
{{ $t(‘componentMessage’) }}
{{ $t(‘globalMessage’) }}
“`
组件内的消息会与全局消息合并。如果键名冲突,组件内的消息会覆盖全局消息。
方式二:在 SFC 中使用 <i18n>
块 (推荐)
这是 Vue 3 和 vue-i18n@next
推荐的方式,更简洁直观。
“`vue
{{ $t(‘title’) }}
{{ $t(‘message’) }}
{{ $t(‘globalMessage’) }}
{
“en”: {
“title”: “Component Title”,
“message”: “This is a message defined in the
},
“zh”: {
“title”: “组件标题”,
“message”: “这是一条在
}
}
“`
使用 <i18n>
块需要在你的构建工具中配置相应的 loader,例如 @intlify/vue-i18n-loader
或 @intlify/vite-plugin-vue-i18n
(对于 Vite)。这些 loader 会自动处理 <i18n>
块并将其消息合并到组件中。
5.5 异步加载语言包 (Lazy Loading)
对于大型应用,将所有语言包一次性加载会增加初始加载时间。vue-i18n
支持按需异步加载语言包。
实现思路是:当需要切换到某个语言时,先判断该语言包是否已加载,如果未加载则动态导入并添加到 i18n
实例中。
“`javascript
// src/i18n.js (创建一个单独的文件来管理 i18n 实例和加载逻辑)
import { createI18n } from ‘vue-i18n’;
// 默认语言和备用语言
const DEFAULT_LOCALE = ‘en’;
const FALLBACK_LOCALE = ‘en’;
// 创建 i18n 实例,初始只加载默认语言包
const i18n = createI18n({
locale: DEFAULT_LOCALE,
fallbackLocale: FALLBACK_LOCALE,
messages: {
[DEFAULT_LOCALE]: await import(./locales/${DEFAULT_LOCALE}.json
).then(m => m.default)
},
// … other options (dateTimeFormats, numberFormats, etc.)
// globalInjection: true // 如果需要全局注入
});
// 用于存储已加载的语言包
const loadedLocales = [DEFAULT_LOCALE];
// 异步加载语言包的函数
async function loadLocaleMessages(locale) {
// 如果语言包已加载,则直接返回
if (loadedLocales.includes(locale)) {
return Promise.resolve();
}
try {
// 动态导入语言包
const messages = await import(./locales/${locale}.json
);
// 将语言包添加到 i18n 实例
i18n.global.setLocaleMessage(locale, messages.default);
// 标记为已加载
loadedLocales.push(locale);
return Promise.resolve();
} catch (e) {
console.error(Error loading locale ${locale}
, e);
return Promise.reject(e);
}
}
// 切换语言并加载语言包的函数
async function setLocale(locale) {
if (i18n.global.locale.value === locale) {
return Promise.resolve(); // 已经是当前语言
}
try {
// 异步加载语言包
await loadLocaleMessages(locale);
// 设置当前语言
i18n.global.locale.value = locale;
// 可选:将语言偏好保存到 localStorage
// localStorage.setItem(‘user-locale’, locale);
return Promise.resolve();
} catch (e) {
// 加载失败时,可以回退到备用语言或其他处理
console.error(Failed to set locale to ${locale}, falling back to ${FALLBACK_LOCALE}
, e);
i18n.global.locale.value = FALLBACK_LOCALE;
return Promise.reject(e);
}
}
export { i18n, setLocale, DEFAULT_LOCALE };
“`
在 main.js
中:
“`javascript
// src/main.js
import { createApp } from ‘vue’;
import App from ‘./App.vue’;
import { i18n, setLocale, DEFAULT_LOCALE } from ‘./i18n’; // 导入 i18n 实例和 setLocale 函数
const app = createApp(App);
// 使用 i18n 实例
app.use(i18n);
// 可选:根据用户偏好或浏览器设置初始化语言
const userPreferredLocale = localStorage.getItem(‘user-locale’) || navigator.language.split(‘-‘)[0] || DEFAULT_LOCALE;
setLocale(userPreferredLocale).then(() => {
app.mount(‘#app’);
});
// 或者直接 mount,然后在需要时异步加载
// app.mount(‘#app’);
// setLocale(userPreferredLocale);
“`
在组件中切换语言:
“`vue
“`
5.6 结合 Vue Router
在单页应用中,将语言信息放在 URL 中是一种常见的做法,例如 /en/about
, /zh/contact
。这有助于 SEO 和书签。你可以利用 Vue Router 的导航守卫来根据路由参数或路径前缀设置语言。
“`javascript
// src/router/index.js
import { createRouter, createWebHistory } from ‘vue-router’;
import { i18n, setLocale, DEFAULT_LOCALE } from ‘@/i18n’; // 导入 setLocale 函数
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes: [
// 定义支持的语言列表
{
path: ‘/:locale(en|zh)/’, // 使用路由参数捕捉语言,并限制参数值
component: { render: () => h(‘router-view’) }, // 一个简单的容器组件
children: [
{
path: ”, // e.g., /en/
name: ‘home’,
component: () => import(‘../views/HomeView.vue’)
},
{
path: ‘about’, // e.g., /en/about
name: ‘about’,
component: () => import(‘../views/AboutView.vue’)
},
// … 其他路由
]
},
// 重定向到带默认语言的首页
{
path: ‘/:pathMatch(.)‘, // 匹配所有未匹配的路径
redirect: /${DEFAULT_LOCALE}/
}
]
});
// 全局前置守卫
router.beforeEach(async (to, from, next) => {
const locale = to.params.locale;
// 如果路由参数中有语言,且不是当前语言,则设置语言并加载语言包
if (locale && locale !== i18n.global.locale.value) {
try {
await setLocale(locale); // 异步加载语言包并设置语言
} catch (e) {
console.error(‘Failed to load locale, redirecting to default.’);
// 加载失败时,重定向到默认语言的路由
return next({ …to, params: { locale: DEFAULT_LOCALE } });
}
} else if (!locale) {
// 如果路由中没有语言参数(例如直接访问 /),则重定向到带默认语言的路由
// 这里的逻辑取决于你的路由设计,如果所有路由都在 /:locale 之下,这一步是必需的
const defaultLocale = localStorage.getItem(‘user-locale’) || navigator.language.split(‘-‘)[0] || DEFAULT_LOCALE;
return next({ …to, params: { locale: defaultLocale } });
}
next(); // 继续路由导航
});
export default router;
“`
在 main.js
中使用这个路由器:
“`javascript
// src/main.js
import { createApp, h } from ‘vue’; // 导入 h 函数用于 router-view 渲染
import App from ‘./App.vue’;
import router from ‘./router’;
import { i18n } from ‘./i18n’; // 导入 i18n 实例
const app = createApp(App);
app.use(i18n);
app.use(router); // 使用路由器
// mount 应用
app.mount(‘#app’);
“`
通过这种方式,当用户访问 /zh/about
时,Vue Router 捕获 zh
参数,导航守卫会调用 setLocale('zh')
加载并切换到中文语言包。
5.7 处理缺失的翻译 (Missing Translations)
如果 $t
或 t
调用的键在当前语言包中不存在,vue-i18n
默认会回退到 fallbackLocale
中查找。如果备用语言中也不存在,则会输出警告并返回原始键名。
你可以通过 missing
选项来自定义处理缺失键的行为,例如记录错误或返回特定的字符串:
javascript
const i18n = createI18n({
locale: 'en',
fallbackLocale: 'en',
messages: {
en: { hello: 'Hello' },
zh: { } // zh 中没有 hello
},
missingTrack: true, // 开启追踪缺失键的功能 (开发环境下有用)
missingWarn: true, // 开启控制台警告 (开发环境下有用)
missing: (locale, key, message) => {
// 自定义处理逻辑
console.warn(`[i18n] Missing key '${key}' for locale '${locale}'. Original message: '${message}'`);
// 返回一个备用字符串,或者原始键
return `[MISSING] ${key}`;
}
});
6. 最佳实践与注意事项
- 组织语言包: 将不同语言的翻译放在单独的文件中(如
en.json
,zh.json
)。在每个文件内部,使用嵌套对象来组织消息,例如按功能模块或页面划分。 - 命名规范: 使用清晰、有描述性的键名,尽量避免缩写。例如,
button.save
,page.user.profile.title
. - 避免硬编码字符串: 所有面向用户的文本都应该通过
vue-i18n
进行翻译。 - 使用参数插值: 对于包含变量的句子,使用参数插值
{variable}
而不是拼接字符串,这样可以更好地处理不同语言的语法结构和词序。 - 复数处理: 仔细考虑不同语言的复数规则,并正确使用复数功能。
- 日期/数字格式: 利用
$d
和$n
处理日期和数字,而不是手动格式化,确保符合当地习惯。 - 懒加载: 对于大型应用,务必实现语言包的懒加载,减少初始加载负担。
- 工作流: 考虑如何提取需要翻译的字符串、如何与翻译人员协作、如何合并翻译文件。可以使用一些工具来辅助(例如
vue-i18n-extract
)。 - CDN/性能: 考虑将大型语言包部署到 CDN,或者使用构建工具的 chunking 功能,优化加载性能。
- 服务端渲染 (SSR): 在 SSR 环境下使用
vue-i18n
需要特别注意状态的同步,确保服务器和客户端渲染时使用相同的语言环境和消息。vue-i18n
提供了 SSR 相关的 API 和指南。 - 单元测试: 在进行组件单元测试时,需要 mock 或提供一个简单的
vue-i18n
实例,以便组件中的$t
等方法可以正常调用。
7. 总结
vue-i18n
是一个功能强大、灵活且与 Vue.js 生态紧密集成的国际化库。通过掌握其核心概念(消息、语言环境、插值、复数、格式化)和高级用法(组件内本地化、异步加载、路由集成),你可以高效地为你的 Vue 应用添加多语言支持,极大地提升应用的可用性和用户体验,为全球用户提供更好的服务。
无论是小型项目还是大型企业级应用,vue-i18n
都能提供可靠的解决方案,让你轻松应对国际化带来的挑战。开始使用 vue-i18n
,让你的应用走向世界吧!