Sponsor
ChatHubChatHub Use GPT-4, Gemini, Claude 3.5 and more chatbots side-by-side
ここをクリック
Menu

国際化(i18n)ルーティング

Next.jsはv10.0.0から国際化(i18n)ルーティングを組み込みでサポートしています。ロケールのリスト、デフォルトロケール、ドメイン固有のロケールを提供すると、Next.jsが自動的にルーティングを処理します。

i18nルーティングのサポートは、現在、react-intlreact-i18nextlinguirosettanext-intlnext-translatenext-multilingualtolgeeparaglide-nextnext-intlayerなどの既存のi18nライブラリソリューションを補完することを目的としており、ルートとロケールの解析を合理化します。

はじめに

開始するには、next.config.jsファイルにi18n設定を追加します。

ロケールはUTS ロケール識別子で、ロケールを定義するための標準化された形式です。

一般的に、ロケール識別子は、ダッシュで区切られた言語、地域、スクリプトで構成されます:language-region-script。地域とスクリプトはオプションです。例:

  • en-US - アメリカ合衆国で話される英語
  • nl-NL - オランダで話されるオランダ語
  • nl - オランダ語、特定の地域なし

ユーザーのロケールがnl-BEで、設定に記載されていない場合、利用可能であればnlにリダイレクトされ、そうでない場合はデフォルトロケールにリダイレクトされます。 すべての国の地域をサポートする予定がない場合、フォールバックとして機能する国のロケールを含めることをお勧めします。

next.config.js
module.exports = {
  i18n: {
    // アプリケーションでサポートするすべてのロケール
    locales: ['en-US', 'fr', 'nl-NL'],
    // デフォルトで使用したいロケール(非ロケール接頭辞のパス e.g. `/hello`を訪れる際)
    defaultLocale: 'en-US',
    // ロケールドメインとそのデフォルトロケール(ドメインルーティングを設定する場合のみ必要)
    // 注意:サブドメインはドメイン値に含める必要があります(例:「fr.example.com」)
    domains: [
      {
        domain: 'example.com',
        defaultLocale: 'en-US',
      },
      {
        domain: 'example.nl',
        defaultLocale: 'nl-NL',
      },
      {
        domain: 'example.fr',
        defaultLocale: 'fr',
        // オプションのhttpフィールドを使用して、
        // httpsの代わりにhttpでローカルにロケールドメインをテストできます
        http: true,
      },
    ],
  },
}

ロケール戦略

ロケール処理には2つの戦略があります:サブパスルーティングとドメインルーティング。

サブパスルーティング

サブパスルーティングは、URLパスにロケールを配置します。

next.config.js
module.exports = {
  i18n: {
    locales: ['en-US', 'fr', 'nl-NL'],
    defaultLocale: 'en-US',
  },
}

上記の設定では、en-USfrnl-NLがルーティング可能で、en-USがデフォルトロケールです。pages/blog.jsがある場合、以下のURLが利用可能になります:

  • /blog
  • /fr/blog
  • /nl-nl/blog

デフォルトロケールには接頭辞がありません。

ドメインルーティング

ドメインルーティングを使用すると、異なるドメインからロケールを提供できます:

next.config.js
module.exports = {
  i18n: {
    locales: ['en-US', 'fr', 'nl-NL', 'nl-BE'],
    defaultLocale: 'en-US',
 
    domains: [
      {
        // 注意:サブドメインはドメイン値に含める必要があります
        // 例:www.example.comは、予期されるホスト名の場合に使用する必要があります
        domain: 'example.com',
        defaultLocale: 'en-US',
      },
      {
        domain: 'example.fr',
        defaultLocale: 'fr',
      },
      {
        domain: 'example.nl',
        defaultLocale: 'nl-NL',
        // このドメインにリダイレクトされる
        // 他のロケールを指定
        locales: ['nl-BE'],
      },
    ],
  },
}

例えば、pages/blog.jsがある場合、以下のURLが利用可能になります:

  • example.com/blog
  • www.example.com/blog
  • example.fr/blog
  • example.nl/blog
  • example.nl/nl-BE/blog

自動ロケール検出

ユーザーがアプリケーションのルート(通常は/)を訪れると、Next.jsはAccept-Languageヘッダーと現在のドメインに基づいて、ユーザーが希望するロケールを自動的に検出しようとします。

