Next.jsの多言語対応ライブラリ next-i18nextの使い方
目次
はじめに
こんにちは。フロントエンドエンジニア兼デザイナーのKimuraです。
以前、Reactでの多言語対応を実現するreact-i18nextの使い方について記事を書きました。今回は、Next.jsで多言語対応を実現するnext-i18nextの使い方をご紹介いたします。 本記事の内容は、next-i18nextの公式ドキュメントを参考としています。
利用技術
- Next.js
- TypeScript
- next-i18next
next-i18nextの役割とは
Next.jsは、ディレクトリ構造をベースとしたルーティングシステムを持っており、URL内のサブパスまたはドメインにロケール情報を追加することが可能です。(詳細はこちら)
しかし、Next.jsにはページやコンポーネント上で多言語メッセージを切り替える機能は含まれておりません。この部分を担うのが、next-i18nextとなります。
next-i18nextによる多言語対応の実装
本記事では、URL内にロケール情報を追加し、それに基づいてページ内の表示言語が切り替わるように実装します。また、next-i18nextのカスタマイズ方法について記載します。
URLへロケール情報を追加
まずは、ロケール情報をURLに追加します。ロケール情報は、サブパス(http://example/ja
)かドメイン名末尾(http://example.ja/
)として追加することができます。今回はサブパスへ追加します。
以下の通りに設定することで、日本語と英語のロケールが有効となります。デフォルトロケールは日本語とします。なお、next-i18nextの公式ドキュメントにならい、i18nの設定を別ファイルに分割しています。
1 2 3 4 5 6 |
module.exports = { i18n: { locales: ['ja', 'en'], defaultLocale: 'ja' }, }; |
1 2 3 4 5 6 |
const { i18n } = require('./next-i18next.config'); module.exports = { i18n, // 既存設定... } |
これにより、ロケール情報のサブパスを持つルーティングが有効となります。
localhost:3000/ja
localhost:3000/en
表示している画面のソースは以下のとおりです。現在は言語切替を実装していないため、仮置きで両言語のメッセージを表示しています。
1 2 3 4 5 |
export default function Home() { return ( <h1>タイトル / Title</h1> ) } |
インストール
続いて、画面上のメッセージ切替を実装します。ここからは、next-i18nextを利用するため、まずはインストールを行います。
1 2 3 4 |
# npm npm i next-i18next react-i18next i18next # yarn yarn add next-i18next react-i18next i18next |
メッセージJSONファイルの配置
日本語、英語それぞれのJSONファイルを配置します。
1 2 3 4 5 |
{ "Common": { "title": "タイトル" } } |
1 2 3 4 5 |
{ "Common": { "title": "Title" } } |
_app.tsx変更
カスタムAppのエクスポートをappWithTranslation
経由に変更します。
1 2 3 4 5 6 7 |
import { appWithTranslation } from 'next-i18next'; function App({ Component, pageProps }: AppProps) { // 省略 } export default appWithTranslation(App); |
言語切替実装
最後に、各ページ内での言語切替を実装します。こちらは2つのステップがあります。
まず、getStaticProps
またはgetServerSideProps
内で、必要なロケールと名前空間(JSON)を読み込み対象に設定します。TypeScriptの場合、locale
はstring | undefined
型と判定されます。それによるエラー回避のため、!
を付与して値の存在を明示しています。
1 2 3 4 5 6 7 8 9 10 11 |
import { GetStaticProps } from "next"; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; export const getStaticProps: GetStaticProps = async ({ locale }) => ({ props: { ...(await serverSideTranslations( locale!, ['common'] )) } }); |
そして、各コンポーネント内で言語切替を実装します。これで完成です。
1 2 3 4 5 6 7 8 |
import { useTranslation } from 'next-i18next'; export default function Home() { const { t } = useTranslation('common'); return ( <h1>{t('Common.title')}</h1> ); } |
serverSideTranslationsのカスタマイズ
serverSideTranslations
の引数によって、ユースケースに沿ったカスタマイズが可能となります。
第1引数:initialLocale
型 | 必須/任意 |
---|---|
string | 必須 |
初期表示対象のロケールです。指定されたロケールは、デフォルトでロード対象となります。通常はgetStaticProps
またはgetServerSideProps
の引数として受け取れるlocale
を指定して、リクエストごとに可変となるようにします。
第2引数:namespacesRequired
型 | 必須/任意 |
---|---|
string[] | 任意 |
ロード対象JSONの名前空間の配列です。指定された全ての名前空間がロード対象となります。未指定か空配列が指定された場合は、どの名前空間もロードしません。通常は、画面内で利用する名前空間を全て指定します。
next-i18nextでは名前空間(JSON)をコンポーネントごとに分割することが推奨されます。各コンポーネントでは、単一の名前空間を読み込めるようになっています。分割しない場合、該当ページで利用しないメッセージも読み込まれることになり、パフォーマンスに影響します。
下記は、フッター用の名前空間を定義した例です。
1 2 3 4 5 |
{ "Footer": { "title": "フッター" } } |
1 2 3 4 5 |
{ "Footer": { "title": "Footer" } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import Footer from "../components/Footer"; export const getStaticProps = async ({ locale }) => ({ props: { ...(await serverSideTranslations( locale!, ['common', 'fotter'] )) }, }); export default function Home() { const { t } = useTranslation('common'); return ( <> <h1>{t('Common.title')}</h1> <Footer /> </> ); } |
1 2 3 4 5 6 7 8 9 10 |
import { useTranslation } from "react-i18next"; const Footer = () => { const { t } = useTranslation('footer'); return ( <div>{t('Footer.title')}</div> ) }; export default Footer; |
第3引数:configOverride
型 | 必須/任意 |
---|---|
UserConfig | 任意 |
指定したオブジェクトの内容で、next.config.js
内に定義したi18n設定をオーバーライドします。
第4引数:extraLocales
型 | 必須/任意 |
---|---|
false | string[] | 任意 |
初期ロード対象のロケールを追加指定します。デフォルトでは、第1引数で指定した初期表示対象ロケールのみがロード対象となります。
URL内のロケール情報に関わらず、getFixedT
経由で特定ロケールを常に使用する場合などで指定できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import { TFunction } from "next-i18next"; export const getStaticProps: GetStaticProps = async ({ locale }) => ({ props: { ...(await serverSideTranslations( locale!, ['common'], null, ['ja'] )) } }); export default function Home() { const { i18n, t } = useTranslation('common'); const ja: TFunction = i18n.getFixedT('ja'); return ( <> <h1>{t('Common.title')}</h1> <p>{ja('Common.title')}</p> </> ); } |
英語ページへアクセスした場合、以下の表示となります。
Next.js構成を利用せずにnext-i18nextを利用する場合
URLのロケール情報をカスタマイズしたいなどの理由で、next.config.js
にi18nを指定しない場合を挙げられます。アプリ全体ではなく、ページごとにi18n設定を読み込むという方法です。ただし、Next.jsで組み込める構成は国際的な標準であることにご留意ください。
この構成の場合、locale
を通したロケール情報の取得はできなくなりますが、params
経由などで任意の値を初期ロケールに指定することができます。
1 2 3 |
module.exports = { // i18n設定を削除 } |
1 2 3 4 5 |
import nextI18NextConfig from '../next-i18next.config.js'; // 省略 export default appWithTranslation(App, nextI18NextConfig); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import nextI18NextConfig from '../next-i18next.config.js'; export const getStaticPaths: GetStaticPaths = async () => ({ fallback: "blocking", paths: [] }); export const getStaticProps: GetStaticProps = async ({ params }) => ({ props: { ...(await serverSideTranslations( String(params?.locale), ['common'] )) } }); |
おわりに
next-i18nextで多言語対応を行う方法についてご紹介しました。手軽に多言語対応の組み込みとカスタマイズができるライブラリですので、ぜひご利用ください。