国際化 (i18n) ルーティング
サンプル
Next.js には v10.0.0
以降、国際化 (i18n) ルーティングのサポートが組み込まれています。ロケールのリスト、デフォルトロケール、ドメイン固有のロケールを提供すると、Next.js が自動的にルーティングを処理します。
現在の i18n ルーティングサポートは、react-intl
、react-i18next
、lingui
、rosetta
、next-intl
、next-translate
、next-multilingual
、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 と ミドルウェア を使用すると、回避策 でデフォルトロケールにプレフィックスを追加できます。
例えば、以下はいくつかの言語をサポートする 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
は 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 ヘッダーを上書きすることをサポートしています。このクッキーは言語スイッチャーを使用して設定でき、ユーザーがサイトに戻ってきたときに、/
から正しいロケールの場所にリダイレクトする際にクッキーで指定されたロケールが使用されます。
例えば、ユーザーの 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` をプロップとして受け取ります
return {
props: {
posts,
},
}
}
i18n 設定の制限
locales
: 合計100ロケールdomains
: 合計100ロケールドメインアイテム
知っておくと良いこと: これらの制限は、当初は ビルド時の潜在的なパフォーマンス問題 を防ぐために追加されました。Next.js 12 では ミドルウェア を使用したカスタムルーティングでこれらの制限を回避できます。