デフォルトロケール以外のロケールが検出された場合、ユーザーは以下のいずれかにリダイレクトされます:

  • サブパスルーティングを使用する場合: ロケール接頭辞のパス
  • ドメインルーティングを使用する場合: そのロケールをデフォルトとして処理するドメイン

ドメインルーティングを使用する場合、Accept-Languageヘッダーがfr;q=0.9のユーザーがexample.comを訪れると、そのドメインがデフォルトでfrロケールを処理するため、example.frにリダイレクトされます。

サブパスルーティングを使用する場合、ユーザーは/frにリダイレクトされます。

デフォルトロケールの接頭辞

Next.js 12とミドルウェアを使用すると、ワークアラウンドでデフォルトロケールに接頭辞を追加できます。

例えば、以下は数か国語をサポートするnext.config.jsファイルです。「default」ロケールが意図的に追加されていることに注意してください。

next.config.js
module.exports = {
  i18n: {
    locales: ['default', 'en', 'de', 'fr'],
    defaultLocale: 'default',
    localeDetection: false,
  },
  trailingSlash: true,
}

次に、ミドルウェアを使用してカスタムルーティングルールを追加できます:

middleware.ts
import { NextRequest, NextResponse } from 'next/server'
 
const PUBLIC_FILE = /\.(.*)$/
 
export async function middleware(req: NextRequest) {
  if (
    req.nextUrl.pathname.startsWith('/_next') ||
    req.nextUrl.pathname.includes('/api/') ||
    PUBLIC_FILE.test(req.nextUrl.pathname)
  ) {
    return
  }
 
  if (req.nextUrl.locale === 'default') {
    const locale = req.cookies.get('NEXT_LOCALE')?.value || 'en'
 
    return NextResponse.redirect(
      new URL(`/${locale}${req.nextUrl.pathname}${req.nextUrl.search}`, req.url)
    )
  }
}

このミドルウェアは、APIルートと、フォントや画像などの公開ファイルへの接頭辞の追加をスキップします。デフォルトロケールへのリクエストがあった場合、/enにリダイレクトします。

自動ロケール検出の無効化

自動ロケール検出は以下のように無効にできます:

next.config.js
module.exports = {
  i18n: {
    localeDetection: false,
  },
}

localeDetectionfalseに設定されている場合、Next.jsはユーザーの優先ロケールに基づいて自動的にリダイレクトしなくなり、上記で説明したロケールベースのドメインまたはロケールパスから検出されたロケール情報のみを提供します。

ロケール情報へのアクセス

Next.jsルーターを介してロケール情報にアクセスできます。例えば、useRouter()フックを使用すると、以下のプロパティが利用可能です:

  • localeは現在アクティブなロケールを含みます。
  • localesは設定されたすべてのロケールを含みます。
  • defaultLocaleは設定されたデフォルトロケールを含みます。

getStaticPropsまたはgetServerSideProps事前レンダリングする場合、ロケール情報は関数に提供されるコンテキストで提供されます。

getStaticPathsを活用する場合、設定されたロケールは関数のコンテキストパラメーターのlocalesの下に、設定されたデフォルトロケールはdefaultLocaleの下に提供されます。

ロケール間の遷移

next/linkまたはnext/routerを使用してロケール間を遷移できます。

next/linkの場合、現在アクティブなロケールとは異なるロケールに遷移するためにlocaleプロパティを提供できます。localeプロパティが提供されない場合、クライアント遷移中は現在アクティブなlocaleが使用されます。例:

import Link from 'next/link'
 
export default function IndexPage(props) {
  return (
    <Link href="/another" locale="fr">
      To /fr/another
    </Link>
  )
}

next/routerメソッドを直接使用する場合、遷移オプションを介して使用するべきlocaleを指定できます。例:

import { useRouter } from 'next/router'
 
export default function IndexPage(props) {
  const router = useRouter()
 
  return (
    <div
      onClick={() => {
        router.push('/another', '/another', { locale: 'fr' })
      }}
    >
      to /fr/another
    </div>
  )
}

動的ルートのクエリ値や非表示のhrefクエリ値などのすべてのルーティング情報を維持しながら、localeのみを切り替える場合は、hrefパラメーターをオブジェクトとして提供できます:

import { useRouter } from 'next/router'
const router = useRouter()
const { pathname, asPath, query } = router
// ロケールのみを変更し、hrefのクエリを含むすべてのルート情報を維持
router.push({ pathname, query }, asPath, { locale: nextLocale })

