Communitygithub.com

mwasobaddy/skills

Next.js App Router アプリの国際化を設計・実装します。フォルダ構造、ロケールプレフィックス付きルーティング、型付きメッセージファイル、ICU 複数形、hreflang によるローカライズ SEO をカバー。複数言語や翻訳の追加・修正、next-intl/Lingui/next-i18next の選択、[locale] セグメントとプロキシ/ミドルウェアの連携、英語専用アプリのローカライズ URL 移行時に使用します。i18n、localization、multi-language、translations、locale routing、lang attribute、hreflang、next-intl で発動します。

skills とは?

このスキルは、Next.js App Router アプリケーションの国際化(i18n)を、設計から実装まで体系的にガイドします。フォルダ構成、ロケールプレフィックス付きルーティング、型安全なメッセージファイル、ICU メッセージ構文、hreflang を使ったローカライズ SEO など、多言語対応に必要なすべてをカバーします。主な対象は、Claude Code、Coder、Cursor、Codex などの AI エージェントです。このスキルは、まず next-intl(新規プロジェクトのデフォルト)、Lingui、next-i18next から適切なライブラリを選択するよう指示し、次に canonical なフォルダ構造とセットアップ手順を提供します。さらに、`setRequestLocale` のレガシー化や `params` が Promise になったことなど、よくある落とし穴を列挙し、既存アプリの移行手順や回避すべきアンチパターンも詳述しています。このスキルは、エージェントが i18n、localization、multi-language、translations、locale routing などのキーワードを含むプロンプトを受け取ったときに発動するよう設計されています。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/mwasobaddy/skills/tree/HEAD/nextjs-i18n

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Next.js i18n

Next.js アプリを複数言語に対応させるための構造ガイド:フォルダレイアウトの要件、不適切な場合に何が壊れるか、そして避けるべきこと。

最初にライブラリを選択する

App Router を使用する新しいプロジェクトでは、デフォルトで next-intl を選択してください。ビルドプラグイン、マクロステップ、ロケールごとのインスタンス管理は不要で、メッセージはソースロケールから型付けされたプレーンな JSON です。

ライブラリ使用するタイミングコスト
next-intl (デフォルト)新しい App Router プロジェクト、任意のチームサイズなし — プレーン JSON、コード生成不要
@lingui/reactコンパイル時の抽出と小さなランタイムが必要な場合、または React/Vue/Astro 間で統一された i18n レイヤーが必要な場合SWC または Babel マクロプラグイン、正確なバージョン固定が必要
next-i18nextチームがすでに i18next を知っている場合、または Pages Router のコードを移行する場合リクエストごとのインスタンス、getT/useT の分割
Next.js 組み込み i18nPages Router のみ。App Router では絶対に使用しない該当なし

Next.js の i18n: { locales, defaultLocale } 設定は Pages Router のみ です。App Router では機能しません。App Router でのロケールルーティングは [locale] セグメントとプロキシを使用します。エージェントに App Router プロジェクトで組み込み i18n を「有効化」させないでください。

バンドルコストやライブラリごとのトレードオフを含む完全な比較は、references/library-comparison.md を参照してください。

標準的なアーキテクチャ

静的ディクショナリは src/ の外 に配置し、バンドルとインポートパスが明確になるようにし、すべてのロケール関連の仕組みを1つのフォルダにまとめます。

messages/                      # 純粋な静的ディクショナリ — /src の外に配置
├── en.json                     # ソースオブトゥルース;他のすべてのロケールはこれをミラーリング
├── de.json
└── ja.json
src/
├── i18n/
│   ├── routing.ts              # defineRouting: ロケール + defaultLocale
│   ├── request.ts              # getRequestConfig: ロケール解決、メッセージ読み込み
│   └── navigation.ts           # createNavigation: ロケール対応 Link/redirect
├── proxy.ts                    # ロケール検出 + リダイレクト (Next 16; 以前は middleware.ts)
└── app/
    ├── [locale]/               # ローカライズされたアプリ全体をラップ
    │   ├── layout.tsx          # ロケールの検証、<html lang>、generateStaticParams
    │   └── page.tsx
    └── not-found.tsx           # グローバル 404 は [locale] の外に配置

この構造を機能させるルール:

  • [locale] はルートの動的セグメントです。 ローカライズされたすべてのものはこの下に配置されます。ルートレベルのルート(not-found.tsx、error.tsx、api/)は外側に残し、無条件に i18n フックを呼び出してはいけません。呼び出すと実行時にクラッシュします。
  • 1つのソースロケール。 messages/en.json がマスターです。他のすべてのファイルはその形状を完全にミラーリングする必要があります。
  • すべてのロケール知識は src/i18n/ に。 コンポーネントは @/i18n/navigation から Link をインポートし、next/link からは絶対にインポートしません。言語を追加する場所は1か所です。

