mcpField
W
web-i18n-next-intl

web-i18n-next-intl

github.com/agents-inc開発2696 回インストール2 日前に更新
リポジトリ

web-i18n-next-intl について

web-i18n-next-intlは、Next.js App Routerでnext-intlを使う開発者向けの国際化パターンガイドです。ルーティング、メッセージ表示、フォーマット、静的レンダリングを扱い、設定上の注意点も示します。Pages Routerや翻訳コンテンツ管理は対象外です。

主な機能

  1. 01ロケールルーティングと検出の設定
  2. 02コンポーネントや非同期コンテキストでのメッセージ取得
  3. 03複数形、補間、リッチテキストの処理
  4. 04ロケールに応じた日付と数値の表示
  5. 05ロケール別ページの静的生成
  6. 06メッセージキーと書式の型付け

活用シーン

  1. 01ロケールセグメント付きルーティングの構築
  2. 02ページ、メタデータ、Server Actionsのローカライズ
  3. 03ロケールに応じた複数形や日付の表示
  4. 04ロケール別ページの静的生成と型の確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/agents-inc/skills --skill web-i18n-next-intl

インストール後、Claude Code で/web-i18n-next-intlを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

metadata.yaml
reference.md
examples/
core.md
formatting.md
markup.md
pluralization.md

詳細

翻訳後に原文が更新されています。最新の内容は原文をご確認ください。

next-intl の国際化パターン

クイックガイド: useTranslations はメッセージを、useFormatter は日付、数値、リストをレンダリングし、createMiddleware はロケールを検出します。ページまたはレイアウトの先頭で setRequestLocale(locale) を呼び出すことで、静的レンダリングが可能になります。v4.0+ では AppConfig インターフェースを通じて型を登録し、ユーザーが Accept-Language の設定から切り替えた場合にのみロケール Cookie を設定します。ここで紹介するパターンはすべて App Router 用です。Pages Router との連携は別の API であり、ここでの内容は適用されません。

詳細なリソース:

  • examples/core.md — セットアップ、プロバイダー、useTranslations、複数形、フォーマット、静的レンダリング、ロケールの切り替え、型
  • examples/formatting.md — 自動更新される相対時間、リストのフォーマット、組み合わせたフォーマットパターン
  • examples/pluralization.md — 序数、select 内にネストされた複数形、ゼロの場合の処理
  • examples/markup.md — HTML 文字列向けの t.markup():メール本文、フィード、サニタイズ
  • reference.md — 判断ツリー、アンチパターンのコード、ICU 構文の表、セットアップのチェックリスト

該当する方法

  • コンポーネント内でのレンダリング — useTranslations と useFormatter を使います。これらを呼び出す Client Component の上位に NextIntlClientProvider が必要です。examples/core.md を参照してください。
  • コンポーネントツリーの外部でのレンダリング — メタデータ、Server Actions、その他の非同期コンテキストでは getTranslations({ locale, namespace }) を使います。ロケールを明示的に渡す必要があります。こちらも examples/core.md を参照してください。
  • 要素ではなく HTML 文字列を生成する場合 — t.rich() ではなく t.markup() を使い、あわせてサニタイズを行います。examples/markup.md を参照してください。

<critical_requirements>

next-intl のコードを書く前に

各ページとレイアウトの先頭で、フックを使う前に setRequestLocale(locale) を呼び出してください。 これにより next-intl はリクエストなしでロケールを解決できるようになり、ルートを静的にレンダリングできます。

ロケールを使う前に、hasLocale(routing.locales, locale) で検証してください。 検証されていないセグメントはメッセージローダーに渡され、元のルートから大きく離れた場所でエラーになります。

ツリーを NextIntlClientProvider でラップしてください。 クライアントコンポーネントはこのコンテキストからメッセージを読み取るため、コンテキストがないと何もレンダリングされません。

</critical_requirements>


自動検出: next-intl、useTranslations、useFormatter、useLocale、getTranslations、 setRequestLocale、NextIntlClientProvider、defineRouting、createNavigation、hasLocale、ICU メッセージ フォーマット

