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

    TanStack Start i18nライブラリ - 2026年ベンチマークレポート

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

    目次

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

    結果のリファレンス:

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

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

    はじめに

    国際化ソリューションは、Reactアプリにおいて最も重い依存関係の一つです。TanStack Startにおいて主なリスクとなるのは、不必要なコンテンツ(単一ルートのバンドルに含まれる他のページや他のロケールの翻訳)を送出してしまうことです。

    アプリが成長するにつれて、この問題はクライアントに送信されるJavaScriptを急速に肥大化させ、ナビゲーションを低下させる可能性があります。

    実際、最適化が不十分な実装では、国際化されたページがi18nなしのバージョンよりも数倍重くなることがあります。

    もう一つの影響は開発体験(DX)への影響です。コンテンツの宣言方法、型、ネームスペースの構成、動的ロード、およびロケール変更時の反応性などが挙げられます。

    TL;DR

    • Intlayer: TanStack Startにおいて最高のパフォーマンスと最小のバンドルサイズ(v8.7.12)を提供します。
    • react-i18next & use-intl: 大規模なエコシステムを持つ成熟した代替案ですが、大幅に重く、最適化がより複雑です。
    • Paraglide: 革新的なツリーシェイキングのアイデアですが、実際には機能しません。TanStack StartにおいてはDXが複雑で、反応性のオーバーヘッドがあります。
    • 避けるべき: General Translation (GT)Lingo.dev。深刻なパフォーマンスの問題、AIクォータの制限、およびベンダーロックインのためです。

    アプリをテストする

    i18nのリーク問題を素早く特定するために、無料のスキャナーを用意しました。こちらで利用可能です。

    intlayer.org

    問題点

    多言語アプリのコストを制限するには、2つの手段が不可欠です。

    • ページやネームスペースごとにコンテンツを分割し、不要なときに辞書全体をロードしないようにする
    • 必要なときにのみ、適切なロケールを動的にロードする

    これらのアプローチの技術的な制限を理解する:

    動的ロード

    動的ロードを行わない場合、ほとんどのソリューションは最初のレンダリングからメッセージをメモリ上に保持し続けます。これにより、多くのルートとロケールを持つアプリでは大きなオーバーヘッドが生じます。

    動的ロードを採用する場合、トレードオフを受け入れることになります。初回のJS量は減りますが、言語の切り替え時に追加のリクエストが発生する場合があります。

    コンテンツの分割

    const t = useTranslation() + t('a.b.c')を中心に構築された構文は非常に便利ですが、実行時に大きなJSONオブジェクトを保持することを助長しがちです。ライブラリがページごとの本当の分割戦略を提供していない限り、このモデルではツリーシェイキングが困難になります。

    調査方法

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

    • Base App(i18nライブラリなし)
    • react-intlayer (v8.7.12)
    • react-i18next (v17.0.2)
    • use-intl (v4.9.1)
    • @lingui/core (v5.3.0)
    • @inlang/paraglide-js (v2.15.1)
    • @tolgee/react (v7.0.0)
    • react-intl (v10.1.1)
    • wuchale (v0.22.11)
    • gt-react (vlatest)
    • lingo.dev (v0.133.9)

    フレームワークはTanStack Startで、10ページ10言語を持つ多言語アプリを使用しました。

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

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

    戦略の構成

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

    GitHubのスター

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

    Star History Chart

    結果の詳細

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

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

    遭遇した問題:

    (General Translation) (gt-react@latest):

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

    (Lingo.dev) ([email protected]):

    • AIのクォータを超過(またはサーバー依存関係のブロック)し、支払いをしない限りビルドやプロダクションへのデプロイがリスクとなります。
    • コンパイラが翻訳コンテンツの約40%を認識していませんでした。動作させるために、すべての.mapをフラットなコンポーネントブロックに書き換える必要がありました。
    • CLIにバグがあり、理由もなく設定ファイルをリセットすることがありました。
    • ビルド時に、新しいコンテンツが追加されると生成されたJSONを完全に消去してしまいました。結果として、数個のキーのために既存の何百ものキーが消失することがありました。
    • TanStack Startでの反応性に問題がありました。ロケール変更時にプロバイダーの強制的な再レンダリングが必要でした。

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

    (Wuchale) ([email protected]):

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

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

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

    Paraglideは革新的でよく考えられたアプローチを提供しています。それにもかかわらず、このベンチマークでは、Next.jsの実装やTanStack Startにおいて、彼らが宣伝していたツリーシェイキングは機能しませんでした。ワークフローとDXも他の選択肢より複雑です。個人的には、プッシュのたびにJSファイルを再生成しなければならないのが好きではありません。これはPRを通じて常にマージ競合のリスクを生み出します。

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

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

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

    TanStack Startでは反応性の問題もありました。ロケール変更時にプロバイダーを強制的に再レンダリングし、ロケール変更イベントを購読して、別の言語でのロードが正しく動作するようにする必要がありました。

    (use-intl) ([email protected]):

    use-intlは、Reactエコシステムにおける最もファッショナブルな「intl」の一部であり(next-intlと同じファミリー)、AIエージェントによって頻繁に推奨されますが、パフォーマンス重視の設定においては間違いであるというのが私の見解です。導入は比較的簡単です。しかし実際には、リークを最適化し制限するプロセスは非常に複雑です。同様に、動的ロード、ネームスぺーシング、TypeScriptの型を組み合わせると、開発が著しく遅くなります。

    TanStack Startでは、Next.js特有の罠(setRequestLocale、静的レンダリング)は回避できますが、根本的な問題は同じです。厳格な規律がなければ、バンドルはすぐに大量のメッセージを抱え込み、ルートごとのネームスペースの維持は苦痛になります。

    (react-i18next) ([email protected]):

    react-i18nextは、JavaScriptアプリのi18nニーズに応えた初期のソリューションの一つであったため、おそらく最も人気のある選択肢です。特定の課題に対するコミュニティプラグインも豊富です。

    それでも、t('a.b.c')上に構築されたスタックと同じ大きな欠点を共有しています。最適化は可能ですが非常に時間がかかり、大規模プロジェクトでは悪い習慣(ネームスペース、動的ロード、型)に陥るリスクがあります。

    メッセージ形式も異なります。use-intlはICU MessageFormatを使用しますが、i18nextは独自の形式を使用しており、これらを混ぜるとツールや移行が複雑になります。

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

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

    (react-intl) ([email protected]):

    react-intlは、Format.jsチームによるパフォーマンス重視の実装です。DXは冗長なままです。const intl = useIntl() + intl.formatMessage({ id: "xx.xx" })は複雑さを増し、JavaScriptの余分な作業を増やし、グローバルなi18nインスタンスをReactツリーの多くのノードに結びつけます。

    4 - 推奨事項

    このTanStack Startベンチマークには、next-translate(Next.jsプラグイン + getStaticProps)に直接相当するものはありません。成熟したエコシステムとt() APIを切望するチームにとって、react-i18nextuse-intlは「妥当な」選択肢であり続けますが、リークを回避するための最適化には多くの時間を投資することを覚悟してください。

    (Intlayer) ([email protected]):

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

    個人的なメモ

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

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