コンテンツへスキップ

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

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

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

はじめに

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

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

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

  • 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-USfr、およびnl-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とミドルウェアを使用すると、回避策を使用して、デフォルトのロケールにプレフィックスを追加できます。

たとえば、いくつかの言語をサポートする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
// 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ウェブマスターのドキュメントを参照してください。

これは静的生成でどのように機能しますか?

国際化ルーティングは、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を使用したカスタムルーティングでこれらの制限を回避できます。