Skip to main content

Next.js 国際化完全ガイド

App Router で next-intl を導入し、ロケールルーティングを設定して、AI による翻訳を自動化する方法を解説します。

1

next-intl をインストール

next-intl は、Next.js App Router のロケールルーティング、メッセージの読み込み、翻訳フックを 1 つで処理するパッケージです。

Terminal
npm install next-intl
next-i18next ではなく next-intl を選ぶ理由は、App Router とサーバーコンポーネント向けに構築されているためです。next-i18next は Pages Router 向けに設計されており、App Router への対応は限定的です。
2

i18n リクエスト設定を作成

メッセージを読み込む src/i18n/request.ts と、ロケールを定義する src/i18n/routing.ts の 2 ファイルを作成します。これらのファイルで、next-intl がメッセージとルートを解決する方法を設定します。

src/i18n/routing.ts
// src/i18n/routing.ts
import { defineRouting } from 'next-intl/routing';
import { createNavigation } from 'next-intl/navigation';

export const routing = defineRouting({
  locales: ['en', 'de', 'ja', 'es'],
  defaultLocale: 'en',
  localePrefix: 'as-needed',  // /about for en, /de/about for de
});

export const { Link, redirect, usePathname, useRouter } =
  createNavigation(routing);
Turbopack(Next.js 15 のデフォルトバンドラー)では、next.config.js に experimental.turbo.resolveAlias が必要です。この設定がないと、"Couldn't find next-intl config file" エラーが発生します。
3

ミドルウェアを設定

ロケール検出、URL の書き換え、リダイレクトを処理する middleware.ts を追加します。ミドルウェアはすべてのリクエストを仲介し、適切なロケールが適用されるようにします。

middleware.ts
// middleware.ts  <- Must be in project ROOT, not src/
import createMiddleware from 'next-intl/middleware';
import { routing } from './src/i18n/routing';

export default createMiddleware(routing);

export const config = {
  matcher: ['/((?!api|_next|.*\\..*).*)'],
};
middleware.ts は src/ 内ではなく、必ずプロジェクトのルートディレクトリに配置してください。next-intl で最もよくある設定ミスです。
4

[locale] フォルダー構成を設定

アプリのルートを app/[locale]/ 内へ移動します。generateStaticParams を追加し、ビルド時にロケールごとのページを生成します。これにより、/en/about、/de/about などの URL 構造が作成されます。

app/[locale]/layout.tsx
// app/[locale]/layout.tsx
import { routing } from '@/i18n/routing';

export function generateStaticParams() {
  return routing.locales.map((locale) => ({ locale }));
}
翻訳を使用するすべての page.tsx と layout.tsx で、必ず setRequestLocale(locale) を呼び出してください。呼び出さないと Next.js は動的レンダリングへフォールバックし、ビルドのパフォーマンスが大幅に低下します。
5

ルートレイアウトを更新

getMessages() でメッセージを読み込み、ロケールのルートレイアウトにある NextIntlClientProvider へ渡します。html の lang 属性にはロケールパラメーターを設定してください。

app/[locale]/layout.tsx
// app/[locale]/layout.tsx
import { NextIntlClientProvider } from 'next-intl';
import { getMessages, setRequestLocale } from 'next-intl/server';
import { routing } from '@/i18n/routing';
import { notFound } from 'next/navigation';

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

export default async function LocaleLayout({
  children,
  params,
}: {
  children: React.ReactNode;
  params: { locale: string };
}) {
  const { locale } = await params;
  if (!routing.locales.includes(locale as any)) notFound();

  setRequestLocale(locale);
  const messages = await getMessages();

  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider locale={locale} messages={messages}>
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}
NextIntlClientProvider では locale prop を明示的に指定する必要があります。省略すると、クライアントコンポーネントでデバッグが難しい問題が発生します。
6

コンポーネントで翻訳を使用

サーバーコンポーネントでは getTranslations(非同期、await)を、クライアントコンポーネントでは useTranslations(フック)を使用します。コンポーネントのレンダリング場所に応じて選択してください。サーバーコンポーネントなら、翻訳を JavaScript バンドルから完全に除外できます。

app/[locale]/page.tsx
// Server Component (default)
import { getTranslations, setRequestLocale } from 'next-intl/server';

