Lingvit
  • Nuxt.js
  • i18n
  • ガイド

Nuxt.js の国際化:@nuxtjs/i18n vs 自動翻訳(2026年版)

読了時間8分

Nuxt.js が多言語サイトを構築する方法は大きく2つあります。Vue Router、ロケールファイル、スラッグ ルーティングを結びつける公式の@nuxtjs/i18n モジュールと、1行のスクリプトタグで実現するDOM ベースの自動翻訳です。どちらも機能しますが、解決する問題もチームへの適性もまったく 異なります。ここでは正直なトレードオフを解説します。

@nuxtjs/i18n が実際にできること

@nuxtjs/i18n(nuxt-i18n)は Nuxt の国際化に用いられる公式モジュールです。vue-i18n をラップし、Nuxt のファイルベースのルーティングと Vue Router に密接に 統合されます。提供される機能は以下の通りです。

  • ロケールを付与した URL ルーティング — /fr/about、 /de/pricing — を Nuxt の ルーティング層が自動的に処理します。
  • サーバー側では Accept-Language ヘッダー、 クライアント側では navigator.language から ブラウザのロケールを検出します。
  • 各言語版のページに対する hreflang タグを自動生成 — SEO 上重要です。
  • テンプレートや <script setup> 内で 文字列を翻訳するための $t() コンポーザブルと useI18n() フック。

できないこと:コンテンツそのものの翻訳です。すべてのロケールファイル内の すべての文字列は、人間が書く(あるいは手動で翻訳サービスに通す)必要があります。モジュールが 管理するのはルーティングと文字列の参照であり、実際の翻訳を埋めるのはあなたの責任です。

@nuxtjs/i18n のセットアップ:実際に必要な作業

3言語向けの最小限の nuxt-i18n セットアップには、以下の作業が必要です。

  1. モジュールをインストールし、 nuxt.config.ts の modules に登録する。
  2. i18n 設定ブロックで ロケール、デフォルトロケール、遅延読み込み戦略を宣言する。
  3. ロケールごとの JSON ファイルを作成する: locales/en.json、 locales/fr.json、 locales/de.json — 対応言語ごとに1つずつ、新しい UI 文字列が追加されるたびに更新します。
  4. すべてのコンポーネント内のハードコードされた文字列を $t('key') または t('key') 呼び出しに 置き換える — プロジェクト内のすべての .vue ファイルに手を 入れることになります。
  5. ナビゲーションにロケールスイッチャーコンポーネントを追加し、カスタムのスラッグページの ルーティングロジックを更新する(例: /blog/[slug] にあった ブログ記事は、Nuxt i18n ルーティングでは /fr/blog/[slug] になります)。

最小限の nuxt.config.ts の i18n ブロックの例:

export default defineNuxtConfig({
  modules: ["@nuxtjs/i18n"],
  i18n: {
    locales: [
      { code: "en", file: "en.json" },
      { code: "fr", file: "fr.json" },
      { code: "de", file: "de.json" },
    ],
    defaultLocale: "en",
    lazy: true,
    langDir: "locales/",
    strategy: "prefix_except_default",
  },
})

そして対応する locales/en.json:

{
  "nav": {
    "home": "Home",
    "pricing": "Pricing",
    "docs": "Documentation"
  },
  "hero": {
    "headline": "Translate your Nuxt app",
    "cta": "Get started free"
  }
}

すべてのキーは fr.json や de.json にも複製・翻訳する 必要があり、プロダクトが進化し続ける限り、それを永久に同期させ続けなければなりません。

積み重なる保守コスト

1文字列あたりのオーバーヘッドは単体では小さく見えます。しかし実際のプロダクトで積み重なると、 そのコストは急速に膨らみます。

コンテンツとコードの結合

新しい UI 文字列を追加するたびに、ソースロケールファイルにキーを追加し、すべての ターゲットロケールファイルにも対応するエントリを追加する必要があります。月曜日に出荷した 機能は、フランス語やドイツ語のファイルを更新するまで、そのユーザーにはキーのフォール バックしか表示されません。

コンポーネントのリファクタリングが全ファイルに波及

既存プロジェクトに nuxt-i18n を追加するということは、テキストを描画するすべての .vue ファイルに手を 入れることを意味します。文字列を $t() でラップし、値を ロケールファイルに抽出し、漏れがないか検証する — 50以上のコンポーネントを持つ プロジェクトでは、これは数日がかりのリファクタリングになります。

