Next.js には v10.0.0
から国際化(i18n)ルーティングのビルトインサポートがあります。ロケールの一覧やデフォルトのロケール、特定ドメインのロケールを指定すると、Next.js がルーティングを自動的に処理してくれます。
i18n ルーティングはルートとロケールの解析を効率化することで、react-intl
, react-i18next
, lingui
, rosetta
, next-intl
やその他の既存 i18n ライブラリによる実装を補完することを目的としています。
まずはじめに、next.config.js
ファイルに i18n
の設定を追加します。
ロケールには、ロケールを定義するための標準化されたフォーマットである UTS ロケール識別子(UTS Locale Identifiers)を用います。
一般的にロケール識別子は、ダッシュ区切りの言語、地域、スクリプトで構成されます: 言語-地域-スクリプト
。地域とスクリプトは任意項目です。例としては、以下のようになります:
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',
},
],
},
}
ロケールを扱う戦略は 2 つあります: サブパスルーティングとドメインルーティングです。
サブパスルーティングはロケールを 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
デフォルトのロケールはプレフィックスがありません。
ドメインルーティングを用いると、異なるドメインごとにロケールを設定できます。
// 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',
// このドメインにリダイレクトすべき他のロケールを指定
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
With Next.js 12 and Middleware, we can add a prefix to the default locale with a workaround.
For example, here's a next.config.js
file with support for a few languages. Note the "default"
locale has been added intentionally.
// next.config.js
module.exports = {
i18n: {
locales: ['default', 'en', 'de', 'fr'],
defaultLocale: 'default',
localeDetection: false,
},
trailingSlash: true,
}
Next, we can use Middleware to add custom routing rules:
// pages/_middleware.ts
import { NextRequest, NextResponse } from 'next/server'
const PUBLIC_FILE = /\.(.*)$/
const stripDefaultLocale = (str: string): string => {
const stripped = str.replace('/default', '')
return stripped
}
export function middleware(request: NextRequest) {
const shouldHandleLocale =
!PUBLIC_FILE.test(request.nextUrl.pathname) &&
!request.nextUrl.pathname.includes('/api/') &&
request.nextUrl.locale === 'default'
return shouldHandleLocale
? NextResponse.redirect(
`/en${stripDefaultLocale(request.nextUrl.pathname)}${
request.nextUrl.search
}`
)
: undefined
}
This Middleware skips adding the default prefix to API Routes and public files like fonts or images. If a request is made to the default locale, we redirect to our prefix /en
.
ユーザーがアプリケーションのルート(通常は /
)にアクセスすると、Next.js は Accept-Language
ヘッダーと現在のドメインに基づいて、ユーザーが好むロケールを自動的に検出しようとします。
デフォルトのロケール以外のロケールが検出された場合、ユーザーはいずれかのロケールにリダイレクトされます:
ドメインルーティングを使用している場合、Accept-Language
ヘッダー fr;q=0.9
のユーザーが example.com
を訪れると、そのドメインは fr
ロケールをデフォルトとするため、ユーザーは example.fr
にリダイレクトされます。
サブパスルーティングを使用している場合、ユーザーは /fr
にリダイレクトされます。
ロケールの自動検出は次の設定で無効にできます:
// next.config.js
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
prop を指定できます。locale
prop を指定しない場合、現在有効なロケールがクライアント遷移の際に使用されます。例としては、以下のようになります:
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>
)
}
Note that to handle switching only the locale
while preserving all routing information such as dynamic route query values or hidden href query values, you can provide the href
parameter as an object:
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 })
See here for more information on the object structure for router.push
.
既にロケールを含む 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
ロケールにリダイレクトされます。
Next.js はユーザーが訪れている言語を把握しているので、自動的に <html>
タグに lang
属性を追加します。
Next.js はページの複数バージョンについて把握していないので、next/head
を使って hreflang
メタタグを追加するかどうかは実装次第です。hreflang
の詳細については Google Webmasters ドキュメントを参照してください。
next export
は Next.js ルーティングレイヤーを利用しないため、国際化されたルーティングはnext export
と統合されないことに注意してください。next export
を使用しないハイブリッド Next.js アプリケーションは完全にサポートされています。
getStaticProps
PagesFor pages using getStaticProps
with Dynamic Routes, all locale variants of the page desired to be prerendered need to be returned from getStaticPaths
. Along with the params
object returned for paths
, you can also return a locale
field specifying which locale you want to render. For example:
// 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,
}
}
For Automatically Statically Optimized and non-dynamic getStaticProps
pages, a version of the page will be generated for each locale. This is important to consider because it can increase build times depending on how many locales are configured inside getStaticProps
.
For example, if you have 50 locales configured with 10 non-dynamic pages using getStaticProps
, this means getStaticProps
will be called 500 times. 50 versions of the 10 pages will be generated during each build.
To decrease the build time of dynamic pages with getStaticProps
, use a fallback
mode. This allows you to return only the most popular paths and locales from getStaticPaths
for prerendering during the build. Then, Next.js will build the remaining pages at runtime as they are requested.
For pages that are automatically statically optimized, a version of the page will be generated for each locale.
自動的に静的最適化(Automatic Static Optimization)されたページについては、ロケールごとにページのバージョンが生成されます。
動的ではない 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` を props として受け取ります。
return {
props: {
posts,
},
}
}
locales
: 100 total localesdomains
: 100 total locale domain itemsNote: These limits have been added initially to prevent potential performance issues at build time. You can workaround these limits with custom routing using Middleware in Next.js 12.