著者:
    作成:2026-09-02最終更新:2026-09-02

    2026年においてnext-intlは時代遅れなのか?

    VercelがApp Routerを導入し、Pages Routerの組み込みi18n機能を非推奨とした際、その受け皿として急速に支持を集めたのがnext-intlでした。Jan Amann氏による丁寧なドキュメントと迅速なApp Router対応により、コミュニティの標準的な選択肢としての地位を確立しました。

    ではなぜ今、その妥当性を問い直す必要があるのでしょうか?

    過去3年間でWebフロントエンドの設計思想は大きく進化しましたが、next-intlの基本アーキテクチャはそのままだからです。

    Next.jsがReact Server Components(RSC)、ストリーミング、コンパイラによる最適化へと移行する中で、next-intlは依然として国際化を実行時の責務として処理しています。巨大なJSONオブジェクトをクライアントプロバイダー経由で配布し、ブラウザ内でICUフォーマッターを実行し、バンドルサイズを抑えるために手動の名前空間分割に頼っています。

    主なポイント

    開発ペースの落ち着き:

    過去12か月間でnext-intlのコミット数は約187回にとどまり、主にNext.jsのバージョン追従やマイナー修正が中心となっています。

    クライアント実行時の負荷:

    NextIntlClientProvideruseTranslations()を組み合わせると、テキストを表示する前に約12.8 KB(gzip圧縮後、Minifiedで51 KB)のコードが追加されます。これはnext-intlayer(4.3 KB)の約3倍に相当します。

    約90%に及ぶ翻訳データの無駄な配信:

    一般的な構成では、ページに渡される翻訳データの89.8%が他のルート用のものです。/contactにアクセスしただけで、/pricingや管理画面の文言まで同時にダウンロードされます。

    手動による名前空間の管理負担:

    バンドルの肥大化を防ぐにはルートごとに手動で名前空間を切り出す必要があり、本番環境での文言欠落リスクが高まります。

    提携関係による方向性:

    Crowdinの公式パートナーであるため、CLIに完全無料で使えるローカルAI翻訳機能を自前で組み込む強い動機が存在しません。

    保守状況と現代的ツールの比較

    過去12か月間のコミット動向:

    リポジトリ スター数 総コミット数 年間コミット数 最終コミット
    amannn/next-intl stars commits yearly last
    aymericzip/intlayer stars commits yearly last

    直近12か月の実績:

    • amannn/next-intl: 187コミット(フレームワーク追従と不具合修正)。
    • aymericzip/intlayer: 4,343コミット(コンパイラ機能の拡充、IDE拡張機能、MCPサーバー、翻訳エンジンの開発)。

    Star History Chart

    成熟したライブラリは安心感をもたらします。しかし現在のi18n環境は大きく変化しました。ビルド時に不要文言を自動削除し、CI環境でLLMが翻訳を行い、開発者はLanguage Server(LSP)やAIエージェントの支援を受けます。ランタイムに頼る設計では、こうした新しい恩恵を十分に享受できません。

    Next.js 16 App Routerでの性能測定

    10ルート、10言語で構成された典型的なApp Routerアプリケーションでの計測結果:

    動的な JSON 読み込み

    実行時に翻訳を遅延読み込みします

    スコープ付き JSON (ネームスペース)

    ページごとの翻訳ネームスペース

    I18n パフォーマンス ベンチマーク

    この指標は何ですか?

    国際化ライブラリバンドルの合計gzip圧縮サイズ。これには、ツリーシェイキングと縮小化(minification)後のプロバイダーとコンテンツ取得ロジックのみが含まれます。

    なぜ重要なのか?

    ライブラリのサイズが小さければ初期 JavaScript ペイロードが削減され、クライアント側でのダウンロードと実行が高速化されます।

    表示形式

    実ブラウザ環境で本番用gzip圧縮を適用して計測。詳細はNext.jsベンチマークレポートに掲載しています。

    ライブラリ本体のオーバーヘッド

    翻訳ファイル読み込み前の初期フットプリント:

    ライブラリ Gzip圧縮後 Minified
    next-intl@4.9.1 12.8 KB 51.0 KB
    next-intlayer@8.7.12 4.3 KB 13.3 KB

    ページサイズと不要データの混入率

    構成 平均ページJS (gz) 他言語混入率 他ページ混入率 平均コンポーネント (gz)
    ベース(i18nなし) 150.8 KB 0.0% 0.0% 0.7 KB
    next-intl(静的) 163.5 KB 4.2% 89.8% 20.5 KB
    next-intl(動的) 163.4 KB 9.7% 89.9% 20.5 KB
    next-intlayer 152.1 KB 0.0% 0.0% 7.2 KB

    ページ間データ漏洩の構造

    標準的なnext-intl構成では、ルートレイアウトですべてのメッセージを一括取得します。

    app/[locale]/layout.tsx
    export default async function RootLayout({ children, params }) {
      const messages = await getMessages();
    
      return (
        <html>
          <body>
            <NextIntlClientProvider messages={messages}>
              {children}
            </NextIntlClientProvider>
          </body>
        </html>
      );
    }
    

    トップレベルでmessagesをクライアントプロバイダーに渡すため、ブラウザはどのページでもアプリ全体の文言一式を受け取ることになります。/loginを開いたユーザーが、FAQやヘルプ、ダッシュボード専用の文言まで同時にダウンロードしてしまうのです。

    JSONファイルを名前空間ごとに分けることで緩和できますが、どのルートにどの名前空間が必要かを人間が管理し続けるのは骨が折れる作業です。

    Intlayerはこの問題を静的解析で解決します。Intlayerコンパイラが該当ルートで使用されている文言だけを過不足なく抽出するため、ページ間のデータ漏洩率は0.0%となります。

    next-intlがTree-shakingを阻害する要因

    ライブラリのAPIが、実行時に文字列キーを動的評価する構造になっているためです。

    UserProfile.tsx
    "use client";
    
    import { useTranslations } from "next-intl";
    
    export function UserProfile() {
      const t = useTranslations("UserProfile");
    
      return <h2>{t("heading")}</h2>;
    }
    
    UserProfile.tsx
    "use client";
    
    import { useIntlayer } from "next-intlayer";
    
    export function UserProfile() {
      const { heading } = useIntlayer("user-profile");
    
      return <h2>{heading}</h2>;
    }
    

    TurbopackやWebpackは、UserProfile内でどのキーが実際に呼ばれるかを推測できません。実行時エラーを防ぐため、バンドラーは名前空間全体をクライアントコードに含めざるを得ません。一方、Intlayerのようにオブジェクトのプロパティを分割代入する形式であれば、コンパイラが参照関係を把握し、未使用の文言を安全に削除できます。詳細はバンドル最適化をご覧ください。

    開発体験(DX)の違い

    独立したJSONとコンポーネント共配置

    next-intlでは文言がコードから離れたmessages/配下のJSONに保管されます。Intlayerではコンテンツの宣言をコンポーネントと同じディレクトリに配置できます。

    messages/en.json
    {
      "authModal": {
        "title": "Sign in to your account",
        "submitButton": "Continue"
      }
    }
    
    messages/ja.json
    {
      "authModal": {
        "title": "アカウントにサインイン",
        "submitButton": "続ける"
      }
    }
    
    AuthModal.tsx
    import { useTranslations } from "next-intl";
    
    export const AuthModal = () => {
      const t = useTranslations("authModal");
      return (
        <form>
          <h2>{t("title")}</h2>
          <button type="submit">{t("submitButton")}</button>
        </form>
      );
    };
    
    AuthModal.content.ts
    import { t, type Dictionary } from "intlayer";
    
    export default {
      key: "auth-modal",
      content: {
        title: t({
          en: "Sign in to your account",
          ja: "アカウントにサインイン",
        }),
        submitButton: t({
          en: "Continue",
          ja: "続ける",
        }),
      },
    } satisfies Dictionary;
    
    AuthModal.tsx
    import { useIntlayer } from "next-intlayer";
    
    export const AuthModal = () => {
      const { title, submitButton } = useIntlayer("auth-modal");
      return (
        <form>
          <h2>{title}</h2>
          <button type="submit">{submitButton}</button>
        </form>
      );
    };
    

    AuthModal.tsxの名称変更や削除を行うと、対応するコンテンツ定義も自然に追従します。

    単なる補完と厳格な型安全性の差異

    next-intlIntlMessagesを宣言すればエディタの補完が有効になります。

    global.d.ts
    import en from "./messages/en.json";
    
    type Messages = typeof en;
    
    declare global {
      interface IntlMessages extends Messages {}
    }
    

    しかし型チェックの基準はデフォルト言語に限られます。ja.jsonからキーが抜け落ちていてもTypeScriptは警告を出さず、CIビルドも通過してしまい、本番で文言が欠落します。

    Intlayerはすべての言語のコンテンツ定義から直接型を生成します。strictModeを有効にすれば、いずれかの言語で翻訳が欠落している場合にビルドエラーとなり、事前にミスを防げます。

    開発環境とAIエコシステム

    機能 next-intl Intlayer
    VS Code拡張機能 ❌ なし 公式拡張機能
    Language Server (LSP) ❌ なし 専用LSP
    AIエージェント用MCPサーバー ❌ なし 組み込みMCPサーバー
    エージェントスキル ❌ なし 利用可能なスキル群
    インコンテキストビジュアルCMS ❌ なし 無料かつオープンソース

    LSPやMCPサーバーが備わっていることで、AIアシスタントがプロジェクトの多言語構造を正しく把握し、正確な補完や更新を行えます。

    Crowdinとのパートナーシップ

    next-intlはCrowdinと公式パートナーシップを結んでいます。スポンサーシップはOSSの継続に有益ですが、プロジェクトの方向性にも影響を与えます。外部TMSとの連携を前提とする設計のため、ローカル環境で無料利用できるAI翻訳ワークフローをCLI自体に持たせる優先順位は低くなります。

    Intlayerはオープンなアプローチを基本に据えています。

    ローカルAI翻訳機能(intlayer fill):

    自身のOpenAI、Anthropic、Mistral、GeminiのAPIキーを使って、不足しているキーを自動検出して補完できます。

    セルフホスト可能なビジュアルCMS:

    Intlayer CMSを導入すれば、非エンジニアのメンバーがWeb上で文言を直接編集し、変更をGitへ反映できます。

    オープンなライセンス:

    すべてのツール群がApache 2.0ライセンスのもとで公開されています。

    今もnext-intlが適しているケース

    ネストされた複雑な複数形分岐や序数フォーマットを多用するシステムでは、実績のあるnext-intlのICU実装が強みを発揮します。

    すでに組織全体でCrowdinを活用した翻訳業務が確立されている場合、親和性高く運用できます。

    現行のシステムが問題なく稼働し、バンドルサイズが大きなボトルネックになっていないのであれば、直ちに移行する必要はありません。

    既存のnext-intl環境を向上させるには?

    Intlayerは、next-intlの主要な関数やフック(useTranslationsgetTranslations、ルーティングヘルパーなど)のシグネチャをそのまま再現するドロップイン互換パッケージを提供しています。コンポーネントを全面的に書き直すことなく、コンパイラ主導の最適化の恩恵を受けることができます。

    導入はコマンド1行で完了します:

    bash
    npx intlayer init --interactive
    

    この対話型CLIは以下の作業を自動で行います:

    1. @intlayer/next-intl互換パッケージをインストール。
    2. バンドラーのエイリアスを設定し、既存のインポート(next-intlnext-intl/server)をシームレスにIntlayerへルーティング。これにより古いライブラリをpackage.jsonから安全に削除できます。
    3. エディタ内でのLanguage Server(LSP)診断、ビルド時のルート間リーク排除(完全なTree-shaking)、ローカルAI翻訳ワークフローを大規模なリファクタリングなしで即座に有効化します。

    詳しい手順については、以下のガイドをご覧ください:

    • 互換レイヤーの提供: next-intl互換レイヤーを使うことで、コード内のuseTranslations記述を保ったまま最適化ビルドを導入できます。
    • 移行ガイド: 既存のJSONファイルを型付きコンテンツに移行するためのnext-intl移行ガイドを用意しています。
    • 段階的な併用: ランタイムにnext-intlを残したまま、Intlayerとnext-intlを併用して型の恩恵やローカルAI翻訳のみを取り入れることも可能です。

    自社サイトのバンドルサイズと翻訳漏れは無料のi18n SEOスキャナーで診断できます。

    おすすめの関連記事

    コメント

    まだコメントはありません。最初のコメントを共有しましょう。

    関連記事

    最新の投稿