🚀

【Next.js和訳】Advanced Features/Internationalized Routing

10 min read

この記事について

株式会社 UnReact はプロジェクトの一環としてNext.js ドキュメントの和訳を行っています。

この記事は、Advanced Features/Internationalized Routingの記事を和訳したものです。

記事内で使用する画像は、公式ドキュメント内の画像を引用して使用させていただいております。

Internationalized Routing

例 | Examples

Next.js はv10.0.0以降、国際化(i18n)ルーティングをビルトインでサポートしています。

ロケールのリスト、デフォルトロケール、ドメイン固有のロケールを指定すると、Next.js が自動的にルーティングを処理してくれます。

i18n ルーティングのサポートは、ルートとロケールの解析を合理化することで、react-intlreact-i18nextlinguirosettanext-intlなどの既存の国際化ライブラリソリューションを補完することを目的としています。

はじめに | Getting started

まずは、next.config.jsファイルにi18nコンフィグを追加してみましょう。

ロケールは、ロケールを定義するために標準化されたフォーマットであるUTS Locale Identifiersです。

一般的にロケール識別子は、言語、地域、スクリプトをダッシュで区切ったもので、language-region-scriptとなります。

地域とスクリプトはオプションです。

例を挙げます。

  • en-US - 米国で話されている英語
  • nl-NL - オランダで話されているオランダ語
  • nl - オランダ語、特定の地域なし
next.config.js
module.exports = {
  i18n: {
    // アプリケーションでサポートしたいすべてのロケールです
    locales: ['en-US', 'fr', 'nl-NL'],
    // 非ロケールの接頭辞を持つパス(例:`/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,
      },
    ],
  },
}

ロケール戦略 | Locale Strategies

ロケールの取り扱いには 2 つの方法があります。「サブパスルーティング」と「ドメインルーティング」です。

サブパスルーティング | Sub-path Routing

サブパスルーティングでは、ロケールを URL パスに入れます。

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

上記の設定では、en-US, fr, nl-NL がルーティングされるようになり、en-US がデフォルトのロケールとなります。もし、pages/blog.jsがあれば、以下の URL が利用可能になります。

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

デフォルトのロケールにはプレフィックスがありません。

ドメインルーティング | Domain Routing

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

next.config.js
module.exports = {
  i18n: {
    locales: ['en-US', 'fr', 'nl-NL', 'nl-BE'],
    defaultLocale: 'en-US',
    domains: [
      {
        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
  • example.fr/blog
  • example.com/blog example.fr/blog example.nl/blog
  • example.nl/nl-BE/blog

ロケールの自動検出 | Automatic Locale Detection

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

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

  • サブパスルーティングを使用している場合 : ロケール付きのパス
  • ドメインルーティングを使用している場合 : そのロケールがデフォルトで指定されているドメイン

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

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

ロケールの自動検出を無効にする | Disabling Automatic Locale Detection

ロケールの自動検出を無効にするには

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

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

ロケール情報へのアクセス | Accessing the locale information

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

  • locale には、現在アクティブなロケールが入ります。
  • locale には、現在有効なロケールが入ります。
  • defaultLocale は、設定されたデフォルトのロケールを含みます。

getStaticPropsgetServerSideProps を使ってページをプリレンダリングする場合、ロケール情報はその関数に提供されるコンテキストの中で提供されます。

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

ロケール間の移行 | Transition between locales

ロケール間の移行には、next/link または next/router を使用できます。

next/link では、現在アクティブなロケールとは異なるロケールに移行するために、locale のプロップを提供することができます。

local prop が提供されない場合、クライアントの移行時には、現在アクティブな locale が使用されます。例えば、以下のようになります。

import Link from "next/link"
export default function IndexPage(props) {
  return (
    <Link href="/another" locale="fr">
      <a>To /fr/another</a>
    </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がある場合は、ロケールのプレフィックスを自動的に処理しないようにすることができます。

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

Next.js は、accept-language ヘッダーを NEXT_LOCALE=the-locale クッキーで上書きすることをサポートしています。

このクッキーは、言語切り替え機能を使って設定することができ、ユーザーがサイトに戻ってきたときに、クッキーで指定されたロケールを利用して、/から正しいロケールの場所にリダイレクトすることができます。

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

検索エンジン最適化 | Search Engine Optimization

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

Next.js はページのバリエーションについては知らないので、next/headを使ってhreflangメタタグを追加するのはあなた次第です。

hreflangについては、Google Webmasters のドキュメントで詳しく説明されています。

静的生成との相性は? | How does this work with Static Generation?

Internationalized Routing は、next exportとは統合されません。next exportは Next.js のルーティングレイヤーを利用しないためです。ただし、next export を使用しないハイブリッドな Next.js アプリケーションはサポートされています。

ダイナミックルートとgetStaticPropsページ | Dynamic Routes and getStaticProps Pages

ダイナミックルートgetStaticProps を使用しているページでは、プリレンダリングを行いたいページのすべてのロケールのバリエーションを getStaticPaths から返す必要があります。

paths で返される params オブジェクトと一緒に、レンダリングしたいロケールを指定する locale フィールドを返すこともできます。例えば、以下のようになります。

pages/blog/[slug].js
// 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,
  }
}

Automatically Statically Optimized および非ダイナミックな getStaticProps ページ では、ロケールごとにページのバージョンが生成されます

これは、getStaticProps内で設定されているロケールの数に応じて、ビルド時間が長くなる可能性があるため、考慮する必要があります。

例えば、50 個のロケールを構成して、10 個の非動的なページをgetStaticPropsを使って作成した場合、getStaticPropsが 500 回呼ばれることになります。1 回のビルドで 10 ページの 50 バージョンが生成されます。

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

自動的かつ静的に最適化されたページ | Automatically Statically Optimized Pages

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

非道的な getStaticProps ページ | Non-dynamic getStaticProps Pages

非動的な 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,
    },
  }
}

国際化設定の制限 | Limits for the i18n config

  • locales:総ロケール数 100
  • domains: 総ロケールドメイン数 100

注意
これらの制限は、ビルド時の潜在的なパフォーマンス問題を防ぐために最初に追加されました。これらの制限が十分であるかどうかについては、引き続き評価を行っています。

Discussion

ログインするとコメントできます