ロケールファイルのドリフト

@nuxtjs/i18n の 欠落キー検出やサードパーティスクリプトのようなツールがなければ、ロケールファイルは 静かに乖離していきます。英語ファイルにはあってフランス語ファイルにはないキーが増え、 フォールバックレンダリングによってフランス語ユーザーに静かに英語テキストが表示されて しまいます。

RTL には別途レイアウト対応が必要

nuxt-i18n は文字列を翻訳しますが、レイアウトの方向は反転しません。アラビア語や ヘブライ語に対応するには、ルート要素に dir="rtl" を設定し、 すべてのコンポーネントの物理 CSS プロパティを監査し、レイアウト全体を RTL モードで テストする — 別の開発工数が必要な作業です。

@nuxtjs/i18n が正しい選択となる場合

フルモジュールスタックが明確に正当化される具体的な状況があります。

  • 言語ごとに URL を持つ SEO 重視のコンテンツサイト

    各言語版が固有のインデックス可能な URL であり、独自の canonical と hreflang を持つ必要が あり、それを構築・維持する開発リソースがある場合、nuxt-i18n はまさにそれを 実現します。

  • コミュニティ翻訳のあるオープンソースアプリ

    GitHub 上のプルリクエストを通じてコミュニティが翻訳を提供する場合、ロケール JSON ファイルは適切な貢献者インターフェースです。Crowdin や Weblate といった プラットフォームはこの形式と直接統合できます。

  • 規制対象のコンプライアンスコンテンツ

    法務、医療、金融のアプリケーションでは、すべての文字列を出荷前に明示的にレビュー・ 承認する必要があります。nuxt-i18n は、すべての文字列をバージョン履歴付きの監査 可能な独立した成果物として扱えます。

  • オフライン対応の PWA

    オフラインで動作する必要がある Nuxt アプリは、すべてのロケール文字列をビルド 時にバンドルしておく必要があります。nuxt-i18n のロケール JSON ファイルはビルドに 含まれますが、DOM ベースの翻訳はネットワーク接続なしでは動作できません。

Nuxt 3 アプリに Lingvit を追加する

もう一つの選択肢はスクリプトタグです。モジュールのインストールも、ロケールファイルも、 コンポーネントの変更も不要です。Nuxt 3 では、 nuxt.config.ts の app.head プロパティ経由で スクリプトを追加します。

// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      script: [
        {
          src: "https://cdn.lingvit.com/widget/v1/widget.bundle.js",
          "data-project-id": "YOUR_PROJECT_ID",
          async: true,
          tagPosition: "bodyClose",
        },
      ],
    },
  },
})

あるいは、コンポーネント単位で制御したい場合は、ルートの app.vue 内で useHead コンポーザブルを 使うこともできます。

<!-- app.vue -->
<script setup lang="ts">
useHead({
  script: [
    {
      src: "https://cdn.lingvit.com/widget/v1/widget.bundle.js",
      "data-project-id": "YOUR_PROJECT_ID",
      async: true,
      tagPosition: "bodyClose",
    },
  ],
})
</script>

必要な統合作業はこれだけです。Lingvit のウィジェットは MutationObserver を使って、Nuxt がレンダリング・ハイドレートした後の DOM を監視します。対応する内容は以下の通りです。

  • Nuxt の SSR エンジンからストリーミングされたサーバーレンダリング HTML — ハイドレーション完了後、即座に翻訳されます。
  • リアクティブな Vue の更新 — v-if で表示される コンテンツ、 v-for で描画される コンテンツ、非同期データ取得で読み込まれるコンテンツは、新しいノードが DOM に 現れるたびに翻訳されます。
  • Vue Router のナビゲーション — Nuxt のクライアントサイドルート遷移は、新しく レンダリングされたページコンテンツに対してオブザーバーをトリガーします。
  • RTL レイアウト — アラビア語やヘブライ語など RTL 言語に切り替える際、 document.documentElement.dir が自動的に設定されます。

セットアップの比較

@nuxtjs/i18n のセットアップ

  1. @nuxtjs/i18n を インストールし、 nuxt.config.ts に 登録する
  2. i18n ブロックで ロケール、戦略、遅延読み込みを設定する
  3. locales/en.json、 fr.json などを 作成する
  4. すべての .vue ファイルの テンプレート文字列を $t() でラップする
  5. ロケールスイッチャー UI コンポーネントを追加する
  6. 各言語の翻訳ファイルを埋める

