コンテンツにスキップ

Next.js で国際化を実装する方法

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

i18n ルーティングサポートは、現在、react-intlreact-i18nextlinguirosettanext-intlnext-translatenext-multilingualtolgeeparaglide-nextnext-intlayergt-react、その他多くのライブラリを、ルーティングとロケール解析の合理化によって補完することを目的としています。

はじめに

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

ロケールは、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 と Proxy により、デフォルトロケールにカスタムプレフィックスを追加できます。回避策

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

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

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

proxy.ts
import { NextRequest, NextResponse } from 'next/server'
 
const PUBLIC_FILE = /\.(.*)$/
 
export async function proxy(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)
    )
  }
}

このProxyは、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>
  )
}

locale のみを切り替えて、動的ルートのクエリ値や非表示の href クエリ値などのルーティング情報をすべて維持するには、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 Cookie を設定できます。これは accept-language ヘッダーよりも優先されます。この Cookie は言語セレクターを使用して設定でき、ユーザーがサイトに戻ってきたときに、/ から正しいロケール場所へのリダイレクト時に Cookie に指定されたロケールを活用します。

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

検索エンジン最適化

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

Next.js はページのバリエーションを認識しないため、next/head を使用して hreflang メタタグを追加するのはユーザーの責任です。hreflang についての詳細は、Google Search Console のドキュメント を参照してください。

静的生成との連携方法

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

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

getStaticProps動的ルーティングを使用するページの場合、プリレンダリングしたいすべてのロケールバリエーションは、getStaticPaths から返される必要があります。pathsparams オブジェクトに加えて、レンダリングしたいロケールを指定する 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 内で設定されているロケールの数によっては、ビルド時間が長くなる可能性があるため、考慮することが重要です。

例えば、50 のロケールが設定されており、10 の非動的ページが getStaticProps を使用している場合、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 の Proxy を使用したカスタムルーティングで、これらの制限を回避できます。