🌐
Next.js App router で多言語化対応 w/next-i18n-router
next-i18n-routerとi18nextを使用して、すっきりとi18nに対応します。
加えて、react-i18nextによってCSRにも対応します。
サンプルをGitHubで公開しています。
next-i18n-routerの設定
まずはnext-i18n-routerのREADMEに書いてある通りです(ほぼ英訳)。
bash
npm install next-i18n-router
typescript
export const i18nConfig = {
locales: ["en", "ja"],
defaultLocale: "ja",
};
/i18n/config.ts
bash
└── app
└── [locale]
├── layout.js
└── page.js
bash
import { i18nRouter } from "next-i18n-router";
import { NextRequest } from "next/server";
import { i18nConfig } from "@/i18n/config";
export function middleware(request: NextRequest) {
return i18nRouter(request, i18nConfig);
}
// only applies this middleware to files in the app directory
export const config = {
matcher: "/((?!api|static|.*\\..*|_next).*)",
};
ここまでの設定によって、クライアントサイド/サーバーサードそれぞれ以下のような形で、ブラウザの言語設定にしたがったロケールを取得できるようになります。
typescript
'use client';
import { useCurrentLocale } from 'next-i18n-router/client';
import i18nConfig from '@/i18nConfig';
function ExampleClientComponent() {
const locale = useCurrentLocale(i18nConfig);
...
}
typescript
// server component
function ExampleServerComponent({ params: { locale } }) {
...
}
そして、default localeをjaにしている場合、ブラウザの設定が日本語の場合はlocaleのパスが省略されます。たとえばproducts へのルーティングの場合、パスは
日本語: /products
英語: /en/products
となります。
i18nextによるSserver Componentの対応
i18nextを導入してSSGされるコンポーネントに対応します。
typescript
npm install i18next
typescript
import { InitOptions } from "i18next";
import { en } from "./en";
import { ja } from "./ja";;
export const i18nextInitOptions: InitOptions = {
lng: "ja",
fallbackLng: "ja",
resources: {
en,
ja,
},
};
i18n/config
typescript
export const ja = {
translation: {
hello: "こんにちは!",
},
};
i18n/ja.ts
typescript
export const en = {
translation: {
hello: "Hello!",
},
};
i18n/en.ts
typescript
import i18n from "i18next";
import { i18nextInitOptions } from "@/i18n/config";
i18n.init(i18nextInitOptions, (err) => {
if (err) {
console.error("i18next failed to initialize", err);
}
});
export default function RootLayout({
children,
params: { locale },
}: {
children: React.ReactNode;
params: {
locale: string;
};
}) {
i18n.changeLanguage(locale);
return (
<html lang={locale}>
<body>
{children}
</body>
</html>
);
}
typescript
import { t } from "i18next";
export default function Hello() {
return <p>{t("hello")}</p>; // 日本語なら`こんにちは!`、 英語なら`Hello!`
}
react-i18nextによるClient Componentの対応
i18nextの設定ファイルはそのままに、csrも同様にt関数をできるようにします。
typescript
npm install react-i18next
typescript
"use client";
import i18n from "i18next";
import { i18nConfig, i18nextInitOptions } from "@/i18n/config";
import { I18nextProvider } from "react-i18next";
import { useCurrentLocale } from "next-i18n-router/client";
import { ReactNode } from "react";
i18n.init(i18nextInitOptions, (err) => {
if (err) {
console.error("i18next failed to initialize", err);
}
});
export const I18nProvider = ({ children }: { children: ReactNode }) => {
i18n.changeLanguage(useCurrentLocale(i18nConfig));
return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>;
};
typescript
import i18n from "i18next";
import { i18nextInitOptions } from "@/i18n/config";
import { I18nProvider } from "./i18nProvider";
export const metadata = {
title: "Next.js",
description: "Generated by Next.js",
};
i18n.init(i18nextInitOptions, (err) => {
if (err) {
console.error("i18next failed to initialize", err);
}
});
export default function RootLayout({
children,
params: { locale },
}: {
children: React.ReactNode;
params: {
locale: string;
};
}) {
i18n.changeLanguage(locale);
return (
<html lang={locale}>
<body>
<I18nProvider>{children}</I18nProvider>
</body>
</html>
);
}
これで、Server Componentと同様にClient Componentでも同じように多言語対応できます。
typescript
"use client";
import { t } from "i18next";
export default function Hello() {
return <p>{t("hello")}</p>; // 日本語なら`こんにちは!`、 英語なら`Hello!`
}
所感
はじめはNext.jsのドキュメントにある形で実装しましたがちょっと煩雑になっている感が否めなかったのですが、next-i18n-routerにあやかりスッキリしました。
Server Componentsでの対応はNextRequestでi18nのインスタンス管理ができればmiddlewareで設定できそうな気もしますが、上手くできなかったのでlayout.tsxに記述してしまう形で落ち着きました。(いい方法あればぜひコメントください)