Lingvit のセットアップ

  1. nuxt.config.ts の app.head に script エントリを 1つ追加する
  2. Lingvit ダッシュボードで対象言語を選択する

自動翻訳で十分なケース

多くの Nuxt プロジェクトにとって、DOM ベースの翻訳は妥協ではなく、正しいエンジニアリング 判断です。

マーケティングサイト・コンテンツサイト

Nuxt のマーケティングサイトやブログには複雑な複数形処理もオフライン要件もなく、 コピーは頻繁に変わります。自動翻訳は開発者の作業なしに、初回レンダリング時に すべての新しい段落を処理します。

言語追加を急ぐ SaaS ダッシュボード

国際的なユーザーは国際的な収益を意味します。スクリプトタグでフランス語を公開するのは 半日仕事です。既存の SaaS ダッシュボードに nuxt-i18n を後付けするのは数週間かかり、 すべてのコンポーネントに手を入れることになります。

高速なプロダクトイテレーション

UI コピーが毎週変わる場合、ロケールファイルの保守が開発速度の足かせになります。 翻訳者がレビューできるようになる前に、コピーの更新のたびにすべてのロケール ファイルに手を入れる必要があります。自動翻訳は、開発者の作業なしに初回レンダリング 時に新しい文字列を処理します。

i18n を導入していない既存の Nuxt アプリ

nuxt-i18n を導入せずに構築されたプロジェクトに後から追加するということは、 テキストを描画するすべてのコンポーネントに手を入れることを意味します。DOM ベースの ウィジェットならコンポーネントの変更はゼロです — スクリプトタグを追加し、言語を 選択すれば完了です。

自動翻訳がカバーしない範囲

DOM ベースの翻訳には現実的な限界があります。選択する前に理解しておきましょう。

サーバー専用の文字列

サーバー側のメールテンプレート、API レスポンスボディ、Nuxt のサーバールートで 使われる文字列は、ブラウザの DOM に一切レンダリングされないため、翻訳されません。

複雑な ICU 複数形処理

複雑な複数形ルールを持つ言語(アラビア語:6形、ポーランド語:4形)で、動的にカウント される値に対して文法的に正しい複数形処理が必要なアプリケーションには、ICU メッセージ フォーマットのサポートが必要です — これはライブラリの機能であり、DOM レベルの翻訳 ではありません。

オフライン/PWA 要件

Nuxt アプリが完全にオフラインで動作する必要がある場合、自動翻訳は選択肢に なりません — 翻訳を取得するにはネットワーク通信が必要だからです。ビルド時に バンドルされるロケール JSON ファイルを使用してください。

よくある質問

Lingvit は Nuxt 3 と Nuxt 4 で動作しますか?

はい。Lingvit はフレームワークに依存せず、DOM レベルで動作します。Nuxt 3 と Nuxt 4 はどちらも標準的な HTML を生成し、ウィジェットはそれを MutationObserver で監視します。nuxt.config.ts の app.head.script に tagPosition: 'bodyClose' でスクリプトを追加するだけで、そのまま動作します。

Lingvit は Nuxt の SSR で動作しますか?

はい。Nuxt がサーバーでレンダリングした HTML はブラウザに送られ、その後 Vue がハイドレートします。Lingvit はハイドレーション完了後に初期化され、サーバー由来かクライアント側の状態変化由来かを問わず、レンダリングされた DOM のすべてのテキストノードを取得します。

Lingvit は Vue のハイドレーションを妨げますか?

いいえ。Lingvit のスクリプトは DOM の準備完了後に非同期で読み込まれ、翻訳を始める前にページが安定するのを待ちます。仮想 DOM には触れず、コンポーネントの状態も変更せず、Vue のリアクティビティもトリガーしません。変更するのはレンダリング済みのテキストノードだけです。

@nuxtjs/i18n と Lingvit を併用できますか?

はい。一部のコンテンツに既に @nuxtjs/i18n を使っていて、残りを自動翻訳したい場合、両者は共存できます。Lingvit はレンダリング時に DOM に存在するテキストノードをすべて翻訳します。これには nuxt-i18n が既に翻訳した文字列も含まれます。