セットアップ(新規プロジェクト)

以下の順序で実行してください。順序が重要です。proxy.ts は routing.ts に依存し、レイアウトは request.ts に依存します。

  1. next-intl をインストールし、next.config.ts を createNextIntlPlugin() でラップします。
  2. src/i18n/routing.ts を作成し、defineRouting を設定します。
  3. src/proxy.ts を作成し、createMiddleware(routing) とデフォルトのマッチャーをエクスポートします。
  4. src/i18n/request.ts を作成し、ロケールを解決する getRequestConfig を設定します。
  5. app/** を app/[locale]/** の下に移動し、generateStaticParams を追加してパラメータを検証します。
  6. src/i18n/navigation.ts を作成し、createNavigation(routing) を設定します。
  7. messages/en.json を作成し、ハードコードされた文字列を useTranslations に置き換えます。
  8. <html lang> を設定し、ローカライズされたメタデータと hreflang を追加します。

各ステップの正確なファイル内容は、references/next-intl-setup.md を参照してください。

落とし穴

i18n コードを書く前にこのセクションを読んでください。これらは何時間も費やす原因となる失敗です。

  • setRequestLocale はレガシーです。 Next.js 16.3+ では、getRequestConfig 内で next/root-params を使用してロケールを読み取ってください。ほとんどのガイド、チュートリアル、LLM のトレーニングデータは依然として setRequestLocale(params.locale) を教えています。これはまだ動作し、後方互換性があると文書化されていますが、古い API です。Next < 16.3 でのみ使用してください。どちらの場合でも generateStaticParams は依然として必要です。
  • params は Next 15+ で Promise です。 const {locale} = await params。params.locale を実行時エラーではなく型エラーとして扱ってください。
  • ロケールを検証しないと、未知の値がレンダリングされます。 hasLocale(routing.locales, locale) と notFound() を使用してください。これがないと、/xx/about が偽の URL で英語のページをレンダリングします。
  • Next 16 では middleware.ts ではなく proxy.ts です。 middleware.ts は非推奨です。プロキシは Node.js のみで実行され、Edge ランタイムはサポートされません。Lingui と next-i18next は Edge セーフなプロキシを宣伝していますが、Edge が必要な場合は、無料の勝利ではなく、真のトレードオフがあります。
  • 翻訳された文字列を連結しないでください。 "You have " + count + " items" は日本語やアラビア語では非文法的です。ICU を使用してください:"{count, plural, =1 {One item} other {# items}}"。
  • メッセージ内で + やテンプレートリテラルを使って補間しないでください。 名前付きプレースホルダー({name})を使用して、語順を変更できるようにしてください。
  • 日付と数値はハイドレーションの不一致を引き起こします。 サーバーとブラウザでの new Date() はタイムゾーンとクロックが異なります。getRequestConfig で timeZone を設定するか、相対時間の場合はクライアントのみでレンダリングしてください。症状:「テキストコンテンツがサーバーレンダリングされた HTML と一致しません」。
  • フォーマットはロケールに依存します。 明示的なロケールなしの toLocaleString() は、リクエストされた ロケールではなく、ランタイム ロケールでフォーマットします。useFormatter() / getFormatter() を使用してください。
  • getRequestConfig はリクエストごとに実行され、React によってキャッシュされます。 リクエストごとに変化する状態(カート、ユーザー設定)をここに配置しないでください。ロケールごとの設定のみを配置してください。
  • サーバーアクションとルートハンドラにはルートパラメータがありません。 await locale() はサーバーコンポーネントでのみ機能します。アクションとハンドラでは、ロケールを明示的に引数として渡してください。

既存アプリの改造

これは設定変更ではなく、URL に影響を与える移行として扱ってください。[locale] プレフィックスを追加すると、既存のすべての URL の形状が変わります。

以下の順序で計画してください:

  1. 文字列を棚卸しする — scripts/find-untranslated.sh を実行して、ハードコードされた JSX テキストを見つけます。
  2. コーディング前にルーティング戦略を選択します。 ロケールプレフィックス常時(/en/about、/de/about)がデフォルトで、曖昧さがなく、hreflang もクリーンです。localePrefix: 'as-needed' はデフォルトロケールをベアパスで提供します — より良い URL ですが、SEO シグナルは弱くなります。今決めてください;後で切り替えると完全な再移行になります。
  3. コンポーネントに触れる前にルーティングを立ち上げます。 proxy.ts + [locale] を英語のみのメッセージで動作させます。
  4. ルートを [locale]/ の下に移動します — これは大規模な機械的な git mv です。git log --follow が有用であり続けるように、1つのコミットで行います。
  5. 文字列を名前空間ごとに置き換えます。ファイルごとではなく、名前空間ごとに。1つの名前空間をエンドツーエンドで翻訳して、キーの形状が増殖する前に証明します。
  6. 変更されたすべての URL に対してリダイレクトを追加します。 /about → /en/about(または検出されたロケールに 307)。これがないと、既存のすべてのランキングが低下します。必須です。
  7. hreflang + ローカライズされたメタデータは、すべてのロケールがルートを持った後にのみ追加します。 そうしないと、404 を指す代替 URL を発行することになります。

リダイレクトとロールバックの注意事項を含む完全な移行シーケンスは、references/migration.md を参照してください。

避けるべきこと

  • messages/ を src/ の中に配置しないでください。 ソースツリー内のサーバー専用ディクショナリは、クライアントに何が送られるかを曖昧にします。
  • ローカライズされたルートで next/link から Link をインポートしないでください。 ロケールプレフィックスが失われ、静かに間違った言語にリンクします。
  • href でロケールプレフィックスを手書きしないでください(href={/${locale}/about})。@/i18n/navigation の Link を使用してください。これはプレフィックス付き、as-needed、ドメインルーティングを処理します。
  • 1つのフラットなキー名前空間を使用しないでください。 機能/ページごとにグループ化してください。フラットなキーは衝突し、検索不可能です。
  • ビルド時に自動翻訳をソースオブトゥルースとして使用しないでください。 機械出力はドラフトです;レビューされずにコミットすると、エラーが永続化され、追跡不可能になります。
  • ロケールを routing.locales に追加して完了としないでください。 翻訳された messages/xx.json がないと、すべてのページがキーをレンダリングするか、静かにフォールバックします。
  • ルーティング戦略を混在させないでください。 as-needed とプレフィックス付き URL を指す hreflang の組み合わせは、自己責任の SEO バグです。

参考資料

必要に応じてオンデマンドで読んでください:

  • references/library-comparison.md — next-intl、Lingui、next-i18next の選択;バンドルコスト、移行トリガー、ライブラリごとのセットアップ形状。
  • references/next-intl-setup.md — 標準セットアップのすべてのファイル、コピー&ペースト可能。
  • references/messages-and-keys.md — 名前空間構造、キー命名、ICU 構文、日付/数値/リストのフォーマット、型付きメッセージ。
  • references/seo.md — hreflang、<html lang>、ローカライズされた generateMetadata、canonical、サイトマップ代替、ロケールスイッチャーの SEO ルール。
  • references/migration.md — 既存アプリの改造、リダイレクト、ロールバック。

スクリプト

  • scripts/find-untranslated.sh — プロジェクト内のハードコードされた可能性が高いユーザー向け文字列をフラグ付けし、作業を開始する前に規模を測定できるようにします。改造のステップ 1 として実行します。
  • scripts/run-trigger-eval.sh — このスキルの description が適切なプロンプトで発動するかを測定します。説明を変更するときに使用します。

説明のテスト

description は、エージェントがこのスキルをロードする前に確認する唯一のものです。これが発動しない場合、以下のものはすべて無意味です。

bash scripts/run-trigger-eval.sh --dry-run          # 計画を表示、API 呼び出しなし
bash scripts/run-trigger-eval.sh --split train     # 12 クエリ x 3 実行
bash scripts/run-trigger-eval.sh --split validation --out report.json

クエリセット:evals/trigger-queries.json — 12 の発動すべき、8 の発動すべきでない、60/40 に分割して固定の train と validation セットにします。

説明を編集する際は:

  • train セットのみを使用して失敗を修正します。最後に validation セットに一度だけ触れて、変更が汎化されているかを確認します — train が改善され validation が低下した場合、過学習です。
  • 発動すべき失敗は説明が狭すぎることを意味します:クエリのキーワードではなく、クエリが表す 概念 を追加します。
  • 発動すべきでない失敗は説明が広すぎることを意味します:スキルがカバーしない内容を記述します。
  • 編集前に再実行します。発動は非決定的であり、1回の実行では何も証明できません。

失敗したクエリからキーワードを追加しないでください — そうすると説明がキーワードリストのようになり、実際のプロンプトと一致しなくなります。

関連スキル