コンテンツへスキップ

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

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

i18nルーティングのサポートは、現在、既存のi18nライブラリソリューション(例:react-intlreact-i18nextlinguirosettanext-intlnext-translatenext-multilingualtolgeeparaglide-nextnext-intlayerなどを、ルーティングとロケール解析を効率化することで補完するものです。

始める

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

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

一般的に、ロケール識別子は、言語、地域、スクリプトをハイフンで区切った形式(language-region-script)で構成されます。地域とスクリプトはオプションです。例:

  • en-US - アメリカ合衆国で話されている英語
  • nl-NL - オランダで話されているオランダ語
  • nl - オランダ語(地域指定なし)

ユーザーのロケールがnl-BEであり、それが設定にリストされていない場合、利用可能であればnlにリダイレクトされ、そうでなければデフォルトロケールにリダイレクトされます。国の一部の地域のみをサポートする予定がない場合は、フォールバックとして機能する国別ロケールを含めるのが良い慣行です。

next.config.js
module.exports = {
  i18n: {
    // These are all the locales you want to support in
    // your application
    locales: ['en-US', 'fr', 'nl-NL'],
    // This is the default locale you want to be used when visiting
    // a non-locale prefixed path e.g. `/hello`
    defaultLocale: 'en-US',
    // This is a list of locale domains and the default locale they
    // should handle (these are only required when setting up domain routing)
    // Note: subdomains must be included in the domain value to be matched e.g. "fr.example.com".
    domains: [
      {
        domain: 'example.com',
        defaultLocale: 'en-US',
      },
      {
        domain: 'example.nl',
        defaultLocale: 'nl-NL',
      },
      {
        domain: 'example.fr',
        defaultLocale: 'fr',
        // an optional http field can also be used to test
        // locale domains locally with http instead of https
        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: [
      {
        // Note: subdomains must be included in the domain value to be matched
        // e.g. www.example.com should be used if that is the expected hostname
        domain: 'example.com',
        defaultLocale: 'en-US',
      },
      {
        domain: 'example.fr',
        defaultLocale: 'fr',
      },
      {
        domain: 'example.nl',
        defaultLocale: 'nl-NL',
        // specify other locales that should be redirected
        // to this domain
        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とMiddlewareを使用すると、回避策を使用してデフォルトロケールにプレフィックスを追加できます。

例えば、いくつかの言語をサポートするnext.config.jsファイルは次のとおりです。"default"ロケールが意図的に追加されていることに注意してください。

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

次に、Middlewareを使用してカスタムルーティングルールを追加できます。

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)
    )
  }
}

このMiddlewareは、API Routesやフォント、画像などのpublicファイルへのデフォルトプレフィックスの追加をスキップします。デフォルトロケールへのリクエストが行われた場合、プレフィックス/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
// change just the locale and maintain all other route information including href's query
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.jsではNEXT_LOCALE=the-localeというクッキーを設定でき、このクッキーはAccept-Languageヘッダーよりも優先されます。このクッキーは言語スイッチャーを使用して設定でき、ユーザーがサイトに戻ってきた際に、/から正しいロケールの場所へリダイレクトする際に、このクッキーで指定されたロケールが活用されます。

例えば、ユーザーがAccept-Languageヘッダーでfrロケールを優先しているが、NEXT_LOCALE=enクッキーが設定されている場合、/にアクセスした際にユーザーはenロケールの場所にリダイレクトされます。この状態はクッキーが削除されるか期限切れになるまで続きます。

検索エンジン最適化

Next.jsはユーザーがどの言語でサイトを訪れているかを知っているため、自動的に<html>タグにlang属性を追加します。

Next.jsはページのバリアントについて認識しないため、next/headを使用してhreflangメタタグを追加するのは開発者側の責任です。hreflangについては、Google Webmaster向けドキュメントで詳しく学ぶことができます。

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

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

動的ルーティングとgetStaticPropsページ

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

pages/blog/[slug].js
export const getStaticPaths = ({ locales }) => {
  return {
    paths: [
      // if no `locale` is provided only the defaultLocale will be generated
      { params: { slug: 'post-1' }, locale: 'en-US' },
      { params: { slug: 'post-1' }, locale: 'fr' },
    ],
    fallback: true,
  }
}

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

例えば、getStaticPropsを使用する非動的ページが10ページあり、50のロケールが設定されている場合、getStaticPropsは500回呼び出されることになります。ビルドごとに10ページが50バージョン生成されます。

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

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

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

非動的getStaticPropsページ

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

export async function getStaticProps({ locale }) {
  // Call an external API endpoint to get posts.
  // You can use any data fetching library
  const res = await fetch(`https://.../posts?locale=${locale}`)
  const posts = await res.json()
 
  if (posts.length === 0) {
    return {
      notFound: true,
    }
  }
 
  // By returning { props: posts }, the Blog component
  // will receive `posts` as a prop at build time
  return {
    props: {
      posts,
    },
  }
}

i18n設定の制限

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

ご存知の通り:これらの制限は、初期的にビルド時の潜在的なパフォーマンス問題を防ぐために追加されました。Next.js 12のMiddlewareを使用してカスタムルーティングを行うことで、これらの制限を回避できます。