今回はNext.jsを使っているサイトをi18n対応する手順を解説します。
国際化ルーティングを行わないと、
サイト品質やSEO順位が著しく下がります。
順番に説明していくので、絶対に行いましょう!
Next jsにおけるi18nについて
Next jsには国際化(i18n)ルーティングをビルトインでサポートしています。
ロケールのリスト、デフォルトロケール、ドメイン固有のロケールを指定すると、Next jsが自動的にルーティングを処理します。
i18nルーティングのサポートは、ルートとロケールの解析を合理化することで、react-intl、react-i18next、lingui、rosettaなどの既存の国際化ライブラリソリューションを補完することを目的としています。
→ ソフトウェアグローバリゼーション入門 国際化I18Nと地域化L10Nによる多言語対応
Next.jsでi18nの初期設定
Next.jsで設定するにはi18n configをnext.config.jsファイルに追加します。
ロケールは、ロケールを定義するために標準化されたフォーマットであるUTS Locale Identifierです。
一般的にロケール識別子は、言語、地域、スクリプトをダッシュで区切ったものです。地域とスクリプトはオプションです。
i18nの例
- jp-JP – 日本で話されている日本語
- en-US – 米国で話されている英語
- nl-NL – オランダで話されているオランダ語
- nl – オランダ語、特定の地域なし
// next.config.js
module.exports = {
i18n: {
// These are all the locales you want to support in
// your application
locales: ['en-US', 'fr', 'nl-NL'],
// This is the default locale you want to be used when visiting
// a non-locale prefixed path e.g. `/hello`
defaultLocale: 'en-US',
// This is a list of locale domains and the default locale they
// should handle (these are only required when setting up domain routing)
// Note: subdomains must be included in the domain value to be matched e.g. "fr.example.com".
domains: [
{
domain: 'example.com',
defaultLocale: 'en-US',
},
{
domain: 'example.nl',
defaultLocale: 'nl-NL',
},
{
domain: 'example.fr',
defaultLocale: 'fr',
},
],
},
}
Next.js i18nにおけるロケールの考え方
ロケールの取り扱いには『サブパスルーティング』と『ドメインルーティング』の2つがあります。
それぞれ解説していきます。
(1) i18nのサブパスルーティング(Sub-path Routing)
サブパスルーティングは、ロケールを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
また、デフォルトのロケールにはプレフィックスがありません。
(2)i18nのドメインルーティング(Domain Routing)
ドメインルーティングは、異なるドメインで提供されるロケールを設定可能です。
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.nl/blog
- example.nl/nl-BE/blog
Next.js i18nのロケールの自動検出
Next.jsはユーザーがアプリケーションのルートを訪れた際に、Accept-Languageヘッダーと現在のドメインに基づいて、ユーザーのロケールを自動的に検出します。
デフォルトのロケール以外のロケールが検出された場合、ユーザーはどちらかにリダイレクトされます。
- サブパスルーティングの場合 … ロケール付きのパス
- ドメインルーティングの場合 … デフォルトで指定されているドメイン
i18nにおける自動検出の例
(1)ドメイン ルーティングを使用する場合
- Accept-Language ヘッダー fr;q=0.9 を持つユーザーが example.com にアクセスすると、example.fr というドメインがデフォルトで fr ロケールを扱うため、example.fr にリダイレクトされます。
(2)サブパスルーティングを使用している場合
- ユーザーは/frにリダイレクトされます。
Next.js i18nのロケールの自動検出を無効にする場合
ロケールの自動検出を無効にするには、下記の設定をnext.config.jsに記載します。
module.exports = {
i18n: {
localeDetection: false,
},
}
localeDetectionをfalseに設定すると、Next.jsはユーザーの好みのロケールに基づいて自動的にリダイレクトを行わなくなります。
その結果、ロケールベースのドメインまたはロケールパスから検出されたロケール情報のみを提供します。
Next.js i18nのロケール情報へのアクセス
ロケール情報へのアクセスは、Next.jsのルーターを介して行うことができます。
たとえば、useRouter()フックを使うと、以下のようなプロパティが利用できます。
- locale … 現在アクティブなロケール
- locales … 設定されているすべてのロケール
- defaultLocale … 設定されているデフォルトのロケール
getStaticProps または getServerSideProps を用いてページをプリレンダリングする場合、ロケール情報は関数に提供されるコンテキストで提供されます。
getStaticPathsを利用する場合、設定されたロケールは、関数のcontextパラメータのlocalesと、設定されたdefaultLocaleのdefaultLocaleで提供されます。
Next.js i18nのロケール間の遷移
ロケール間の遷移には、next/linkまたはnext/routerを使用できます。
next/linkでは、ロケールのpropを指定することで、現在アクティブなロケールとは異なるロケールに移行することができます。
ロケールの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メソッドを直接使用する場合は、transitionオプションで使用するロケールを指定することができます。
例えば、以下のようになります。
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 i18nのNEXT_LOCALEクッキーの利用
Next.jsは、NEXT_LOCALE=the-localeクッキーでaccept-languageヘッダーをオーバーライドすることをサポートしています。
このクッキーを言語切り替えツールで設定すると、ユーザーがサイトに戻ってきたときに、ルートパスから正しいロケールの場所にリダイレクトする際に、クッキーで指定されたロケールを活用することができます。
たとえば、ユーザーが accept-language ヘッダーで fr ロケールを選択していて、NEXT_LOCALE=en クッキーで en ロケールが設定されている場合、ルートパスにアクセスすると、クッキーが削除されるか期限切れになるまで、ユーザーは en ロケールの場所にリダイレクトされます。
Next.js i18nの検索エンジンの最適化
Next.jsはユーザーがどの言語でアクセスしているかを把握しているため、自動的にタグにlang属性を追加します。
Next.jsはページのバリエーションについては知らないので、next/headを使ってhreflangメタタグを追加するかどうかは考える必要があります。
hreflangについては、Google Webmastersのドキュメントで詳しく説明されています。
Static Generationとの連携方法
next exportはNext.jsのルーティングレイヤーを利用しないため、Internationalized Routingはnext exportとは統合されません。
ネクストエクスポートを使用しないハイブリッドなNext.jsアプリケーションは完全にサポートされています。
自動的に静的に最適化されたページ
自動的に静的に最適化されるページでは、ロケールごとにページのバージョンが生成されます。
非ダイナミックなページ
非動的なページでは、各ロケール用のバージョンが生成されます。
getStaticProps は、レンダリングされる各ロケールで呼び出されます。
特定のロケールについてプリレンダリングを行わないようにしたい場合は、 getStaticProps で notFound: true を返せば、そのバージョンのページは生成されません。
export async function getStaticProps({ locale }) {
// Call an external API endpoint to get posts.
// You can use any data fetching library
const res = await fetch(`https://.../posts?locale=${locale}`)
const posts = await res.json()
if (posts.length === 0) {
return {
notFound: true,
}
}
// By returning { props: posts }, the Blog component
// will receive `posts` as a prop at build time
return {
props: {
posts,
},
}
}
動的なページ
動的なページでは、プリレンダリングを行いたいページのロケールのバリエーションをgetStaticPathsから返す必要があります。
パスに対応するparamsオブジェクトとともに、レンダリングしたいロケールを指定するlocaleフィールドを返すこともできます。
例えば、以下のようになります。
export const getStaticPaths = ({ locales }) => {
return {
paths: [
{ params: { slug: 'post-1' }, locale: 'en-US' },
{ params: { slug: 'post-1' }, locale: 'fr' },
],
fallback: true,
}
}
Next.jsにおけるi18nの設定は難しいけど効果あり
今回は、国際化の手順を解説していきました。
国際化には、デフォルトのロケールの設定や可用するロケールの設定なども行わないといけないので難しいです。
しかし、国際化を行って検索エンジンに認識させると検索順位にひっかかりやすくなるため、PVが飛躍的に向上します。
ぜひ、行いましょう!!