国際化 (i18n) ルーティング
Next.js は v10.0.0
以降、国際化 (i18n) ルーティングを組み込みでサポートしています。ロケールのリスト、デフォルトロケール、ドメイン固有のロケールを提供すると、Next.js が自動的にルーティングを処理します。
i18n ルーティングサポートは現在、react-intl
、react-i18next
、lingui
、rosetta
、next-intl
、next-translate
、next-multilingual
、typesafe-i18n
、tolgee
などの既存の i18n ライブラリソリューションを補完することを目的としており、ルートとロケール解析を合理化します。
はじめに
開始するには、next.config.js
ファイルに i18n
設定を追加します。
ロケールは UTS ロケール識別子 で、ロケールを定義するための標準化された形式です。
一般的に、ロケール識別子は言語、地域、スクリプトをダッシュで区切って構成されます: language-region-script
。地域とスクリプトはオプションです。例:
en-US
- アメリカで話される英語nl-NL
- オランダで話されるオランダ語nl
- オランダ語、特定の地域なし
ユーザーのロケールが nl-BE
で、設定にリストされていない場合、利用可能であれば nl
にリダイレクトされ、それ以外の場合はデフォルトロケールにリダイレクトされます。
国のすべての地域をサポートする予定がない場合、フォールバックとして機能する国ロケールを含めることが良いプラクティスです。
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,
},
],
},
}
ロケール戦略
ロケール処理には2つの戦略があります: サブパスルーティングとドメインルーティングです。
サブパスルーティング
サブパスルーティングでは、ロケールをURLパスに配置します。
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
デフォルトロケールにはプレフィックスがありません。
ドメインルーティング
ドメインルーティングを使用すると、異なるドメインからロケールを提供するように設定できます:
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
にアクセスすると、example.fr
にリダイレクトされます。これはそのドメインが fr
ロケールをデフォルトで処理するためです。
サブパスルーティングを使用している場合、ユーザーは /fr
にリダイレクトされます。
デフォルトロケールのプレフィックス設定
Next.js 12 と Middleware を使用すると、回避策 でデフォルトロケールにプレフィックスを追加できます。
例えば、以下はいくつかの言語をサポートする next.config.js
ファイルです。意図的に "default"
ロケールが追加されていることに注意してください。
module.exports = {
i18n: {
locales: ['default', 'en', 'de', 'fr'],
defaultLocale: 'default',
localeDetection: false,
},
trailingSlash: true,
}
次に、Middleware を使用してカスタムルーティングルールを追加できます:
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 ルート や public ファイル(フォントや画像など)にデフォルトプレフィックスを追加するのをスキップします。デフォルトロケールへのリクエストがある場合、プレフィックス /en
にリダイレクトします。
自動ロケール検出の無効化
自動ロケール検出は以下のように無効化できます:
module.exports = {
i18n: {
localeDetection: false,
},
}
localeDetection
を false
に設定すると、Next.js はユーザーの優先ロケールに基づいて自動的にリダイレクトしなくなり、上記で説明したロケールベースのドメインまたはロケールパスから検出されたロケール情報のみを提供します。
ロケール情報へのアクセス
Next.js ルーターを介してロケール情報にアクセスできます。例えば、useRouter()
フックを使用すると、以下のプロパティが利用可能です:
locale
には現在アクティブなロケールが含まれます。locales
には設定されたすべてのロケールが含まれます。defaultLocale
には設定されたデフォルトロケールが含まれます。
getStaticProps
または getServerSideProps
でページを プリレンダリング する場合、ロケール情報は関数に提供される コンテキスト で提供されます。
getStaticPaths
を活用する場合、設定されたロケールは関数のコンテキストパラメータの locales
の下に、設定された defaultLocale
は 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 クエリ値など、すべてのルーティング情報を保持しながらロケールのみを切り替える処理を行うには、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 ヘッダーを上書きすることをサポートしています。このクッキーは言語スイッチャーを使用して設定でき、ユーザーがサイトに戻ってきた際に /
から正しいロケールの場所にリダイレクトする際に指定されたロケールが使用されます。
例えば、ユーザーの accept-language ヘッダーが fr
を好むが、NEXT_LOCALE=en
クッキーが設定されている場合、/
にアクセスすると、クッキーが削除または期限切れになるまで en
ロケールの場所にリダイレクトされます。
検索エンジン最適化
Next.js はユーザーが訪問している言語を知っているため、<html>
タグに自動的に lang
属性を追加します。
Next.js はページのバリアントを知らないため、next/head
を使用して hreflang
メタタグを追加するのはあなたの責任です。hreflang
についての詳細は Google Webmasters ドキュメント で学べます。
静的生成との連携方法
国際化ルーティングは Next.js のルーティングレイヤーを利用しないため、
output: 'export'
とは統合されません。output: 'export'
を使用しないハイブリッド Next.js アプリケーションは完全にサポートされています。
動的ルートと getStaticProps
ページ
動的ルート を使用する getStaticProps
ページの場合、プリレンダリングするすべてのロケールバリアントのページを getStaticPaths
から返す必要があります。paths
に対して返される params
オブジェクトと共に、レンダリングしたいロケールを指定する locale
フィールドも返せます。例えば:
export const getStaticPaths = ({ locales }) => {
return {
paths: [
// `locale` が提供されない場合、デフォルトロケールのみが生成されます
{ 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` を prop として受け取ります
return {
props: {
posts,
},
}
}
i18n 設定の制限
locales
: 合計100ロケールdomains
: 合計100ロケールドメイン項目
知っておくと良いこと: これらの制限は、ビルド時の潜在的なパフォーマンス問題 を防ぐために最初に追加されました。Next.js 12 では Middleware を使用したカスタムルーティングでこれらの制限を回避できます。