Author: Aymeric PINEAU
    Creation:2026-04-20Last update:2026-05-18

    Next.js i18nライブラリ - 2026年ベンチマークレポート

    このページは、Next.jsにおけるi18nソリューションのベンチマークレポートです。

    目次

    インタラクティブベンチマーク

    結果のリファレンス:

    intlayer.org
    完全なベンチマークデータを見る

    ベンチマークのリポジトリ全体はこちらでご確認いただけます。

    はじめに

    国際化ライブラリはアプリケーションに大きな影響を与えます。主なリスクは、ユーザーが1ページしか閲覧しないにもかかわらず、すべてのページとすべての言語のコンテンツをロードしてしまうことです。

    アプリが成長するにつれて、バンドルサイズが指数関数的に増大し、パフォーマンスが著しく低下する可能性があります。

    最悪のケースでは、国際化によってページサイズが4倍近くに膨れ上がることがあります。

    また、i18nライブラリのもう一つの影響として、開発の遅延が挙げられます。コンポーネントを多言語対応に作り変える作業は時間がかかります。

    この問題の解決は難しいため、DX(開発体験)にフォーカスしたもの、パフォーマンスやスケーラビリティにフォーカスしたものなど、さまざまなソリューションが存在します。

    Intlayerは、これらの各側面において最適化を試みています。

    TL;DR

    • Intlayer & next-translate: Next.jsのパフォーマンスにおいて最適な選択肢。最小のフットプリントと最高の静的レンダリングサポートを提供。
    • next-intl: 最もトレンドのオプションだが、大規模なアプリケーション向けに最適化するには重く、複雑。
    • next-i18next: 人気がありプラグインも豊富だが、バンドル重量が非常に大きい(Intlayer의約3倍)。
    • 避けるべき: gt-nextlingo.dev。深刻なパフォーマンスの問題、ベンダーロックイン、ビルドを破壊するバグのため。

    アプリをテストする

    これらの問題を顕在化させるために、無料のスキャナーを作成しました。こちらで試すことができます。

    intlayer.org

    問題点

    多言語アプリがバンドルサイズに与える影響を制限するには、主に2つの方法があります。

    • JSON(またはコンテンツ)をファイル、変数、ネームスペースごとに分割し、特定のページで使用されないコンテンツをバンドラーがツリーシェイキングできるようにする。
    • ページのコンテンツをユーザーの使用言語のみ動的にロードする。

    これらのアプローチの技術的な制限:

    動的ロード

    WebpackやTurbopackを使用し、[locale]/page.tsxのようなルートを宣言したり、generateStaticParamsを定義したりしたとしても、バンドラーはlocaleを静的な定数として扱いません。つまり、すべての言語のコンテンツが各ページに引き込まれる可能性があります。これを制限する主な方法は、動的インポート(例:import('./locales/${locale}.json'))を介してコンテンツをロードすることです。

    ビルド時に何が起こるかというと、Next.jsはロケールごとに1つのJSバンドルを生成します(例:./locales_fr_12345.js)。サイトがクライアントに送信され、ページが実行されると、ブラウザは必要なJSファイル(例:./locales_fr_12345.js)に対して追加のHTTPリクエストを実行します。

    同じ問題を解決する別の方法は、fetch()を使用してJSONを動的にロードすることです。これは、JSONが/publicの下にある場合のTolgeeや、コンテンツのロードにgetStaticPropsを使用するnext-translateの仕組みです。流れは同じで、ブラウザがアセットをロードするために追加のHTTPリクエストを行います。

    コンテンツの分割

    const t = useTranslation() + t('my-object.my-sub-object.my-key')のような構文を使用する場合、通常、ライブラリがキーを解析して解決できるように、JSON全体がバンドルに含まれている必要があります。そのため、ページで使用されていないコンテンツの多くが同梱されてしまいます。

    これを軽減するために、一部のライブラリ(next-i18nextnext-intllinguinext-translatenext-internationalなど)では、ページごとにロードするネームスペースを宣言する必要があります。

    対照的に、Paraglideはビルド前にJSONをconst en_my_var = () => 'my value'のようなフラットなシンボルに変換するステップを追加します。理論的には、これによりページ上の未使用コンテンツをツリーシェイキングできるようになります。しかし、後述するように、この方法にもトレードオフがあります。

    最終的に、Intlayerはビルド時の最適化を適用し、useIntlayer('my-key')が対応するコンテンツに直接置き換えられるようにします。

    調査方法

    このベンチマークでは、以下のライブラリを比較しました。

    • Base App(i18nライブラリなし)
    • next-intlayer (v8.7.12)
    • next-i18next (v16.0.5)
    • next-intl (v4.9.1)
    • @lingui/core (v5.3.0)
    • next-translate (v3.1.2)
    • next-international (v1.3.1)
    • @inlang/paraglide-js (v2.15.1)
    • @tolgee/react (v7.0.0)
    • @lingo.dev/compiler (v0.4.0)
    • wuchale (v0.22.11)
    • gt-next (v6.16.5)

    Next.jsのバージョンは16.2.4(App Router)を使用しました。

    10ページ10言語を持つ多言語アプリを作成しました。

    4つのロード戦略を比較しました。

    戦略 ネームスペースなし(グローバル) ネームスペースあり(スコープ指定)
    静的ロード Static: 起動時にすべてをメモリ上に。 Scoped static: ネームスペースで分割。起動時にすべてロード。
    動的ロード Dynamic: ロケールごとのオンデマンドロード。 Scoped dynamic: ネームスペースとロケールごとのきめ細かなロード。

    戦略の構成

    • Static: シンプル。初回ロード後のネットワーク遅延がない。短所:バンドルサイズが大きい。
    • Dynamic: 初回の重さを軽減(遅延ロード)。ロケールが多い場合に理想的。
    • Scoped static: 複雑な追加ネットワークリクエストなしで、コードを整理(論理的な分離)できる。
    • Scoped dynamic: コード分割とパフォーマンスにおいて最良のアプローチ。現在のビューとアクティブなロケールが必要なものだけをロードすることで、メモリ使用量を最小限に抑える。

    測定対象:

    各スタックにおいて、実際のブラウザで同じ多言語アプリを実行し、実際に何が送出され、どのくらいの時間がかかったかを記録しました。サイズは、生のソースコードのカウントよりも実際のダウンロード量に近いため、一般的なWeb圧縮後の値を報告しています。

    • 国際化ライブラリのサイズ: バンドル、ツリーシェイキング、および圧縮後のi18nライブラリのサイズです。中身が空のコンポーネントにおけるプロバイダー(例:NextIntlClientProvider)とフック(例:useTranslations)のコードのサイズを指します。これには翻訳ファイルのロードは含まれません。コンテンツが入る前にライブラリ自体がどれだけコスト高であるかを示します。

    • ページごとのJavaScript量: 各ベンチマークルートにおいて、ブラウザがその訪問で引き込むスクリプトの量です。スイート内のページ全体(およびレポートで集計されている場合はロケール全体)の平均値です。重いページは遅いページです。

    • 他のロケールからのリーク: 同じページの他の言語のコンテンツが、誤って対象ページにロードされてしまうことです。このコンテンツは不要であり、回避されるべきです(例:/en/aboutページのバンドルに含まれる/fr/aboutページのコンテンツ)。

    • 他のルートからのリーク: アプリ内の他の画面についても同様です。1ページしか開いていないのに、他のページのコピーが紛れ込んでいないかを測定します(例:/en/contactページのバンドルに含まれる/en/aboutページのコンテンツ)。スコアが高い場合は、分割が不十分であるか、バンドルが広範囲すぎることを示しています。

    • コンポーネントの平均バンドルサイズ: 一般的なUIパーツをアプリ全体の巨大な数値に隠すのではなく、一つずつ測定します。これにより、国際化が日常的なコンポーネントをひっそりと膨らませていないかを確認できます。たとえば、コンポーネントが再レンダリングされると、それらすべてのデータをメモリからロードすることになります。コンポーネントに巨大なJSONを添付することは、未使用データの大きな蓄積を接続するようなもので、コンポーネントのパフォーマンスを低下させます。

    • 言語切り替えの反応性: アプリ自身のコントロールを使用して言語を切り替え、ページが明確に切り替わるまでにかかる時間を測定します。ラボ内での微細なステップではなく、訪問者が気づく時間を対象としています。

    • 言語変更後のレンダリング作業: 切り替えが進行し始めてから、新しい言語でのインターフェースの再描画にかかった労力の追跡調査です。「体感」時間とフレームワークのコストが乖離している場合に有用です。

    • 初回ページロード時間: ナビゲーションから、テストシナリオにおいてブラウザがページを完全にロードしたと判断するまでの時間です。コールドスタートの比較に適しています。

    • ハイドレーション時間: アプリが表示している場合、クライアントがサーバーのHTMLを実際にクリックできるものに変換するのにかかる時間です。表内のダッシュ(-)は、その実装がこのベンチマークで信頼できるハイドレーション数値を提供しなかったことを意味します。

    GitHubのスター

    GitHubのスターは、プロジェクトの普及度、コミュニティの信頼、および長期的な関連性を示す強力な指標です。技術的な品質を直接測定するものではありませんが、どれだけの開発者がプロジェクトを有用だと感じ、その進捗をフォローし、採用する可能性があるかを反映しています。プロジェクトの価値を見積もる際、スターは代替案との勢いの比較を助け、エコシステムの成長に関する洞察を提供します。

    Star History Chart

    結果の詳細

    1 - 避けるべきソリューション

    gt-nextlingo.devのようなソリューションは、明らかに避けるのが賢明です。これらはベンダーロックインを伴い、コードベースを汚染します。実装に何時間も費やしたにもかかわらず、TanStack StartでもNext.jsでも、正しく動作させることはできませんでした。

    遭遇した問題:

    (General Translation) ([email protected]):

    • 110kbのアプリに対して、gt-nextは440kb以上の余分なデータを追加します。
    • General Translationを使用した最初のビルドで「Quota Exceeded, please upgrade your plan(クォータ超過、プランをアップグレードしてください)」と表示されました。
    • 翻訳がレンダリングされません。Error: <T> used on the client-side outside of <GTProvider>というエラーが発生しましたが、これはライブラリのバグのようです。
    • gt-nextを実装中、ライブラリの問題にも遭遇しました。does not provide an export named 'printAST' - @formatjs/icu-messageformat-parserというエラーでアプリケーションが壊れました。この問題を報告した後、メンテナは24時間以内に修正しました。
    • このライブラリはNext.jsページの静的レンダリングをブロックします。

    (Lingo.dev) (@lingo.dev/[email protected]):

    • AIのクォータを超過し、ビルドが完全にブロックされました。つまり、支払いをしない限りプロダクションへのデプロイができません。
    • コンパイラが翻訳コンテンツの約40%を認識していませんでした。動作させるために、すべての.mapをフラットなコンポーネントブロックに書き換える必要がありました。
    • CLIにバグがあり、理由もなく設定ファイルをリセットすることがありました。
    • ビルド時に、新しいコンテンツが追加されると生成されたJSONを完全に消去してしまいました。結果として、数個のキーのために既存の300個以上のキーが消失することがありました。

    2 - 実験的なソリューション

    (Wuchale) ([email protected]):

    Wuchaleの背後にあるアイデアは興味深いものですが、まだ実用的ではありません。反応性の問題に遭遇し、アプリを動作させるためにプロバイダーの強制的な再レンダリングが必要でした。ドキュメントもかなり不明瞭で、導入のハードルが高いです。

    (Paraglide) (@inlang/[email protected]):

    Paraglideは革新的でよく考えられたアプローチを提供しています。それにもかかわらず、このベンチマークでは、Next.jsやTanStack Startの設定において、宣伝されていたツリーシェイキングは機能しませんでした。ワークフローとDXは他の選択肢よりも複雑です。 個人的には、プッシュのたびにJSファイルを再生成しなければならないのが嫌いです。これはPRを通じて常にマージ競合のリスクを生み出します。また、このツールはNext.jsよりもViteにフォーカスしているように見えます。 最後に、他のソリューションと比較して、Paraglideはコンテンツをレンダリングするために現在のロケールを取得するためのストア(例:Reactコンテキスト)を使用しません。パースされる各ノードについて、localStorageやクッキーなどからロケールをリクエストします。これにより、コンポーネントの反応性に影響を与える不要なロジックが実行されます。

    paraglideについての注意:このソリューションは、インポートのためにコードベースにコードを注入します。その結果、ベンチマークレポートの「ライブラリサイズ」指標はほぼ0になります。コード生成は良いことです。なぜなら、使用される関数には必要なロジック(すべてのプレフィックス対プレフィックスなし、クッキー対ストレージなど)のみが含まれるからです。対照的に、Intlayerはビルド時に環境変数を注入して、ロジックに応じてコンテンツをツリーシェイキングするようバンドラーに強制します。このおかげで、paraglideとintlayerは、i18nextやnext-intlよりも6〜10倍軽量なソリューションとなっています。

    3 - 許容できるソリューション

    (Tolgee) (@tolgee/[email protected]):

    Tolgeeは前述の問題の多くに対処しています。しかし、同様のツールよりも導入が難しいと感じました。型安全性が提供されていないため、コンパイル時に紛失したキーを見つけることも困難です。キーの不備を検出するために、Tolgeeの関数を自前の関数でラップする必要がありました。

    (Next Intl) ([email protected]):

    next-intlは最もトレンディな選択肢であり、AIエージェントが最も推奨するものですが、私の見解ではそれは間違いです。導入は簡単です。しかし実際には、リークを制限するための最適化は複雑です。動的ロード、ネームスぺーシング、TypeScriptの型を組み合わせると、開発スピードが著しく低下します。パッケージもかなり重いです(NextIntlClientProvider + useTranslationsで約13kb、これはnext-intlayerの2倍以上です)。next-intlはかつてNext.jsページの静的レンダリングをブロックしていました。setRequestLocale()というヘルパーを提供していますが、en.jsonfr.jsonのような集中管理されたファイルに対しては部分的に対処されているものの、コンテンツがen/shared.jsonfr/shared.jsones/shared.jsonのようにネームスペースに分割されている場合、依然として静的レンダリングが壊れます。

    (Next I18next) ([email protected]):

    next-i18nextは、JavaScriptアプリにおける最初期のi18nソリューションの一つであったため、おそらく最も人気のある選択肢です。多くのコミュニティプラグインがあります。これにはnext-intlと同じ大きな欠点があります。パッケージが非常に重いです(I18nProvider + useTranslationで約18kb、next-intlayerの約3倍)。

    メッセージ形式も異なります。next-intlはICU MessageFormatを使用しますが、i18nextは独自の形式を使用します。

    (Next International) ([email protected]):

    next-internationalも上記の問題に取り組んでいますが、next-intlnext-i18nextと大きな違いはありません。ネームスペース固有の翻訳のためにscopedT()が含まれていますが、それを使用してもバンドルサイズへの影響はほとんどありません。

    (Lingui) (@lingui/[email protected]):

    Linguiはしばしば賞賛されます。個人的には、lingui extract / lingui compileのワークフローが他の選択肢よりも複雑で、明確な利点が見出せませんでした。また、AIを混乱させる一貫性のない構文(例:t()t''i18n.t()<Trans>)も見受けられました。

    4 - 推奨事項

    (Next Translate) ([email protected]):

    t()スタイルのAPIがお好みなら、next-translateが私の主な推奨事項です。next-translate-pluginを介して優雅に動作し、Webpack / Turbopackローダーを使用してgetStaticProps経由でネームスペースをロードします。また、今回の中で最も軽量な選択肢です(約2.5kb)。ネームスぺーシングについては、設定ファイルでページやルートごとにネームスペースを定義する方法がよく考えられており、next-intlnext-i18nextのような主要な選択肢よりもメンテナンスが容易です。バージョン3.1.2では、静的レンダリングが機能せず、Next.jsが動的レンダリングにフォールバックすることに気づきました。

    (Intlayer) ([email protected]):

    客観性を保つため、自分自身のソリューションであるnext-intlayerについては個人的な判断を控えさせていただきます。

    個人的なメモ

    このメモは個人的なものであり、ベンチマークの結果には影響しません。i18nの世界では、const t = useTranslation('xx') + <>{t('xx.xx')}</>のようなパターンが合意事項としてよく見られます。

    Reactアプリにおいて、関数をReactNodeとして注入することは、私の考えではアンチパターンです。また、避けられるはずの複雑さとJavaScriptの実行オーバーヘッド(たとえ微々たるものであっても)を付加することになります。