対象範囲:

  • ロケールセグメントによるルーティング、ロケール検出、ロケール対応ナビゲーション API
  • 補間、複数形、埋め込みマークアップを使ったメッセージのレンダリング
  • ロケールごとの日付、数値、相対時間、リストのフォーマット
  • ビルド時にルートのすべてのロケールバリアントを生成
  • メッセージキーとフォーマットを型付けし、キーが見つからない場合にコンパイル時エラーにする

別途扱う内容:

  • ロケールセグメント以外のフレームワークのルーティングとレンダリング — このスキルでは next-intl がルートに追加する内容を扱います。ルートの定義方法は扱いません
  • 翻訳ファイルの作成と翻訳ベンダーとの同期 — メッセージは JSON として渡され、その出所はこのスキルの対象外です
  • ロケール以外のクライアント状態 — ロケールは useLocale() で読み取り、状態に複製することはありません
  • 日付の算術処理 — Date のフォーマットはこのスキルの役割ですが、Date の生成は対象外です

翻訳は名前空間付きの JSON で、サーバー上でリクエストごとに解決され、コンテキスト経由でクライアントに渡されます。ここから2つの方針が導かれます。まず、ロケールに応じたレンダリングはデフォルトでサーバー側の役割であり、クライアントツリーにはインタラクティブな動作に必要なものだけを持たせます。また、通常はリクエストからロケールを取得するため、静的レンダリングでは別の方法でロケールを渡す必要があります。そのために使うのが setRequestLocale であり、ロケールを読み取る処理より前に呼び出す必要があります。


基本パターン

パターン 1:プロジェクトのセットアップ

4つのモジュールとプロキシを用意します。routing.ts でロケールを宣言し、request.ts でリクエストごとにロケールを解決し、navigation.ts でロケール対応のナビゲーション API を生成します。プロキシは URL、Cookie、Accept-Language からロケールを検出します。

// i18n/routing.ts
import { defineRouting } from "next-intl/routing";

export const routing = defineRouting({
  locales: ["en", "de", "fr"],
  defaultLocale: "en",
});

export type Locale = (typeof routing.locales)[number];

Next.js 16 以降ではプロキシファイルは proxy.ts、それより前のバージョンでは middleware.ts です。どちらの場合もエクスポートは createMiddleware(routing) です。

コード全文:examples/core.md

パターン 2:プロバイダーを使うルートレイアウト

ロケールを検証して設定し、メッセージを読み込んで、ツリーをラップします。

if (!hasLocale(routing.locales, locale)) notFound();
setRequestLocale(locale);

return (
  <html lang={locale}>
    <body>
      <NextIntlClientProvider messages={await getMessages()}>{children}</NextIntlClientProvider>
    </body>
  </html>
);

v4.0 以降では、プロバイダーはサーバー設定からメッセージを継承するため、messages プロパティは省略可能です。

コード全文:examples/core.md

パターン 3:useTranslations

名前空間でキーの範囲を指定し、値は名前付きプレースホルダーで表します。

const t = useTranslations("Profile");

t("greeting", { name: user.name }); // "Hello, Jane!"
t("unreadCount", { count: messages.length });

コード全文:examples/core.md

パターン 4:ICU 構文による複数形

複数形はロケール固有の CLDR ルールに従って選択され、# はフォーマット済みの数値を表示します。=0 はゼロと完全一致する場合に使われ、CLDR カテゴリの zero とは異なります。

{
  "itemCount": "{count, plural, =0 {No items} one {# item} other {# items}}"
}

序数には selectordinal を、列挙値のメッセージには select を使います。

コード全文:examples/core.md、序数とネストについては examples/pluralization.md

パターン 5:t.rich() によるリッチテキスト

メッセージ内のタグは開発者が定義し、コンポーネントに対応付けるため、翻訳者は文全体を一続きの文章として扱えます。

t.rich("terms", {
  link: (chunks) => <a href="/terms">{chunks}</a>,
  bold: (chunks) => <strong>{chunks}</strong>,
});

