国際化(i18n)ルーティング
Next.jsはv10.0.0
から国際化(i18n)ルーティングを組み込みでサポートしています。ロケールのリスト、デフォルトロケール、ドメイン固有のロケールを提供すると、Next.jsが自動的にルーティングを処理します。
i18nルーティングのサポートは、現在、react-intl
、react-i18next
、lingui
、rosetta
、next-intl
、next-translate
、next-multilingual
、tolgee
、paraglide-next
、next-intlayer
などの既存の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'],
// デフォルトで使用したいロケール(非ロケール接頭辞のパス e.g. `/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
を訪れると、そのドメインがデフォルトでfr
ロケールを処理するため、example.fr
にリダイレクトされます。
サブパスルーティングを使用する場合、ユーザーは/fr
にリダイレクトされます。
デフォルトロケールの接頭辞
Next.js 12とミドルウェアを使用すると、ワークアラウンドでデフォルトロケールに接頭辞を追加できます。
例えば、以下は数か国語をサポートするnext.config.js
ファイルです。「default」ロケールが意図的に追加されていることに注意してください。
module.exports = {
i18n: {
locales: ['default', 'en', 'de', 'fr'],
defaultLocale: 'default',
localeDetection: false,
},
trailingSlash: true,
}
次に、ミドルウェアを使用してカスタムルーティングルールを追加できます:
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
にリダイレクトします。
自動ロケール検出の無効化
自動ロケール検出は以下のように無効にできます:
module.exports = {
i18n: {
localeDetection: false,
},
}
localeDetection
がfalse
に設定されている場合、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
// ロケールのみを変更し、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ヘッダーよりも優先されます。このクッキーは言語切り替えを使用して設定でき、サイトに戻ってきたユーザーが/
からそのロケールの場所に遷移する際に、クッキーで指定されたロケールを使用します。
例えば、ユーザーが fr
ロケールを accept-language ヘッダーで指定しているが、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
フィールドも返すことができます。例:
export const getStaticPaths = ({ locales }) => {
return {
paths: [
// `locale` が提供されない場合、defaultLocaleのみが生成されます
{ 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` をプロップとして受け取ります
return {
props: {
posts,
},
}
}
i18n 設定の制限
locales
: 合計100ロケールdomains
: 合計100ロケールドメインアイテム
補足: これらの制限は、ビルド時のパフォーマンス上の問題を防ぐために最初に追加されました。Next.js 12のミドルウェアを使用したカスタムルーティングでこれらの制限を回避できます。