Vue 3やNuxtで多言語対応(i18n)アプリを構築する際、長年にわたりデファクトスタンダードとして君臨してきたのが vue-i18n です。Kazupon氏をはじめとするコミュニティによって成熟したエコシステムが築かれ、Vue開発者にとって最初の選択肢であり続けました。
しかし、なぜ vue-i18n が現在の挙動になっているのかを理解するには、その設計時期を振り返る必要があります。このアーキテクチャが作られたのは、動的インポート(import())やルート単位のコード分割がまだ標準化されていなかった、古き良きSPA(Single Page Application)の全盛期でした。当時は、アプリ起動時に巨大なグローバル翻訳オブジェクトを丸ごとメモリに読み込むアプローチが最も自然だったのです。
ところが現在のフロントエンド開発は、Nuxtや静的サイト生成(SSG)、きめ細やかな動的ページ読み込みを駆使した「ページ容量(初期ロードJS)の徹底的な最適化」が前提となっています。この現代的なアーキテクチャにおいて、中央集約型のグローバルプロバイダーモデルは極めて深刻なボトルネックと化しています。
そもそも、なぜ vue-i18n のライブラリ本体はここまで重いのでしょうか? 単に vue-i18n をインポートしただけの空コンポーネントでも、文字を1つも描画しない段階で gzip約24.3 KB(非圧縮83.2 KB) もの容量を消費します。その理由は、動的な変数挿入({{number}} や {name})の評価、複雑な複数形(Pluralization)ルール、リスト整形などをブラウザ側で実行時にパースするための巨大なランタイムエンジンを丸ごと同梱しているからです。さらに多言語ルーティングやURL管理を行おうとすると、Cookieの保存やロケール自動判定、URLプレフィックスの書き換えといった大量のクライアント側ロジックが追加で読み込まれることになります。
そして、それ以上に深刻なのが、画面間での「翻訳文言の大量漏洩(Copy Leakage)」という構造的欠陥です。
一般的なVueアプリでは、createI18n({ messages }) が全言語・全ページの翻訳キーを抱えたモノリシックなグローバルツリーを生成します。その結果、ユーザーがシンプルな /contact ページを開いただけでも、ブラウザは /dashboard や /pricing、/settings など、アプリ全体のすべてのテキストをダウンロードせざるを得ません。標準的な10画面・10言語のVite + Vue 3アプリでベンチマークを実施したところ、ある1画面でダウンロードされた翻訳データの実に90%が、その画面とは全く関係のない他画面のテキストでした。さらに useI18n() がこのグローバルインスタンスに直接バインドされるため、1つのコンポーネントを単体でコンパイルするだけでも平均 196 KB ものJSを引きずり込んでしまいます。
加えて、t("key.path") は実行時に動的に文字列を評価するため、ViteやRollupといったバンドラは実際にどのキーが使われているかを静的に追跡できません。そのため未使用の翻訳をツリーシェイキングで削除することができず、キーのタイポもTypeScriptのビルドエラーにならず本番環境でサイレントに表示崩れを引き起こします。
Intlayerはこれをどう解決するのか?
Intlayerは、肥大化したランタイムパーサーやプロバイダーに頼るのではなく、これらの余分な処理をすべてビルド時に排除し、静的コード変換によってコンテンツをコンポーネントへ直接接続することでこの構造的課題を解決します。
1. ビルド時プリコンパイル(ランタイムパースのオーバーヘッドをゼロに)
{{number}} などの変数挿入をブラウザ上でパースしたり、実行時にCookieやURLプレフィックスを処理する重厚なロジックをクライアントへ送り込む代わりに、Intlayerはこれらすべてをビルド段階で事前に解決・最適化します。ブラウザに届くのは余計な処理が削ぎ落とされた極めて軽量なコードのみであり、ランタイムフットプリントはわずか 3.9 KB(互換アダプターでも7.9 KB)まで削減されます。
2. コンポーネントとコンテンツの直接バインディング
無秩序に肥大化する locales/en.json にすべての文言を詰め込む代わりに、Intlayerは辞書ファイルをそれを消費するコンポーネントのすぐ隣(例: Footer.vue の隣の Footer.content.ts)にコロケーション(同居)させます。その結果、インポートされていないコンポーネントの文言がロードされることは一切ありません。未使用のデッドコードが翻訳データを道連れにバンドルされるのを防ぎます。
3. Viteによるウォーターフォールなしの動的ローディング
さらに強力なのが、Viteの高度なモジュール変換の活用です。動的ページローディングやコード分割を使用している場合でも、Intlayerはそのチャンク内で実際に消費されるローカライズデータだけを、対象チャンクに直接インラインで統合します。描画前にリモートのJSON辞書を待ち受けるような余計なネットワークリクエストやウォーターフォールはゼロ。コンポーネントと必要な翻訳データが1つのチャンクとして同時に届きます。
4. ビルド時の厳格な型安全性
Intlayerはコンテンツ定義からTypeScriptの型定義を自動生成します。IDEでのキーの自動補完はもちろん、翻訳の欠落やタイポがあれば即座にビルドエラーとして検出されるため、本番環境でのフォールバック事故を未然に防げます。
5. 漏洩0%と3倍小さなバンドルサイズ
ビルド時、Intlayerコンパイラは呼び出し元を静的解析し、各ルートが実際に描画するテキストのみを含むように辞書を分割します。実測ベンチマークでは、画面間の文言漏洩が 90%から0% へと完全に消滅し、ページあたりのJavaScriptサイズが 134.9 KBから47.0 KB(gzip)へと激減しました(参考として、i18nを全く導入していないベースアプリの容量は41.3 KBです)。
6. @intlayer/vue-i18n によるDrop-in移行
すでに運用中のVueコードベースがある場合でも、コンポーネントを書き直す必要はありません。互換アダプターである @intlayer/vue-i18n は、既存の vue-i18n と全く同じAPI(useI18n, t(), d(), n(), $t, v-t)を提供します。Viteプラグインを導入し、巨大な messages インポートを削除するだけで、既存の t("key") の呼び出し先がコンパイル・分割済みの辞書へと自動的に差し替わります。.vue ファイルを1行も書き換えることなく、ランタイムは3分の1、コンポーネントサイズは23分の1に縮小します。
もし本番環境で多言語対応のVueまたはNuxtアプリケーションを運用しているなら、ぜひブラウザのNetworkタブを開いてサブページを確認してみてください。ダウンロードされているJavaScriptの大半が、ユーザーが一度も目にすることのない翻訳テキストであることに気づくはずです。
詳細なベンチマーク数値や移行ガイド、アーキテクチャの解説は以下の記事で公開しています:
https://intlayer.org/ja/blog/vue-i18n-vs-intlayer
https://intlayer.org/ja/blog/vue-i18n-vs-intlayer-vue-i18n