export default async function AboutPage({
  params,
}: { params: { locale: string } }) {
  const { locale } = await params;
  setRequestLocale(locale);
  const t = await getTranslations('AboutPage');

  return <h1>{t('title')}</h1>;
}

// Client Component ('use client')
'use client';
import { useTranslations } from 'next-intl';

export default function SearchBar() {
  const t = useTranslations('SearchBar');
  return <input placeholder={t('placeholder')} />;
}
翻訳済みコンテンツにはサーバーコンポーネントを優先してください。翻訳文字列がクライアントの JavaScript バンドルに含まれないため、ユーザーの読み込み時間を短縮できます。
7

SEO を追加:メタデータと hreflang

generateMetadata を使用し、ロケール別のページタイトルと説明を生成します。hreflang タグ用に alternates.languages を追加し、検索エンジンが各ページのすべての言語版を検出できるようにします。

app/[locale]/layout.tsx
// app/[locale]/layout.tsx or any page.tsx
import { getTranslations } from 'next-intl/server';
import { routing } from '@/i18n/routing';

export async function generateMetadata({
  params,
}: { params: { locale: string } }) {
  const { locale } = await params;
  const t = await getTranslations({ locale, namespace: 'Metadata' });

  return {
    title: t('title'),
    description: t('description'),
    alternates: {
      languages: Object.fromEntries(
        routing.locales.map((l) => [l, `/${l}`])
      ),
    },
  };
}
Vercel では metadataBase を設定しないと、ビルド時に localhost が正規 URL として生成される場合があります。ルートレイアウトの metadataBase には、必ず本番環境のドメインを設定してください。
8

エラーページと Not Found ページを処理

error.tsx と not-found.tsx は通常のロケールレイアウト外でレンダリングされる可能性があるため、特別な処理が必要です。ルートの not-found.tsx でローカライズ済みエラーメッセージを表示するには、独自の i18n プロバイダー設定が必要です。

app/[locale]/error.tsx
// app/[locale]/error.tsx
'use client';
import { useTranslations } from 'next-intl';

export default function Error() {
  const t = useTranslations('Error');
  return (
    <div>
      <h1>{t('title')}</h1>
      <p>{t('description')}</p>
    </div>
  );
}

// app/not-found.tsx (root level -- needs own provider)
import { routing } from '@/i18n/routing';

export default async function GlobalNotFound() {
  return (
    <html lang={routing.defaultLocale}>
      <body>
        <h1>404 - Page Not Found</h1>
      </body>
    </html>
  );
}
Next.js がローカライズ済みの 404 ページをレンダリングするのは、コードから notFound() を明示的に呼び出した場合だけです。一致するページがない不明なルートでは、ローカライズ版ではなく、Next.js のデフォルト 404 が表示されます。
9

翻訳を自動化

i18n の設定が完了したら、IDE から直接 AI を使用してメッセージファイルを翻訳するか、CI/CD パイプラインで i18n Agent CLI を使用し、デプロイのたびに翻訳を自動化します。

Terminal
# In your IDE, ask your AI assistant:
> Translate messages/en.json to German, Japanese, and Spanish

✓ messages/de.json created (1.1s)
✓ messages/ja.json created (1.4s)
✓ messages/es.json created (1.0s)
next-intl-localechain を使用すると、インテリジェントなロケールフォールバックを利用できます。ブラジルポルトガル語がない場合でも、pt-BR のユーザーには英語ではなく pt-PT の翻訳が表示されます。

翻訳品質を自動管理

i18n-validate を使用し、キー不足や壊れたプレースホルダーをリリース前に検出します。実際の翻訳が完成する前に、i18n-pseudo の疑似翻訳で UI をテストできます。

Next.js i18n 向けオープンソースツール

これらのオープンソースパッケージは、Next.js の国際化ワークフローでよくある問題を解決します。

next-intl-localechain

標準の next-intl は、翻訳がない場合にデフォルトロケールへ直接フォールバックします。そのため、適切な pt-PT の翻訳があっても、ブラジルポルトガル語のユーザーには英語が表示されます。next-intl-localechain はインテリジェントなフォールバックチェーンを追加し、関連するロケールの翻訳をディープマージするため、地域ロケールのユーザーには常に最も近い翻訳が表示されます。

src/i18n/request.ts
import { getRequestConfig } from 'next-intl/server';
import { withLocaleChain } from 'next-intl-localechain';

