🌏

Nuxt3+Vuetify3で多言語化(i18n)

2023/03/04に公開

vue-i18nというパッケージを使用します。
nuxt/i18nというのもあり、そっちで苦戦した方などはこちらを試してみるのが良いかと。

基本は公式ページの説明の通りです。
実用的な範囲の最小構成でまとめ直したのと、少しばかり書き方の補足をしたものです。

インストール

npm install --save-dev vue-i18n

/plugins/i18n.tsにファイル作成

pluginsフォルダがなければ作成し、その下に以下の内容のi18n.tsを作成。

i18n.ts
import { createI18n } from 'vue-i18n'
import en from '../locales/en.json'
import ja from '../locales/ja.json'

export default defineNuxtPlugin(({ vueApp }) => {
  const i18n = createI18n({
    legacy: false,
    globalInjection: true,
    locale: 'en',
    messages: {
      en,
      ja
    }
  })

  vueApp.use(i18n)
})

/locales/en.json/locales/ja.jsonを作成

localesフォルダを作成し、各言語ごとに翻訳リストを作成。

en.json
{
  "hello": "Hello, {name}!",
  "delte": "Delete"
}
ja.json
{
  "hello": "こんにちは, {name}!",
  "delte": "削除"
}
  • リストの最後にカンマが付いているとエラーになるので注意

Vue (Vuetify3)での書き方の例

<v-alert
    :title="$t('cofirm_delete_account')"
    type="error"
    variant="text"
>
    {{ $t('description_delete_account') }}
</v-alert>

言語切り替え (Vuetify3)

v-selectで実装した例です。

app.vue
<script setup>
  const languages = [
    { title: 'English', value: 'en' },
    { title: '日本語', value: 'ja' },
  ];
</script>

<v-select
  v-model="$i18n.locale"
  :items="languages"
  item-title="title"
  item-value="value"
  single-line
></v-select>

URLのパラメータで振り分け

公式ページによると、URLによるルーティングはNuxt3インテグレーションではまだ対応していないそうです。(正確にはチュートリアルに記載されていないだけとも読めますが。)

route.queryを使う

URLで指定した言語で出力する例です。

https://your_url.com/?lang=ja

という感じで言語を指定した場合に、日本語で出力されるようにするには、plugins/i18n.tsの中で、const route = useRoute()を追加し、localeroute.query.langを代入してやります。

全体のコードは以下のようになります。

i18n.ts
import { createI18n } from 'vue-i18n'
import en from '../locales/en.json'
import ja from '../locales/ja.json'

export default defineNuxtPlugin(({ vueApp }) => {
  const route = useRoute();
  const i18n = createI18n({
    legacy: false,
    globalInjection: true,
    locale: route.query.lang ?? "en",
    messages: {
      en,
      ja
    }
  })

  vueApp.use(i18n)
})

動的ルーティングを使う

上述の方法で一応ルーティングはできますが、URLに?lang=jaが付くのはなんとも格好がよくありません。
まったくエレガントではない方法ですが、https://your_url.com/jaみたいな分け方は、動的ルーティングで実現することができるので、書いておきます。

index.vueを言語分けしたいとして、同じ階層のディレクトリで、[lang].vueというのを作ります。中身はindex.vueをコピペして作ります。

-pages
   - index.vue
   - [lang].vue

こうしておいて、上述のts:i18n.tsの中でroute.query.langroute.params.langにします:

i18n.ts
import { createI18n } from 'vue-i18n'
import en from '../locales/en.json'
import ja from '../locales/ja.json'

export default defineNuxtPlugin(({ vueApp }) => {
  const route = useRoute();
  const i18n = createI18n({
    legacy: false,
    globalInjection: true,
    locale: route.params.lang ?? "en",
    messages: {
      en,
      ja
    }
  })

  vueApp.use(i18n)
})

すると、https://your_url.com/jaとすることで$i18n.localejaが入ります。

「これって単にファイル複製しているだけだから多言語化じゃないじゃん」と不満が聞こえてきそうです。確かにほとんどそんな感じです。ただ、メリットを言うならば

  • コピペするファイルは、index.vueに対応するようなファイルなので、その下の階層のファイルを逐一コピーする必要はない
  • 一応i18nを使っているのでlocaleで管理する"スッキリさ"はある
  • 他のページに遷移しても、$i18n.localeの情報は引き継がれるので、サイト内で違うところに移っても言語設定が引き継がれる

などがあります。

nuxt/i18nを使えば良いのでは?

冒頭で触れたように、vue-i18n以外にnuxt/i18nというのもあります。
ただ、私の場合、こちらはどうしてもエラーが出て動かない箇所があったので(サンプルコードは動くので、何かパッケージが競合しているか何か...)、vue-i18nを採用しました。

それでは。頑張りましょう。

Discussion