Vue i18n vs 自動翻訳:Vueアプリに適したアプローチの選び方(2026年版)
Vueには成熟した、多くの支持を得ているi18nライブラリがあります。同時に、ソースコードを 一切変更する必要がないDOMベースの翻訳ツールのエコシステムもあります。プロジェクトに どちらが適しているかは、他のガイドでは見落とされがちな要因によって決まります — 正直な比較をお届けします。
Vueのi18n事情
vue-i18n(kazuponによって作られ、現在は@intlify組織の もとで@intlify/vue-i18n-nextとして 保守されています)は、Vueの国際化における事実上の標準です。Vue 2(vue-i18n v8)とVue 3 (vue-i18n v9以降)の両方を第一級でサポートしています。
APIはシンプルで文書化も充実しています。Composition APIを使うVue 3アプリでは次のように なります。
// main.ts
import { createI18n } from 'vue-i18n'
const i18n = createI18n({
legacy: false,
locale: 'en',
fallbackLocale: 'en',
messages: { en, fr, de },
})
app.use(i18n)
コンポーネント内ではuseI18nコンポーザブルとt()関数を 使います。
<script setup lang="ts">
const { t } = useI18n()
</script>
<template>
<h1>{{ t('home.title') }}</h1>
<p>{{ t('home.description') }}</p>
</template>
Vue 2やOptions APIでは、テンプレート内で同じ文字列に $t('home.title')としてアクセスします。このライブラリは実戦で鍛えられており、活発なコミュニティを持ち、 単純な文字列検索からECMAScript国際化APIによる複雑な数値・日付フォーマットまで あらゆるものをサポートしています。
保守の負担
vue-i18nは強力ですが、その力にはアプリケーションの成長とともに複利で増える保守コストが 伴います。
- →言語ごとのJSONロケールファイル。 アプリ内のすべての文字列を、 言語ごとにキーと値のファイルに抽出する必要があります。典型的なSaaS製品では数百から 数千のキーがあります。新機能ごとに、すべてのロケールファイルに新しいキーが 追加されます。
- →ファイルの同期を保つこと。 開発者が新しいUI文字列を追加し、 フランス語のロケールファイルの更新を忘れると、フォールバックロケールによって フランス語ユーザーに英語が表示されます — しばしば静かに。
vue-i18n-extractのような ツールが助けにはなりますが、設定の手間が増えます。 - →バンドルサイズと遅延読み込み。 すべてのロケールJSONを事前に 配布するとバンドルが肥大化します。ロケールごとの遅延読み込みにはVite/webpackの 非同期チャンク設定と、ロケール切り替え中の読み込み状態の処理が必要です。
- →言語ごとの複数形ルール。 英語には2つの複数形がありますが、 ロシア語には4つ、アラビア語には6つあります。vue-i18nはICUメッセージ構文で これを扱いますが、誰かが各言語の複数形の文字列を書いて検証する必要があります。
- →日付と数値のフォーマット。 ロケールに応じたフォーマットには ロケールごとの明示的な
datetimeFormatsとnumberFormatsの設定が 必要で、正しくフォーマットされた日付をユーザーが目にするまでに何時間もの セットアップ作業がかかります。
専任のi18nエンジニアがいないチームでは、この負担によって翻訳が製品に追いつかず、 言語に「対応している」はずなのに国際ユーザーへ英語の文字列を出荷してしまうことが よくあります。
vue-i18nが正しい選択となる場面
コミュニティによる翻訳があるオープンソースアプリ
プロジェクトがコミュニティの貢献者による翻訳提供に依存している場合 — オープンソース ツールでは一般的です — vue-i18nのロケールJSONファイルは、翻訳者が直接ファイルを 編集するCrowdinやWeblateのようなプラットフォームとうまく統合します。
厳格な翻訳承認ワークフロー
すべての文字列を本番反映前にレビュー・承認する必要がある法律、医療、政府系の アプリケーションは、vue-i18nが提供する明示的なキー単位の管理から恩恵を受けます。
複雑な数値・日付・通貨のフォーマット
財務データ、科学的な測定値、複雑な日付範囲を表示するアプリケーションには ロケールごとのフォーマットルールが必要です。vue-i18nの組み込みICUメッセージ サポートはこうしたケースを体系的に処理します。
オフライン対応が必要なアプリ
Vueアプリがオフラインで動作する必要がある場合(PWA、Electronアプリ)、すべての ロケール文字列はネットワークリクエストなしで利用可能な状態でバンドルされて いなければなりません。vue-i18nのロケールJSONファイルはビルド時にバンドルされます。
自動翻訳が勝る場面
既存のVueアプリを書き直さずに多言語化する
既存のVue 2またはVue 3アプリケーションにvue-i18nを後付けすることは、テキストを レンダリングするすべてのコンポーネント — しばしば数百ファイル — に触れることを 意味します。DOMベースの翻訳ウィジェットはスクリプトタグ1つで、コンポーネントの 変更は一切不要です。
専任のi18nエンジニアがいないチーム
ロケールファイルを正確に維持するには、製品と対象言語の両方を理解する人材が 必要です。その人材がいなければ、自動翻訳は古い、または不完全なロケールファイルより 良い結果を生みます。
急速なUIの反復
製品が毎週変わる場合、翻訳キーの維持は足かせになります。UIコピーが変わるたびに すべてのロケールファイルの更新が必要です。自動翻訳は開発者側の対応なしで 初回レンダリング時に新しい文字列を処理します。
スピード重視のSaaS製品
国際的な収益を得るには、その市場で実際に稼働している必要があります。DOMベースの アプローチなら、3週間のエンジニアリングスプリントではなく、午後のうちに 「フランス語で稼働」を実現できます。
LingvitとVueの連携方法
Lingvitのウィジェットはフレームワークに依存しません — フレームワークレベルではなく DOMレベルで動作します。Vueアプリの場合は以下の通りです。
- →
index.htmlの</body>の前に スクリプトタグを追加します。Vueはスクリプトの解析後にマウントされ、ウィジェットは DOMが構築された時点で初期化されます。 - →MutationObserverがVueのリアクティブなレンダリングを処理します。
v-ifがセクションを表示したり、v-forがリストを レンダリングしたり、非同期処理の後にコンポーネントがマウントされたりすると、 新しいDOMノードはすぐに翻訳されます。 - →Vue 2とVue 3の両方に対応しています。内部のレンダリング処理は 異なりますが、出力は常にDOMノードであり、ウィジェットが必要とするのはそれだけです。
- →Nuxt.jsにも対応しています。 Nuxt 3(サーバーレンダリング)の 場合は、
nuxt.config.ts内のapp.head.scriptでtagPosition: 'bodyClose'を指定してスクリプトを追加します。Nuxt 2ではnuxt.config.jsのheadプロパティを使います。
セットアップ比較
vue-i18nのセットアップ
- 1.
vue-i18nをインストールし、main.tsでcreateI18nを設定 - 2.
src/locales/en.json、fr.jsonなどを作成 - 3. すべてのコンポーネントのテンプレート文字列を
t()や$t()でラップ - 4. ロケールの遅延読み込み用にViteプラグインを設定
- 5. ロケールスイッチャーのUIコンポーネントを追加
- 6. 言語ごとに翻訳ファイルを記入
Lingvitのセットアップ
- 1.
index.htmlに<script>タグを1つ追加 - 2. ダッシュボードで対象言語を選択
よくある質問
- Nuxt.jsで動作しますか?
- はい。Nuxt 3では、nuxt.config.ts内のapp.head.scriptでtagPosition: 'bodyClose'を指定してスクリプトを追加します。Lingvitは、Nuxtのサーバーレンダリング済みHTMLがハイドレートされた後、およびVue Router連携によるクライアントサイドのナビゲーション後の最終的なDOMを監視します。
- Vue 2でも動作しますか?
- はい。Lingvitはフレームワークに依存せず、DOMレベルで動作します。Vue 2とVue 3はどちらも標準的なDOMノードを生成するため、MutationObserverはお使いのバージョンに関わらずリアクティブな更新を処理します。
- v-modelの入力欄はどうなりますか?ユーザーが入力したテキストは翻訳されますか?
- いいえ。Lingvitは DOM内のテキストノードを翻訳しますが、v-modelでバインドされた入力要素の値は翻訳しません。ユーザーが入力したコンテンツはデフォルトで除外されます。任意の要素にdata-lingvit-ignore属性を追加すると、そのサブツリーを翻訳対象から除外することもできます。