コード全文:examples/core.md

パターン 6:useFormatter

日付、数値、リスト、相対時間を1つのフックで扱い、それぞれ対応する Intl フォーマッターを使用します。

const format = useFormatter();

format.dateTime(date, { year: "numeric", month: "long", day: "numeric" });
format.number(amount, { style: "currency", currency });
format.relativeTime(date, useNow({ updateInterval: 60_000 }));

コード全文:examples/core.md、相対時間の自動更新とリストについては examples/formatting.md

パターン 7:静的レンダリング

generateStaticParams がロケールのバリエーションを列挙し、setRequestLocale によってリクエストなしで それぞれをレンダリングできるようにします。

export function generateStaticParams() {
  return routing.locales.flatMap((locale) =>
    slugs.map((slug) => ({ locale, slug })),
  );
}

コード全体:examples/core.md

パターン 8:ロケールの切り替え

createNavigation のナビゲーション API は、現在のパスを維持したままロケールを切り替えます。

const router = useRouter();
const pathname = usePathname();

router.replace(pathname, { locale: newLocale });

コード全体:examples/core.md

パターン 9:型安全なキー

AppConfig インターフェースにメッセージの形状を登録すると(v4.0 以降)、誤ったキーはコンパイル エラーになります。

declare module "next-intl" {
  interface AppConfig {
    Locale: (typeof routing.locales)[number];
    Messages: typeof en;
    Formats: typeof formats;
  }
}

JSON をインポートするには、tsconfig.json で allowArbitraryExtensions: true を設定します。引数の型を 推論するには、プラグインの experimental.createMessagesDeclaration が型を生成します。

コード全体:examples/core.md

パターン 10:非同期コンテキスト

getTranslations はフックを使えない場所でも動作します。メタデータはコンポーネントツリーの外部で実行されるため、ロケールは推論されず、明示的に渡します。

const t = await getTranslations({ locale, namespace: "Metadata" });

return { title: t("title"), description: t("description") };

コード全体:examples/core.md


<red_flags>

注意点

実行時に問題が発生します:

  • params を待機せずに読み取ること。Next.js 15 以降では Promise です
  • フックがすでにロケールを読み取った後に setRequestLocale を呼び出すこと。フックが失敗しますが、エラーには呼び出し順ではなくフックの名前が表示されます
  • NextIntlClientProvider の外部で Client Component をレンダリングすること。メッセージが届きません
  • 検証されていないロケールセグメントを使用すること。ルートで宣言されていないパスでは、メッセージのインポートに失敗します
  • Next.js 16 でプロキシの名前が middleware.ts のままになっていること。読み込まれないため、ロケールが検出されません
  • マークアップを含むメッセージに t() を使うこと。タグがそのままテキストとして返されます
  • generateMetadata または Server Action 内で useTranslations を使うこと。これはフックであり、どちらもコンポーネントツリーの外部で実行されます。そこで動作するのは getTranslations({ locale, namespace }) です

意外な動作:

  • setRequestLocale を省略すると、静的レンダリングが気づかれないまま行われなくなります。ルートは引き続き動作しますが、動的にレンダリングされます。もう一方に必要なのが generateStaticParams です。どちらか一方でも欠けているルートはリクエストごとにレンダリングされます
  • t.rich() のタグ関数が受け取る chunks は、単一の要素ではなく配列です
  • useNow() が動作するのはクライアント上だけなので、SSR ではハイドレーションまで初期値が表示されます
  • useTranslations で名前空間を省略すると、すべてのキーが一つのグローバルな空間に置かれ、名前が衝突します
  • v4.0 以降、ロケール Cookie はセッション Cookie であり、ユーザーが Accept-Language の設定とは異なるロケールに切り替えた場合にのみ書き込まれます。ルーティング設定の localeCookie で両方の動作を変更できます
  • Next.js 16 では、プロキシは Edge ランタイムではなく Node ランタイムで実行されます

修正方法を示すコード付きのアンチパターン:reference.md。

</red_flags>

コメント