大家好,我是老码农,今天咱们来聊聊Vue 3里一个挺有意思的玩意儿:i18n
,也就是国际化。这玩意儿听起来高大上,其实说白了,就是让你的网站或者App能说各国语言,让来自五湖四海的朋友们都能看得懂。
咱们今天不光要讲怎么用,更要扒一扒它的底层实现,看看它怎么做到动态加载语言包的。保证让你听完之后,下次面试官问你“Vue的i18n怎么实现的?”的时候,你能自信地说:“老子不仅会用,还会造!”
一、i18n
是个啥?为啥要用它?
咱们先来聊聊i18n
是个啥。i18n
是Internationalization的缩写,中间省略了18个字母,所以就叫i18n
。还有个相关的概念叫l10n
,是Localization的缩写,中间省略了10个字母。简单来说,i18n
是指让你的应用具备国际化的能力,而l10n
是指针对特定语言和地区进行适配。
为啥要用它呢?你想啊,你的App如果只支持中文,那老外来了不是抓瞎?所以,为了让更多人能用你的产品,国际化是必不可少的。
二、vue-i18n
:Vue官方推荐的国际化插件
在Vue的世界里,vue-i18n
是官方推荐的国际化插件。它提供了一套简单易用的API,可以让你轻松地实现多语言支持。
-
安装
npm install vue-i18n@next # 或者 yarn add vue-i18n@next
注意:这里用的是
@next
,因为我们要用Vue 3的版本。 -
基本使用
// main.js import { createApp } from 'vue' import { createI18n } from 'vue-i18n' import App from './App.vue' // 语言包 const messages = { en: { message: { hello: 'hello world' } }, zh: { message: { hello: '你好世界' } } } const i18n = createI18n({ locale: 'zh', // 默认语言 fallbackLocale: 'en', // 备用语言 messages }) const app = createApp(App) app.use(i18n) app.mount('#app')
<!-- App.vue --> <template> <p>{{ $t('message.hello') }}</p> <button @click="changeLocale('en')">切换到英文</button> <button @click="changeLocale('zh')">切换到中文</button> </template> <script> import { useI18n } from 'vue-i18n' export default { setup() { const { t, locale } = useI18n() const changeLocale = (lang) => { locale.value = lang } return { t, changeLocale } } } </script>
这段代码很简单,首先我们定义了一个
messages
对象,里面包含了英文和中文的语言包。然后,我们用createI18n
创建了一个i18n
实例,并将其挂载到Vue应用上。在组件中,我们使用
useI18n
hook来获取t
函数和locale
变量。t
函数用于翻译文本,locale
变量用于切换语言。 -
$t
是个啥?$t
是一个全局函数,它接收一个字符串作为参数,这个字符串就是你在语言包里定义的key。vue-i18n
会根据当前的locale
,在语言包里找到对应的文本,并将其返回。例如,
$t('message.hello')
会根据当前的locale
,在messages
对象里找到message.hello
对应的文本。 -
locale
又是啥?locale
指的是当前的语言环境。比如,zh
代表中文,en
代表英文。vue-i18n
会根据当前的locale
来选择使用哪个语言包。
三、动态加载语言包:让你的应用更灵活
上面的例子里,我们将所有的语言包都放在了 main.js
里。这样做虽然简单,但是如果你的应用支持的语言很多,那 main.js
就会变得非常臃肿。而且,每次新增一种语言,都需要修改 main.js
,重新打包发布。
为了解决这个问题,我们可以使用动态加载语言包的方式。
-
把语言包拆分到单独的文件里
首先,我们把语言包拆分到单独的文件里,例如:
-
locales/en.json
{ "message": { "hello": "hello world" } }
-
locales/zh.json
{ "message": { "hello": "你好世界" } }
-
-
使用
createI18n
的messages
选项加载初始语言包// 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 } const i18n = createI18n({ locale: 'zh', // 默认语言 fallbackLocale: 'en', // 备用语言 messages }) const app = createApp(App) app.use(i18n) app.mount('#app')
注意这里
import en from './locales/en.json'
会将 json 文件解析成对象。 -
编写动态加载语言包的函数
// src/i18n.js import { createI18n } from 'vue-i18n' import en from './locales/en.json' import zh from './locales/zh.json' const messages = { en: en, zh: zh } export const i18n = createI18n({ locale: 'zh', // 默认语言 fallbackLocale: 'en', // 备用语言 messages }) export async function loadLocaleMessages(locale) { // 如果已经加载过了,就直接返回 if (i18n.global.availableLocales.includes(locale)) { return } try { // 动态加载语言包 const messages = await import(`./locales/${locale}.json`) // 添加到 i18n 实例中 i18n.global.setLocaleMessage(locale, messages.default) // 设置 locale i18n.global.locale.value = locale } catch (e) { console.error(`Failed to load locale ${locale}`, e) } }
这个函数接收一个
locale
作为参数,然后使用import()
动态加载对应的语言包。加载完成后,我们使用i18n.global.setLocaleMessage
将语言包添加到i18n
实例中,并使用i18n.global.locale.value
设置当前的locale
。import()
是一个动态导入的语法,它允许你在运行时加载模块。这对于按需加载语言包非常有用。 -
在组件中使用动态加载语言包的函数
<!-- App.vue --> <template> <p>{{ $t('message.hello') }}</p> <button @click="changeLocale('en')">切换到英文</button> <button @click="changeLocale('zh')">切换到中文</button> </template> <script> import { useI18n } from 'vue-i18n' import { loadLocaleMessages } from './i18n' export default { setup() { const { t, locale } = useI18n() const changeLocale = async (lang) => { await loadLocaleMessages(lang) locale.value = lang } return { t, changeLocale } } } </script>
在组件中,我们调用
loadLocaleMessages
函数来动态加载语言包。注意,changeLocale
函数需要声明为async
,因为loadLocaleMessages
函数返回的是一个 Promise。
四、vue-i18n
的底层实现:扒一扒源码
光会用还不够,咱们还得扒一扒 vue-i18n
的底层实现,看看它到底是怎么工作的。
-
createI18n
函数createI18n
函数是vue-i18n
的入口函数,它接收一个配置对象作为参数,并返回一个i18n
实例。// packages/vue-i18n/src/index.ts export function createI18n(options: I18nOptions = {}): I18n { // ...省略N行代码... const i18n: I18n = { install(app: App): void { // ...省略N行代码... }, version: VERSION } return i18n }
createI18n
函数主要做了以下几件事:- 创建了一个
i18n
实例。 - 在
i18n
实例上定义了一些属性,例如locale
、fallbackLocale
、messages
等。 - 定义了一个
install
方法,用于将i18n
实例挂载到 Vue 应用上。
- 创建了一个
-
install
方法install
方法是 Vue 插件的标准方法,它会在 Vue 应用启动时被调用。vue-i18n
的install
方法主要做了以下几件事:- 将
$t
函数添加到 Vue 的原型上,这样我们就可以在组件中使用$t
函数了。 - 将
i18n
实例添加到 Vue 的全局属性上,这样我们就可以在组件中使用this.$i18n
来访问i18n
实例了。 - 注册一个全局组件
<i18n>
,用于在模板中进行国际化。 - 注册一个全局指令
v-t
,用于在 DOM 元素上进行国际化。
- 将
-
$t
函数的实现$t
函数的实现比较复杂,它需要根据当前的locale
,在语言包里找到对应的文本,并进行一些处理,例如:- 插值:将文本中的变量替换为实际的值。
- 复数:根据数值的不同,选择不同的文本。
- 格式化:对文本进行格式化,例如日期格式化、数字格式化等。
// packages/vue-i18n/src/composer.ts function translate(key: string, ...values: any[]): string { // ...省略N行代码... // 获取当前 locale 的语言包 const messages = getLocaleMessages(locale.value) // 在语言包里查找对应的文本 const message = lookup(messages, key) // 如果找不到,就使用 fallback locale 的语言包 if (!message && fallbackLocale.value) { // ...省略N行代码... } // 如果还是找不到,就返回 key 本身 if (!message) { return key } // 进行插值和格式化 const formattedMessage = format(message, values) return formattedMessage }
translate
函数主要做了以下几件事:- 获取当前
locale
的语言包。 - 在语言包里查找对应的文本。
- 如果找不到,就使用
fallbackLocale
的语言包。 - 如果还是找不到,就返回
key
本身。 - 对文本进行插值和格式化。
-
动态加载语言包的实现
动态加载语言包的实现主要依赖于
import()
函数和i18n.global.setLocaleMessage
函数。import()
函数用于动态加载语言包,i18n.global.setLocaleMessage
函数用于将语言包添加到i18n
实例中。// src/i18n.js export async function loadLocaleMessages(locale) { // ...省略N行代码... try { // 动态加载语言包 const messages = await import(`./locales/${locale}.json`) // 添加到 i18n 实例中 i18n.global.setLocaleMessage(locale, messages.default) // 设置 locale i18n.global.locale.value = locale } catch (e) { console.error(`Failed to load locale ${locale}`, e) } }
loadLocaleMessages
函数主要做了以下几件事:- 使用
import()
函数动态加载语言包。 - 使用
i18n.global.setLocaleMessage
函数将语言包添加到i18n
实例中。 - 设置当前的
locale
。
- 使用
五、高级用法:更多姿势解锁i18n
-
日期和数字的格式化
vue-i18n
还提供了日期和数字的格式化功能。你可以使用d
函数和n
函数来格式化日期和数字。<template> <p>{{ $d(new Date(), 'short') }}</p> <p>{{ $n(1234567.89, 'currency') }}</p> </template>
你需要先定义一些格式:
// i18n.js import { createI18n } from 'vue-i18n' const i18n = createI18n({ locale: 'zh', fallbackLocale: 'en', messages: { en: { // ... }, zh: { // ... } }, datetimeFormats: { en: { short: { year: 'numeric', month: 'short', day: 'numeric' }, long: { year: 'numeric', month: 'long', day: 'numeric', weekday: 'long', hour: 'numeric', minute: 'numeric', second: 'numeric', timeZoneName: 'short' } }, zh: { short: { year: 'numeric', month: 'short', day: 'numeric' }, long: { year: 'numeric', month: 'long', day: 'numeric', weekday: 'long', hour: 'numeric', minute: 'numeric', second: 'numeric', timeZoneName: 'short' } } }, numberFormats: { en: { currency: { style: 'currency', currency: 'USD' } }, zh: { currency: { style: 'currency', currency: 'CNY' } } } }) export default i18n
-
组件的国际化
vue-i18n
还提供了组件的国际化功能。你可以使用<i18n>
组件来国际化组件。<template> <i18n path="message.hello"> <template #default="{ message }"> <p>{{ message }}</p> </template> </i18n> </template>
或者使用具名插槽传递参数:
<template> <i18n path="message.greeting"> <template #name="{ message }"> <p>{{ message }}</p> </template> </i18n> </template> <script> export default { data() { return { name: '老码农' } }, i18n: { messages: { en: { message: { greeting: 'Hello, {name}!' } }, zh: { message: { greeting: '你好,{name}!' } } }, // 传递参数 placeholders: { name: this.name } } } </script>
注意:组件的国际化需要在组件的
i18n
选项中定义语言包。 -
指令的国际化
vue-i18n
还提供了指令的国际化功能。你可以使用v-t
指令来国际化 DOM 元素。<template> <p v-t="'message.hello'"></p> </template>
六、总结
今天咱们聊了Vue 3的i18n
,从基本使用到动态加载语言包,再到扒源码,最后还介绍了一些高级用法。
希望通过今天的讲解,你能对vue-i18n
有一个更深入的了解,下次面试的时候,也能自信地说:“老子不仅会用,还会造!”
最后,记住一点:国际化不仅仅是翻译文本,更重要的是要考虑不同文化之间的差异,让你的应用更贴近用户的需求。
好了,今天的分享就到这里,感谢大家的收听!咱们下次再见!
表格总结:常用 API
API | 描述 |
---|---|
createI18n |
创建 i18n 实例,接收配置对象,例如 locale 、fallbackLocale 、messages 等。 |
$t(key, ...values) |
翻译文本,根据当前 locale 在语言包中查找对应的文本,并进行插值和格式化。 |
locale.value |
获取或设置当前的语言环境。 |
fallbackLocale.value |
获取或设置备用语言环境。 |
setLocaleMessage(locale, messages) |
动态设置某个 locale 的语言包,用于动态加载语言包。 |
$d(date, format) |
格式化日期,date 为 Date 对象,format 为预定义的格式名称或自定义格式。 |
$n(number, format) |
格式化数字,number 为数字,format 为预定义的格式名称或自定义格式。 |
<i18n> |
组件,用于在模板中进行国际化,可以在组件内部使用插槽传递参数。 |
v-t="key" |
指令,用于在 DOM 元素上进行国际化,key 为语言包中的键名。 |
useI18n() |
Hook,在 setup 函数中使用,返回 i18n 实例的常用方法和属性,例如 t 、locale 等。 |
补充说明:
vue-i18n
的配置项非常丰富,可以根据你的实际需求进行配置。- 动态加载语言包可以有效地减小应用体积,提高加载速度。
- 在进行国际化时,要充分考虑不同文化之间的差异,例如日期格式、数字格式、货币符号等。
- 可以使用一些工具来辅助进行国际化,例如 i18next、PhraseApp 等。
希望这些补充说明能对你有所帮助。