// 结构
{
"en": { // 'en' Locale
"key1": "this is message1", // 基本的 <p>{{ $t('key1') }}</p>
"nested": { // 嵌套 <p>{{ $t('nested.message1') }}</p>
"message1": "this is nested message1"
},
"errors": [ // 数组 <p>{{ $t('errors[0]') }}</p>
"this is 0 error code message",
{ // 数组嵌套对象 <p>{{ $t('errors[1].internal1') }}</p>
"internal1": "this is internal 1 error message"
},
[ // 数组嵌套数组 <p>{{ $t('errors[2][0]') }}</p>
"this is nested array error 1"
]
]
}
}
// 基于组件的本地化
const Component1 = {
template: `
<div class="container">
<p>Component1 locale messages: {{ $t("message.hello") }}</p>
</div>`,
i18n: { // `i18n` 选项,为组件设置语言环境信息
messages: {
en: { message: { hello: 'hello component1' } },
}
}
}
// 组件插值
const messages = {
en: {
term: 'I accept xxx {0}.'
}
}
<i18n path="term" tag="a" url="/123">
<a :href="url" target="_blank">插入到{0}中的a标签</a>
</i18n>
// 具名格式
const messages = {
en: {
info: 'You can {action} until {limit} minutes from departure.'
}
}
<i18n path="info" tag="p" :places="{ action:'插入到{action}中的字符串' }">
<span place="limit">插入到{limit}中</span>
</i18n>
// 具名格式
hello: '{msg} world'
<p>{{ $t('hello', { msg: 'hello' }) }}</p>
// 列表格式
hello: '{0} world'
<p>{{ $t('hello', ['hello']) }}</p>
<p>{{ $t('hello', {'0': 'hello'}) }}</p>
// HTML 格式化
hello: 'hello <br> world'
<p v-html="$t('hello')"></p>
// 复数($tc)
const messages = {
en: {
car: 'car | cars',
apple: 'no apples | one apple | {count} apples'
}
}
<p>{{ $tc('car', 1) }}</p> // <p>car</p>
<p>{{ $tc('car', 2) }}</p> // <p>cars</p>
<p>{{ $tc('apple', 0) }}</p> // <p>no apples</p>
<p>{{ $tc('apple', 1) }}</p> // <p>one apple</p>
<p>{{ $tc('apple', 10, { count: 10 }) }}</p> // <p>10 apples</p>
<p>{{ $tc('apple', 10) }}</p>
// 日期时间本地化($d),参数参考 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
const dateTimeFormats = {
'en-US': {
short: {
year: 'numeric', month: 'short', day: 'numeric'
}
}
}
const i18n = new VueI18n({
dateTimeFormats
})
<div >
<p>{{ $d(new Date(), 'short') }}</p>
<p>{{ $d(new Date(), 'long', 'ja-JP') }}</p>
</div>
// 数字本地化($n),参数请参考 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat
const numberFormats = {
'en-US': {
currency: {
style: 'currency', // 货币格式
currency: 'USD' // 在货币格式化中使用的货币符号
}
}
}
const i18n = new VueI18n({
numberFormats
})
<div >
<p>{{ $n(100, 'currency') }}</p>
<p>{{ $n(100, 'currency', 'ja-JP') }}</p>
</div>
// 链接到另一个翻译关键字(@:(...))
const messages = {
en: {
message: {
dio: 'DIO',
linked: 'There\'s a reason, you lost, @:(message.dio).'
}
}
}
- 自定义指令本地化
- 当 v-t 指令应用于 <transition> 组件内的元素时,你可能会注意到过渡动画之后的翻译过的信息会消失。这与 <transition> 组件实现的方式有关——在过渡开始之前 ,<transition> 组件内消失元素中的所有指令都将被销毁。此行为可能导致内容在短过渡时闪烁,但在长过渡时最明显。为了确保在转换期间指令内容不会被触及,只需将.preserve 修饰符添加到 v-t 指令定义中。
- 也可以在 VueI18n 实例本身设置全局设置,这将对没有修饰符的所有 v-t 指令产生影响。
preserveDirectiveContent: true
new Vue({
i18n: new VueI18n({
locale: 'en',
messages: {
en: { hello: 'hi {name} there!' },
ja: { hello: 'こんにちは!' }
}
})
}).$mount('#string-syntax')
// 字符串语法
<p v-t="'hello'"></p>
// 对象语法
<p v-t="{ path: 'hello', locale: 'ja', args: { name: 'kazupon' } }"></p>
- 自定义翻译(略,搜索:自定义格式)
- 自定义复数翻译(略,搜索:自定义复数)
- 回退本地化
- 回退是指当前语言没有对应的翻译,使用备选语言的翻译,设置 silentFallbackWarn:true 避免回退警告
- 回退插值
- 指$t()传入的字符串作为路径找不到对应翻译时已该字符串作为翻译,设置了formatFallbackMessages后会先查找fallbackLocale设置的备用语言得翻译,如果没有才会以$t传入的字符串作为模板
const i18n = new VueI18n({
locale: 'ru',
fallbackLocale: 'en',
formatFallbackMessages: true,
messages
})
<p>{{ $t('The weather today is {condition}!', { condition: 'sunny' }) }}</p>
- 当前组件中的i18n选项缺少的项(messages中的),会自动回退到根实例中的选项(new VueI18n传入的参数)。设置 silentFallbackWarn:true 关闭警告
- 如果你希望在组件语言环境中进行本地化,可以在 i18n 选项中用 sync: false 和 locale (为当前组件定义特殊语言)
- 函数式组件不支持 i18n 这个选项,可以在 parent 组件中定义供函数式组件使用
parent.$t
- 更改 VueI18n 实例的 locale 属性的值,动态更改语言环境。每个组件都包含一个引用为 $i18n 属性的 VueI18n 实例,该实例也可用于更改语言环境
const i18n = new VueI18n({
locale: 'ja', // 设置语言环境
...
})
i18n.locale = 'en'
- 可以使用具有多个 i18n 自定义块的语言环境信息
<i18n src="./common/locales.json"></i18n>
<i18n>
{
"en": {
"hello": "hello world!"
}
}
</i18n>
- 热重载,webpack热更新的一种优化,应该是能实现直接替换messages而不造成文件级别的替换
————————————————————————————————————————————————————————————————————————
http://kazupon.github.io/vue-i18n/zh/introduction.html
开始
安装
兼容性说明
直接下载 / CDN
NPM
npm install vue-i18n
Yarn
- 在一个模块系统中使用它,你必须通过 Vue.use() 明确地安装 vue-i18n
import Vue from 'vue'
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)
Vue Cli 3.x
- 注释:在@vue/cli创建的项目中添加i18n插件,vue需要全局安装,未全局安装的话可以通过vue ui来进行插件安装
- 注释:安装的插件会生成相应的文件和配置文件
vue add i18n
开发版构建
格式化
具名格式
const messages = {
en: {
message: {
hello: '{msg} world'
}
}
}
<p>{{ $t('message.hello', { msg: 'hello' }) }}</p>
列表格式
const messages = {
en: {
message: {
hello: '{0} world'
}
}
}
<p>{{ $t('message.hello', ['hello']) }}</p>
// 列表格式也接受类似数组的对象:
<p>{{ $t('message.hello', {'0': 'hello'}) }}</p>
HTML 格式化
const messages = {
en: {
message: {
hello: 'hello <br> world'
}
}
}
<p v-html="$t('message.hello')"></p>
支持 ruby on rails 的 i18n 格式
- 注释:ruby on rails 一个使用 Ruby 语言写的开源 Web 应用框架
const messages = {
en: {
message: {
hello: '%{msg} world'
}
}
}
<p>{{ $t('message.hello', { msg: 'hello' }) }}</p>
自定义格式
// 实现自定义格式
class CustomFormatter {
// 注释:options 为实例化时传入的参数
constructor (options) {
// ...
}
//
// 插值
// 注释:以下为 interpolate 函数接收到的两个参数的说明
// @param {string} 信息
// 列表或具名格式的字符串。
// 例如:
// - 具名格式:'Hi {name}'
// - 列表格式:'Hi {0}'
//
// @param {Object | Array} 值
// `message` 插值的值
// 使用 `$t`, `$tc` 和 `i18n` 函数式组件传递值。
// e.g.
// - $t('hello', { name: 'kazupon' }) -> 传递值:Object `{ name: 'kazupon' }`
// - $t('hello', ['kazupon']) -> 传递值:Array `['kazupon']`
// - `i18n` 函数式组件 (组件插值)
// <i18n path="hello">
// <p>kazupon</p>
// <p>how are you?</p>
// </i18n>
// -> 传递值:Array (included VNode):
// `[VNode{ tag: 'p', text: 'kazupon', ...}, VNode{ tag: 'p', text: 'how are you?', ...}]`
//
// @return {Array<any>}
// 插值,你需要返回以下内容:
// - 当使用 `$t` 或 `$tc` 数组中应该是字符串。
// - 当使用 `i18n` 函数式组件时 数组中应包含 VNode 对象。
//
interpolate (message, values) {
// 在这里实现插值逻辑
// ...
// 返回插值数组
return ['resolved message string']
}
}
// 注册 `formatter` 选项
const i18n = new VueI18n({
locale: 'en-US',
formatter: new CustomFormatter(/* 这里是构造函数选项 */),
messages: {
'en-US': {
// ...
},
// ...
}
})
// 启动!
new Vue({ i18n }).$mount('#app')
复数
const messages = {
en: {
car: 'car | cars',
apple: 'no apples | one apple | {count} apples'
}
}
<p>{{ $tc('car', 1) }}</p> // <p>car</p>
<p>{{ $tc('car', 2) }}</p> // <p>cars</p>
<p>{{ $tc('apple', 0) }}</p> // <p>no apples</p>
<p>{{ $tc('apple', 1) }}</p> // <p>one apple</p>
<p>{{ $tc('apple', 10, { count: 10 }) }}</p> // <p>10 apples</p>
// 注释:等价于,应该是自动作为参数,一种便捷的绑定方式
// 注释:参数2为字符串时会被判定为 0
<p>{{ $tc('apple', 10) }}</p>
- 注释:参数2传入VueI18n.prototype.getChoiceIndex方法进行判断
- 注释:默认规律为
- choicesLength === 2 且 choice <= 1 时才返回 0 否则返回 1
- 其他:当 choice > 2 时返回 2,其他时候按照 choice 返回
VueI18n.prototype.getChoiceIndex = function getChoiceIndex (choice, choicesLength) {
// Default (old) getChoiceIndex implementation - english-compatible
var defaultImpl = function (_choice, _choicesLength) {
_choice = Math.abs(_choice);
if (_choicesLength === 2) {
return _choice
? _choice > 1
? 1
: 0
: 1
}
return _choice ? Math.min(_choice, 2) : 0
};
if (this.locale in this.pluralizationRules) {
return this.pluralizationRules[this.locale].apply(this, [choice, choicesLength])
} else {
return defaultImpl(choice, choicesLength)
}
};
通过预定义的参数访问该数字
自定义复数
- 为了实现不同规则,你可以覆盖 VueI18n.prototype.getChoiceIndex 函数(斯拉夫语言具有不同的复数规则)
/**
* @param choice {number} 由 $tc 输入的选择索引:`$tc('path.to.rule', choiceIndex)`
* @param choicesLength {number} 总体可用选择 //注释:message中 | 分割的长度
* @returns 选择复数单词的最终选择索引
**/
VueI18n.prototype.getChoiceIndex = function (choice, choicesLength) {
// this === VueI18n 实例,所有语言环境属性也存在于此处
if (this.locale !== 'ru') {
// 继续执行默认实现
}
if (choice === 0) {
return 0;
}
const teen = choice > 10 && choice < 20;
const endsWithOne = choice % 10 === 1;
if (!teen && endsWithOne) {
return 1;
}
if (!teen && choice % 10 >= 2 && choice % 10 <= 4) {
return 2;
}
return (choicesLength < 4) ? 2 : 3;
}
日期时间本地化
const dateTimeFormats = {
'en-US': {
short: {
year: 'numeric', month: 'short', day: 'numeric'
},
long: {
year: 'numeric', month: 'short', day: 'numeric',
weekday: 'short', hour: 'numeric', minute: 'numeric'
}
},
'ja-JP': {
short: {
year: 'numeric', month: 'short', day: 'numeric'
},
long: {
year: 'numeric', month: 'short', day: 'numeric',
weekday: 'short', hour: 'numeric', minute: 'numeric', hour12: true
}
}
}
const i18n = new VueI18n({
dateTimeFormats
})
<div >
<p>{{ $d(new Date(), 'short') }}</p>
<p>{{ $d(new Date(), 'long', 'ja-JP') }}</p>
</div>
数字本地化
const numberFormats = {
'en-US': {
currency: {
style: 'currency', // 货币格式
currency: 'USD' // 在货币格式化中使用的货币符号
}
},
'ja-JP': {
currency: {
style: 'currency', currency: 'JPY', currencyDisplay: 'symbol'
}
}
}
const i18n = new VueI18n({
numberFormats
})
<div >
<p>{{ $n(100, 'currency') }}</p>
<p>{{ $n(100, 'currency', 'ja-JP') }}</p>
</div>
语言环境信息的语法
结构
{
"en": { // 'en' Locale
"key1": "this is message1", // 基本的
"nested": { // 嵌套
"message1": "this is nested message1"
},
"errors": [ // 数组
"this is 0 error code message",
{ // 数组嵌套对象
"internal1": "this is internal 1 error message"
},
[ // 数组嵌套数组
"this is nested array error 1"
]
]
},
"ja": { // 'ja' Locale
// ...
}
}
<div >
<!-- 基本的 -->
<p>{{ $t('key1') }}</p>
<!-- 嵌套 -->
<p>{{ $t('nested.message1') }}</p>
<!-- 数组 -->
<p>{{ $t('errors[0]') }}</p>
<!-- 数组嵌套对象 -->
<p>{{ $t('errors[1].internal1') }}</p>
<!-- 数组嵌套数组 -->
<p>{{ $t('errors[2][0]') }}</p>
</div>
Linked locale messages
- 如果有一个翻译关键字总是与另一个具有相同的具体文本,你可以链接到它。要链接到另一个翻译关键字,你所要做的就是在其内容前加上一个 @: 符号后跟完整的翻译键名,包括你要链接到的命名空间。
- 注释:用来拼接多个翻译关键字,不需要结束标志,直接跟字符串会自动作为字符串添加到变量后
const messages = {
en: {
message: {
the_world: 'the world',
dio: 'DIO:',
linked: '@:message.dio @:message.the_world我是文本'
}
}
}
<p>{{ $t('message.linked') }}</p> // <p>DIO: the world我是文本</p>
按括号分组
- 如果链接 @:message.something 后紧跟着一个点 . 会被认为成了链接的一部分。这时链接到另一段翻译的键名应该在括号 () 里
const messages = {
en: {
message: {
dio: 'DIO',
linked: 'There\'s a reason, you lost, @:(message.dio).'
}
}
}
回退本地化
- 默认情况下回退到 fallbackLocale 会产生两个控制台警告
- 注释:回退是指当前语言没有对应的翻译,使用备选语言的翻译
- 为了避免这些警告 (同时保留那些完全没有翻译给定关键字的警告),需初始化 VueI18n 实例时设置 silentFallbackWarn:true
回退插值
- 如果在message中找不到相应的键值将回退到原本的语言
- 注释:原本的语言是指$t()传入的字符串回退作为翻译的值
- 为了实现此功能,可以通过设置formatFallbackMessages为true
- 注意: fallbackRoot的优先级高于formatFallbackMessages
- 注释:回退本地化优先于回退插值,即设置了formatFallbackMessages后会先查找fallbackLocale设置的备用语言得翻译,如果没有才会以$t传入的字符串作为模板
const messages = {
ru: {
'Hello {name}': 'Здравствуйте {name}'
}
}
const i18n = new VueI18n({
locale: 'ru',
fallbackLocale: 'en',
formatFallbackMessages: true,
messages
})
<p>{{ $t('Hello {name}', { name: 'John' }}) }}</p>
<p>{{ $t('The weather today is {condition}!', { condition: 'sunny' }) }}</p>
基于组件的本地化
- 注释:当前组件中的i18n选项缺少的项(包括messages中的,也包括locale等),会自动回退到根实例中的选项(new VueI18n传入的参数)
- 回退到根语言环境会在控制台中生成两个警告,为避免以上警告 (同时保留那些完全没有翻译给定关键字的警告) 需初始化 VueI18n 实例时设置 silentFallbackWarn:true(同回退本地化)
- 如果你希望在组件语言环境中进行本地化,可以在 i18n 选项中用 sync: false 和 locale
const Component1 = {
template: `
<div class="container">
<p>Component1 locale messages: {{ $t("message.hello") }}</p>
<p>Fallback global locale messages: {{ $t("message.greeting") }}</p>
</div>`,
i18n: { // `i18n` 选项,为组件设置语言环境信息
messages: {
en: { message: { hello: 'hello component1' } },
ja: { message: { hello: 'こんにちは、component1' } }
}
}
}
函数式组件的翻译
- 在函数式组件上使用 vue-i18n 时,你必须将 $t 称为 parent.$t
- 注释:函数式组件也不支持 i18n 这个选项,可以在 parent 组件中定义供函数式组件使用
{
functional: true,
render(h, context) {
return <div>{context.parent.$t("czb")}</div>;
}
}
自定义指令本地化
- 7.3 新增
- 不仅可以使用 $t 方法进行翻译,还可以使用 v-t 自定义指令
字符串语法
- 注释:注意v-t绑定的是字符串,依然可以使用基于组件的本地化
new Vue({
i18n: new VueI18n({
locale: 'en',
messages: {
en: { hello: 'hi there!' },
ja: { hello: 'こんにちは!' }
}
})
}).$mount('#string-syntax')
<div >
<!-- 字符串 -->
<p v-t="'hello'"></p>
</div>
对象语法
<p v-t="{ path: 'hello', locale: 'ja', args: { name: 'kazupon' } }"></p>
<p v-t="{ path: 'hello', args: { name: nickName } }"></p>
使用翻译
- 8.7 新增
- 当 v-t 指令应用于 <transition> 组件内的元素时,你可能会注意到过渡动画之后的翻译过的信息会消失。这与 <transition> 组件实现的方式有关——在过渡开始之前 ,<transition> 组件内消失元素中的所有指令都将被销毁。此行为可能导致内容在短过渡时闪烁,但在长过渡时最明显。为了确保在转换期间指令内容不会被触及,只需将.preserve 修饰符添加到 v-t 指令定义中。
- 疑问:指令销毁是否指改变创建当前实例的参数?
- 也可以在 VueI18n 实例本身设置全局设置,这将对没有修饰符的所有 v-t 指令产生影响。
new Vue({
i18n: new VueI18n({
locale: 'en',
messages: {
en: { preserve: 'with preserve' },
},
preserveDirectiveContent: true
})
}).$mount('#in-transitions')
$t vs v-t
- $t 在每次重新渲染时都会被执行,因此它确实有翻译成本。
- v-t 比 $t 方法具有更好的性能,因为在一次翻译时自定义指令会进行缓存。
- 疑问:自定义指令进行缓存如何实现的?通过判断传入参数是否改变来决定是否修改对应的vnode对象?
- 此外可以使用由 vue-i18n-extensions 提供的 Vue 编译器模块进行预翻译。
- 注释:vue-i18n-extensions用来实现服务端渲染,参考https://github.com/intlify/vue-i18n-extensions
- 注释:i18n下的message整个对象都会被打包到,不会根据当前语言舍弃不需要语言块
- 带有 v-t 的翻译内容会被插入到元素的 textContent 中(textContent是原生dom的一个属性,指向它的文本内容)
- 当你使用服务器渲染时,你需要设置自定义指令到 createRenderer 函数的 directives 选项
- 疑问:v-t已经被注册到vue中,为什么服务端渲染要设置自定义指令呢?
组件插值
基本用法
- 7.0 新增
- 注释:类似vue的插槽,path翻译的key,tag当前i18n标签渲染的使用的dom名,for 绑定到该dom上的其他 attribute
<p>I accept xxx <a href="/term">Terms of Service Agreement</a></p>
const messages = {
en: {
tos: 'Term of Service',
term: 'I accept xxx {0}.'
},
ja: {
tos: '利用規約',
term: '私は xxx の{0}に同意します。'
}
}
// 把 a 标签插入term的{0}插槽中
<div >
<!-- ... -->
<i18n path="term" tag="label" for="tos">
<a :href="url" target="_blank">{{ $t('tos') }}</a>
</i18n>
<!-- ... -->
</div>
高级用法
- 7.2 新增
- 在 i18n 组件中,仅包含空格的文本内容将被省略
- 在 place 特性的帮助下支持具名格式
- 注释:存在i18n组件实例
<div >
<!-- ... -->
<i18n path="info" tag="p">
<span place="limit">{{ changeLimit }}</span>
<a place="action" :href="changeUrl">{{ $t('change') }}</a>
</i18n>
<!-- ... -->
</div>
const messages = {
en: {
info: 'You can {action} until {limit} minutes from departure.',
change: 'change your flight'
}
}
- i18n 组件的所有子项都必须设置 place 属性。否则它将回退到列表格式
- 注释:列表格式会自动舍去插入元素多余的部分,插入并不存在的位置时该插入元素也会被舍弃
- 注释:不存在插入位都会被渲染为空字符串,当被回退为列表格式时也一样
- 如果你仍想在命名格式中插入文本内容,可以在 i18n 组件上定义 places 属性。
<div >
<!-- ... -->
<i18n path="info" tag="p" :places="{ limit: refundLimit }">
<a place="action" :href="refundUrl">{{ $t('refund') }}</a>
</i18n>
<!-- ... -->
</div>
{
data(){
return {
refundLimit:"refundLimitText",
refundUrl:"/refundUrl"
}
},
i18n:{
messages:{
en: {
info: 'You can {action} until {limit} minutes from departure.',
refund: 'refund the ticket'
}
}
}
}
单文件组件
基本用法
安装 vue-i18n-loader
- 为了使用 <i18n> 自定义块,你需要安装 vue-loader 和 vue-i18n-loader。如果你使用了单文件组件,vue-loader 很可能已在项目中使用了,那么 vue-i18n-loader 必须另外安装
npm i --save-dev @kazupon/vue-i18n-loader
Webpack
- 需要对 Webpack 进行以下配置,对于 vue-loader v15 或更高版本
- 疑问:对模块进行处理时rules是顺序执行的吗?
module.exports = {
// ...
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
},
{
resourceQuery: /blockType=i18n/,
type: 'javascript/auto',
loader: '@kazupon/vue-i18n-loader'
}
// ...
]
},
// ...
}
module.exports = {
// ...
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
// 你需要指定 `i18n` 的值为 `vue-i18n-loader`
i18n: '@kazupon/vue-i18n-loader'
}
}
},
// ...
]
},
// ...
}
Vue CLI 3.0
- Vue CLI 3.0 隐藏了 webpack 配置,因此须在项目的根目录下创建一个 vue.config.js
- 对于 vue-loader v15 或更高版本
module.exports = {
chainWebpack: config => {
config.module
.rule("i18n")
.resourceQuery(/blockType=i18n/)
.type('javascript/auto')
.use("i18n")
.loader("@kazupon/vue-i18n-loader")
.end();
}
}
- 对于 vue-loader v14
- 注释:需要 deepmerge 库用于对象合并
const merge = require('deepmerge')
module.exports = {
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.tap(options =>
merge(options, {
loaders: {
i18n: '@kazupon/vue-i18n-loader'
}
})
)
}
}
Laravel-Mix(略)
加载 YAML
- i18n 自定义块需要指定为 JSON 格式,你也可以通过使用 vue-loader 预加载器功能来使用 YAML 格式。
- 以下是 YAML 格式的 i18n 自定义块:
<i18n>
en:
hello: "hello world!"
ja:
hello: "こんにちは、世界!"
</i18n>
多个自定义块
- 可以使用具有多个 i18n 自定义块的语言环境信息。
- 这些语言环境信息将合并为组件的语言环境信息
<i18n src="./common/locales.json"></i18n>
<i18n>
{
"en": {
"hello": "hello world!"
},
"ja": {
"hello": "こんにちは、世界!"
}
}
</i18n>
Scoped 风格
- 当使用带有 scoped style vue-i18n 时,重要的是要记住使用深度选择器 来设置嵌套转换的样式。
- 疑问:并不存在这样的限制,这里的文档好像有问题
...
<template>
<div class="parent">
<p>message: {{ $t('hello') }}</p>
</div>
</template>
...
<!-- 不可行-->
<style scoped>
.parent p {
color: #42b883;
}
</style>
<!-- 可行 -->
<style>
.parent >>> p {
color: #42b883;
}
</style>
函数式组件中的自定义块
- 以下代码无法使用 i18n 自定义块的语言环境信息进行本地化
<i18n>
{
"en": {
"hello": "hello world"
},
"ja": {
"hello": "こんにちは、世界"
}
}
</i18n>
<template functional>
<!-- 'hello' 的父实例的语言环境信息 -->
<p>{{ parent.$t('hello') }}</p>
</template>
热重载
- 可以监视本地化文件中的更改,并将更改热重载到应用程序中
- 疑问:webpack 属于静态打包,热更新本身就会观察所有加载的模块,使用这种方式是为了提高效率吗?防止文件重新打包,而只是通过setLocaleMessage替换数据?
// 语言环境信息
const messages = {
// ...
}
// VueI18n 实例
const i18n = new Vuei18n({
locale: 'en',
messages
})
// 运行程序
const app = new Vue({
i18n,
// ...
}).$mount('#app')
// 热更新
if (module.hot) {
module.hot.accept(['./en', './ja'], function () {
i18n.setLocaleMessage('en', require('./en').default)
i18n.setLocaleMessage('ja', require('./ja').default)
// 同样可以通过 $i18n 属性进行热更新
// app.$i18n.setLocaleMessage('en', require('./en').default)
// app.$i18n.setLocaleMessage('ja', require('./ja').default)
})
}
语言环境变更
- 通常,使用 Vue 根实例作为起点,使用 VueI18n 类的 locale 属性作为参考来本地化所有子组件
- 可以更改 VueI18n 实例的 locale 属性的值,动态更改语言环境
const i18n = new VueI18n({
locale: 'ja', // 设置语言环境
...
})
i18n.locale = 'en'
- 每个组件都包含一个引用为 $i18n 属性的 VueI18n 实例,该实例也可用于更改语言环境
<template>
<div class="locale-changer">
<select v-model="$i18n.locale">
<option v-for="(lang, i) in langs" :key="`Lang${i}`" :value="lang">{{ lang }}</option>
</select>
</div>
</template>
<script>
export default {
name: 'locale-changer',
data () {
return { langs: ['ja', 'en'] }
}
}
</script>
延迟加载翻译
//i18n-setup.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import messages from '@/lang/en'
import axios from 'axios'
Vue.use(VueI18n)
export const i18n = new VueI18n({
locale: 'en', // 设置语言环境
fallbackLocale: 'en',
messages // 设置语言环境信息
})
const loadedLanguages = ['en'] // 我们的预装默认语言
function setI18nLanguage (lang) {
i18n.locale = lang
axios.defaults.headers.common['Accept-Language'] = lang
document.querySelector('html').setAttribute('lang', lang)
return lang
}
export function loadLanguageAsync (lang) {
if (i18n.locale !== lang) {
if (!loadedLanguages.includes(lang)) {
return import(/* webpackChunkName: "lang-[request]" */ `@/lang/${lang}`).then(msgs => {
i18n.setLocaleMessage(lang, msgs.default)
loadedLanguages.push(lang)
return setI18nLanguage(lang)
})
}
return Promise.resolve(setI18nLanguage(lang))
}
return Promise.resolve(lang)
}
工具
官方工具
Vue Cli 插件
Webpack Loader
ESLint 插件
- eslint-plugin-vue-i18n 是为 Vue I18n 编写的 ESLint 插件。
- 疑问:该插件官方页面已经404,是否已经没有存在的意义了?通过vue-cli-plugin-i18n安装的i18n并不支持格式化i18n自定义块,是否有其他解决方案。
Extensions
第三方工具
BabelEdit