オブジェクト構造の詳細については、router.pushこちらを参照してください。

すでにロケールを含むhrefがある場合、ロケール接頭辞の自動処理をオプトアウトできます:

import Link from 'next/link'
 
export default function IndexPage(props) {
  return (
    <Link href="/fr/another" locale={false}>
      To /fr/another
    </Link>
  )
}

NEXT_LOCALEクッキーの活用

Next.jsでは、NEXT_LOCALE=the-localeクッキーの設定が可能で、これはAccept-languageヘッダーよりも優先されます。このクッキーは言語切り替えを使用して設定でき、サイトに戻ってきたユーザーが/からそのロケールの場所に遷移する際に、クッキーで指定されたロケールを使用します。

例えば、ユーザーが fr ロケールを accept-language ヘッダーで指定しているが、NEXT_LOCALE=en クッキーが設定されている場合、/ にアクセスすると、クッキーが削除または期限切れになるまで、ユーザーは en ロケールの場所にリダイレクトされます。

検索エンジン最適化

Next.jsはユーザーが訪れている言語を把握しているため、自動的に <html> タグに lang 属性を追加します。

Next.jsはページのバリアントを認識できないため、next/head を使用して hreflang メタタグを追加するのはユーザーの責任です。hreflang の詳細については、Google ウェブマスター向けドキュメントで確認できます。

これは静的生成とどのように連携しますか?

国際化ルーティングは、Next.jsのルーティング層を活用しないため、output: 'export' と統合されないことに注意してください。output: 'export' を使用しないハイブリッドNext.jsアプリケーションは完全にサポートされています。

動的ルートと getStaticProps ページ

動的ルートgetStaticProps を使用するページの場合、プリレンダリングする必要があるページのすべてのロケールバリアントを getStaticPaths から返す必要があります。paths オブジェクトから返される params オブジェクトに加えて、レンダリングするロケールを指定する locale フィールドも返すことができます。例:

pages/blog/[slug].js
export const getStaticPaths = ({ locales }) => {
  return {
    paths: [
      // `locale` が提供されない場合、defaultLocaleのみが生成されます
      { params: { slug: 'post-1' }, locale: 'en-US' },
      { params: { slug: 'post-1' }, locale: 'fr' },
    ],
    fallback: true,
  }
}

自動的に静的に最適化された非動的 getStaticProps ページの場合、ページの各ロケールのバージョンが生成されます。これは、getStaticProps 内で構成されているロケールの数に応じて、ビルド時間が増加する可能性があるため、考慮することが重要です。

例えば、50のロケールが構成され、getStaticProps を使用する10の非動的ページがある場合、getStaticProps は500回呼び出されます。10ページの50バージョンが各ビルド時に生成されます。

getStaticProps を使用する動的ページのビルド時間を短縮するには、fallback モードを使用します。これにより、ビルド時にプリレンダリングする最も人気のあるパスとロケールのみを getStaticPaths から返すことができます。その後、Next.jsは要求に応じて残りのページをランタイムでビルドします。

自動的に静的に最適化されたページ

自動的に静的に最適化されたページの場合、ページの各ロケールのバージョンが生成されます。

非動的 getStaticProps ページ

非動的 getStaticProps ページの場合、上記と同様に各ロケールのバージョンが生成されます。getStaticProps は、レンダリングされる各 locale で呼び出されます。特定のロケールからプリレンダリングを除外する場合は、getStaticProps から notFound: true を返すことで、そのページのバリアントは生成されません。

export async function getStaticProps({ locale }) {
  // 外部APIエンドポイントを呼び出して投稿を取得
  // 任意のデータフェッチライブラリを使用できます
  const res = await fetch(`https://.../posts?locale=${locale}`)
  const posts = await res.json()
 
  if (posts.length === 0) {
    return {
      notFound: true,
    }
  }
 
  // `{ props: posts }` を返すことで、Blogコンポーネントは
  // ビルド時に `posts` をプロップとして受け取ります
  return {
    props: {
      posts,
    },
  }
}

i18n 設定の制限

  • locales: 合計100ロケール
  • domains: 合計100ロケールドメインアイテム

補足: これらの制限は、ビルド時のパフォーマンス上の問題を防ぐために最初に追加されました。Next.js 12のミドルウェアを使用したカスタムルーティングでこれらの制限を回避できます。