v9 破壊的変更
Vue I18n v9(Vue 3用)で提供されるAPIの多くは、v8(Vue 2用)からの移行を容易にするために互換性を維持するよう努めています。しかし、アプリケーションを移行する際に遭遇する可能性のある破壊的変更がいくつかあります。このガイドは、アプリケーションをVue I18n v9で動作するように適応させる方法について説明します。
API
new VueI18n が createI18n になりました
Vue I18nはクラスではなく、一連の関数になりました。new VueI18n()と書く代わりに、createI18nを呼び出す必要があります。
Vue I18n v8.x:
import Vue from 'vue'
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)
const i18n = new VueI18n({
// ...
})
new Vue({
i18n,
// ...
})Vue I18n v9 以降:
import { createApp } from 'vue'
import { createI18n } from 'vue-i18n'
const i18n = createI18n({
// ...
})
const app = createApp({
// ...
})
app.use(i18n)理由: Vue 3のグローバルAPIの変更、およびコンポーネントインスタンスに関連するVue 3のAPIアーキテクチャの変更のため。
dateTimeFormats から datetimeFormats へのリネーム
Vue I18n v8.x:
const i18n = new VueI18n({
// ...
dateTimeFormats: {
// ...
}
})Vue I18n v9 以降:
const i18n = createI18n({
// ...
datetimeFormats: {
// ...
}
})翻訳APIの戻り値
$tやtのような翻訳API関数は文字列のみを返します。オブジェクトや配列の値は返されなくなりました。
Vue I18n v8.x:
// 例: 配列構造のロケールメッセージ
const i18n = new VueI18n({
messages: {
en: {
errors: [
'invalid argument',
// ...
'unexpected errors'
]
}
}
})
// 例: エラーコンポーネント
const ErrorMessage = {
props: {
code: {
type: Number,
required: true
}
},
template: `<p class="error">{{ getErrorMessage(code) }}</p>`,
methods: {
getErrorMessage(code) {
return this.$t('errors')[code]
}
}
}Vue I18n v9以降では、$tm / tmでロケールメッセージを取得し、$rtまたはrtでロケールメッセージを解決するように変更されました。以下のComposition APIの例をご覧ください。
// 例: 配列構造のロケールメッセージ
const i18n = createI18n({
messages: {
en: {
errors: [
'invalid argument',
// ...
'unexpected errors'
]
}
}
})
// 例: エラーコンポーネント
const ErrorMessage = {
props: {
code: {
type: Number,
required: true
}
},
template: `<p class="error">{{ errors(code) }}</p>`,
setup() {
const { tm, rt } = useI18n()
const errors = (code) => rt(tm('errors')[code])
return { errors }
}
}理由: 翻訳結果を返す義務を単純化するため、またTypeScriptの型をサポートするため。
複数形APIの戻り値
翻訳APIの戻り値と同様に、$tcやtcのような複数形API関数は文字列のみを返します。オブジェクトや配列の値は返されなくなりました。
理由: 翻訳結果を返す義務を単純化するため、またTypeScriptの型をサポートするため。
getChoiceIndex の削除
CAUTION
getChoiceIndexオプションの実装コードはv10で完全に削除される予定です。
複数形ルールをカスタマイズするために、Vue I18n v8.xではVueI18nクラスのgetChoiceIndexを拡張していました。
Vue I18n v8.x:
VueI18n.prototype.getChoiceIndex = function (choice, choicesLength) {
// this === VueI18n インスタンスなので、locale プロパティが存在します
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;
}Vue I18n v9以降では、以下のオプションでカスタマイズできます。
Legacy APIモード:
import { createI18n } from 'vue-i18n'
function customRule(choice, choicesLength, orgRule) {
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 i18n = createI18n({
// ...
pluralizationRules: {
ru: customRule,
// ...
},
// ...
})Composition APIモード:
import { useI18n } from 'vue-i18n'
function customRule(choice, choicesLength, orgRule) {
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 MyComp = {
setup() {
const { t } = useI18n({
// ...
pluralRules: {
ru: customRule,
// ...
},
// ...
})
// ...
}
}理由: VueI18nクラスが削除されたため。
warnHtmlInMessage オプションのデフォルト値の変更
Vue I18n v8.xでは、warnHtmlInMessageの値は"off"でした。そのため、デフォルトでは、メッセージにHTMLが含まれていてもコンソールに警告は出力されません。
Vue I18n v9以降では、デフォルト値が以下のように変更されました。
- Legacy APIモード:
warnHtmlInMessageプロパティ:"warn" - Composition APIモード:
warnHtmlMessageboolean プロパティ、デフォルトtrue
開発モードでは、この値を変更しない限り、デフォルトでコンソールに警告が表示されます。
本番モードでは、パフォーマンスを最大化するために、メッセージにHTMLが含まれているかどうかを検出しません。
理由: ロケールメッセージのセキュリティを強化するため。
バージョン情報
バージョン情報は、VueI18nクラスの静的プロパティではなく、インポート構文でアクセスできるようになりました。
Vue I18n v8.x:
import VueI18n from 'vue-i18n'
console.log(VueI18n.version)Vue I18n v9 以降:
import { VERSION } from 'vue-i18n'
console.log(VERSION)理由: Tree shakingの最適化、およびVueI18nクラスの削除のため。
Intl availability の削除
主要なブラウザがECMAScript Internationalization APIをサポートするようになったため、削除されました。
Vue I18n v8.x:
import VueI18n from 'vue-i18n'
console.log(VueI18n.availability)理由: IE9のサポート終了、およびVueI18nクラスの削除のため。
Custom formatter の削除
CAUTION
formatterオプションの実装コードはv10で完全に削除される予定です。 代替として、vue-i18nは実験的機能としてカスタムメッセージフォーマットを持っています。
理由: 新しいコンパイラとランタイムAPIでカスタムフォーマットを提供することが困難なため。これらのAPIでサポートするために、次のメジャーバージョンでサポートする予定です。ICUメッセージフォーマットを使用したい場合は、@formatjs/vue-intlを使用できます。
preserveDirectiveContent オプションの削除
CAUTION
preserveDirectiveContentオプションの実装コードはv10で完全に削除される予定です。
Vue 3用のv-tディレクティブは、デフォルトのコンテンツを保持するようになりました。したがって、このオプションとそのプロパティはVueI18nインスタンスから削除されました。
Vue I18n v8.x:
import VueI18n from 'vue-i18n'
const i18n = new VueI18n({
// ...
preserveDirectiveContent: true,
// ...
})理由: v-tディレクティブを持つ要素のコンテンツを維持するため。
メッセージフォーマット構文
リスト補間のための配列風オブジェクトの削除
Vue I18n v8.xでは、リスト補間は次のような配列風オブジェクトをパラメータとして使用できました。
import VueI18n from 'vue-i18n'
const i18n = new VueI18n({
// ...
messages: {
en: {
greeting: 'hello, {0}!'
}
},
// ...
})<p>{{ $t('greeting', { '0': 'kazupon' }) }}</p>Vue I18n v9以降では、リスト補間に配列風オブジェクトを使用することはできず、配列を使用する必要があります。
<p>{{ $t('greeting', ['kazupon']) }}</p>理由: 一貫した引数インターフェースを持つ翻訳APIを提供するため。
特殊文字の処理
Vue I18nで翻訳できるメッセージは、次のようなメッセージフォーマット構文を使用して高度に翻訳できます。
@.caml:{'no apples'} | {0} apple | {n} applesメッセージフォーマット構文は、以下の特殊文字を使用して表現できます。
{,},@,$,|
Vue I18n v9以降、メッセージフォーマット構文はメッセージフォーマットコンパイラによって処理されるようになりました。メッセージの一部としてこれらの特殊文字を使用すると、コンパイル時にエラーが発生します。これらの特殊文字を使用したい場合は、リテラル補間を使用する必要があります。
// 例: メールアドレスでの `@` のユースケース
{emailIdentity}{'@'}{emailDomain}理由: メッセージフォーマットコンパイラがメッセージフォーマット構文で使用される特殊文字を正しく処理するためには、それらを制限する必要があります。
リンクメッセージの括弧によるグループ化の削除
Vue I18n v8.xでは、リンクメッセージ内のキー参照と、括弧 () を使用したメッセージの区別が行われていました。
Vue I18n v8.x:
const messages = {
en: {
message: {
dio: 'DIO',
linked: 'There\'s a reason, you lost, @:(message.dio).'
}
}
}Vue I18n v9以降では、メッセージフォーマットコンパイラが名前付き補間、リスト補間、リテラル補間を処理できるため、括弧は不要になりました。
Vue I18n v9 以降:
const messages = {
en: {
message: {
dio: 'DIO',
linked: "There's a reason, you lost, @:{'message.dio'}."
}
}
}v-t ディレクティブ
preserve 修飾子の削除
CAUTION
preserve修飾子の実装コードはv10で完全に削除される予定です。
preserveDirectiveContent オプションの削除と同様に、Vue 3用のv-tディレクティブはデフォルトのコンテンツを保持するようになりました。したがって、preserve修飾子とその関連機能はv-tディレクティブから削除されました。
Vue I18n v8.x:
<p v-t.preserve="'hello'"></p>理由: v-tディレクティブを持つ要素のコンテンツを維持するため。
翻訳コンポーネント
i18n から i18n-t へのリネーム
翻訳コンポーネント(Vue I18n v8.xでは i18n関数型コンポーネント と呼ばれていました)のタグ名が変更されました。
Vue I18n v8.x:
<i18n path="message.greeting" />Vue I18n v9 以降:
<i18n-t keypath="message.greeting" />理由: タグ名がi18nカスタムブロック <i18n> と同じであるため、ブロック名と混同しやすく、SFCで間違いを犯しやすいため。
tag prop は任意
Vue I18n v8.xでは、タグ名とBoolean値 false を指定することで、ルート要素なしで子要素をレンダリングするために tag prop を使用できました。
Vue I18n v8.x:
<i18n :tag="false" path="message.greeting">
<span>hello!</span>
</i18n>Vue I18n v9以降では、tag propを省略することで同じことができます。
Vue I18n v9 以降:
<i18n-t keypath="message.greeting">
<span>hello!</span>
</i18n-t>理由: Vue 3がFragmentsをサポートするようになったため。
path prop から keypath prop へのリネーム
Vue I18n v8.x:
<i18n path="message.greeting" />Vue I18n v9 以降:
<i18n-t keypath="message.greeting" />place 属性と places prop を使用した place 構文の削除
Vue I18n v9以降では、place属性とplaces propはコンポーネント補間から削除されました。
Vue I18n v8.x:
<i18n path="info" tag="p" :places="{ limit: refundLimit }">
<span place="limit">{{ refundLimit }}</span>
<a place="action" :href="refundUrl">{{ $t('refund') }}</a>
</i18n>Vue I18n v9 以降:
<i18n-t keypath="info" tag="p">
<template #limit>
<span>{{ refundLimit }}</span>
<template>
<template #action>
<a :href="refundUrl">{{ $t('refund') }}</a>
<template>
</i18n-t>理由: slotsを使って同じことができるため。
NumberFormat コンポーネント
tag prop は任意
翻訳コンポーネントのセクションと同様に、NumberFormatコンポーネント(Vue I18n v8.xでは i18n-n関数型コンポーネント と呼ばれていました)では、タグ名とBoolean値 false を指定することで、ルート要素なしで子要素をレンダリングするために tag prop を使用できました。
Vue I18n v8.x:
<i18n-n :tag="false" :value="100" format="currency">
<span v-slot:integer="slotProps" styles="font-weight: bold">{{ slotProps.integer }}</span>
</i18n-n>Vue I18n v9以降では、tag propを省略することで同じことができます。
Vue I18n v9 以降:
<i18n-n :value="100" format="currency">
<span v-slot:integer="slotProps" styles="font-weight: bold">{{ slotProps.integer }}</span>
</i18n-n>理由: Vue 3がFragmentsをサポートするようになったため。