export default getRequestConfig(withLocaleChain({
  loadMessages: (locale) =>
    import(`../../messages/${locale}.json`).then(m => m.default),
  defaultLocale: 'en'
}));
ロケールチェーン全体の翻訳を自動的にディープマージ
ポルトガル語、スペイン語、フランス語、ドイツ語などの組み込みチェーン
不足しているメッセージファイルをエラーなく安全にスキップ
1 行で設定し、既存の getRequestConfig をラップ
GitHub で見る

@i18n-agent/cli

ターミナルを離れずに Next.js のメッセージファイルを翻訳できるコマンドラインツールです。ファイルの直接翻訳、ジョブ状態の確認、結果のダウンロードが可能です。API キー認証を使用して CI/CD パイプラインでも動作し、ローカリゼーションのワークフローを完全に自動化できます。

Terminal
# Install the CLI
npm install -g @i18n-agent/cli

# Authenticate
i18nagent login

# Translate your message files
i18nagent translate ./messages/en.json --lang de,ja,es

# Or use in CI/CD with an API key
export I18N_AGENT_API_KEY=your-key-here
i18nagent translate ./messages/en.json --lang de,ja,es
ターミナルから JSON、YAML、PO などの i18n ファイル形式を翻訳
CI/CD 対応:環境変数で認証し、パイプラインを自動化
ジョブ状態の追跡、失敗したジョブの再開、結果のダウンロード
スクリプトと自動化に使用できる、機械可読の JSON 出力
GitHub で見る

よくある落とし穴

"Unable to find next-intl locale"

ミドルウェアがリクエストに一致していません。middleware.ts がプロジェクトルートにあるか、matcher パターンが静的ファイルを正しく除外しているか、ロケールがルーティング設定に含まれているかを確認してください。

意図しない動的レンダリング

ページまたはレイアウトに setRequestLocale(locale) がありません。この呼び出しがない場合、next-intl はヘッダーまたは Cookie からロケールを検出するため、動的レンダリングが強制され、静的生成ができなくなります。

i18n により並列ルートが機能しない

並列ルート(@modal)とルートのインターセプト((.)photo)には、[locale] 動的セグメントとの既知の非互換性があります。こうした高度なルーティングパターンでは、回避策としてミドルウェアベースのルーティングを使用してください。

言語を切り替えると現在のルートが失われる

ロケールを切り替える際は、usePathname() を使用して現在のパス名を保持し、ロケールセグメントだけを置き換えます。動的ルートのパラメーターは、新しいロケール向けに再解決する必要があるため注意してください。

推奨ファイル構成

Project Structure
my-nextjs-app/
├── middleware.ts              # Locale routing (project root!)
├── next.config.mjs
├── messages/
│   ├── en.json                # Source messages
│   ├── de.json
│   └── ja.json
├── src/
│   ├── i18n/
│   │   ├── request.ts         # Message loading config
│   │   └── routing.ts         # Locale definitions
│   └── app/
│       └── [locale]/
│           ├── layout.tsx     # Root locale layout
│           ├── page.tsx       # Home page
│           ├── error.tsx      # Localized error page
│           ├── not-found.tsx  # Localized 404
│           └── about/
│               └── page.tsx
└── package.json

i18n Agent を今すぐ試す

翻訳ファイルをここにドロップ

JSON, YAML, PO, XML, CSV, Markdown, Properties

またはクリックしてファイルを選択

翻訳先言語

登録不要すぐに見積もり

next-intl-localechain によるロケールフォールバック

pt-BR のような地域ロケールで翻訳キーが不足している場合、next-intl は最初に親ロケールの pt を確認せず、デフォルトロケールへ直接移ります。

Terminal
npm install next-intl-localechain
Configuration
import { withLocaleChain } from 'next-intl-localechain';

export default withLocaleChain({
  fallbacks: {
    'pt-BR': ['pt', 'en'],
    'zh-Hant-HK': ['zh-Hant', 'zh', 'en'],
  },
  defaultLocale: 'en',
  loadMessages: (locale) => import(`./messages/${locale}.json`),
});

対応フレームワークと 75 種類の組み込みチェーンの完全な一覧については、ロケールフォールバックガイドをご覧ください。 Learn more →

よくある質問