はじめに
国際化(i18n)は現代のWebアプリケーションに必須の要件です。Next.jsはPages Routerの統合i18nルーティングから、App Routerのミドルウェアベースの柔軟なアプローチへと進化してきました。本記事では、ルーティング戦略、ロケール検出、コンテンツ管理、多言語SEOについて解説します。
主要な概念はロケール、デフォルトロケール、ロケール検出、そしてサブパスルーティングとドメインルーティングのトレードオフです。App Routerアプローチはより柔軟性を提供しますが、設定も増えます。
ルーティング戦略 — サブパス vs ドメイン
サブパスとドメインの選択はSEOとインフラに大きな影響を与えます。
| 基準 | サブパス(/ja/about) | ドメイン(ja.example.com) |
|---|---|---|
| SEO権威 | 共有ドメイン権威 | ロケールごとに独立 |
| ジオターゲティング | 弱い | 強い |
| インフラ | 単一デプロイ | 複数デプロイ |
| 分析 | 統一 | 分割 |
ほとんどのプロジェクトでは、サブパスルーティングが推奨されます。App Routerでの実装はミドルウェアを使用してロケールプレフィックスを処理します。
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
const locales = ["ja", "en", "fr"];
const defaultLocale = "ja";
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
const pathnameHasLocale = locales.some(
(locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
);
if (pathnameHasLocale) return;
const locale = request.cookies.get("NEXT_LOCALE")?.value || defaultLocale;
return NextResponse.redirect(new URL(`/${locale}${pathname}`, request.url));
}
export const config = {
matcher: ["/((?!api|_next|favicon.ico).*)"],
};
ロケール検出とネゴシエーション
Next.jsはCookieからAccept-Languageヘッダー、デフォルトロケールの順にロケールを決定します。NEXT_LOCALE Cookieでユーザーの言語設定を永続化できます。
function getLocale(request: NextRequest): string {
const cookieLocale = request.cookies.get("NEXT_LOCALE")?.value;
if (cookieLocale && locales.includes(cookieLocale)) return cookieLocale;
const acceptLanguage = request.headers.get("accept-language");
if (acceptLanguage) {
const preferred = acceptLanguage
.split(",")
.map((lang) => lang.split(";")[0].trim())
.find((lang) => locales.includes(lang));
if (preferred) return preferred;
}
return defaultLocale;
}
このネゴシエーションロジックにより、ユーザーは常に優先言語でコンテンツを表示できます。
next-intlによるコンテンツ管理
next-intlはApp Routerのi18nに推奨されるライブラリです。型安全な翻訳キー、名前空間分割、効率的なメッセージ読み込みを提供します。
// middleware.ts
import createMiddleware from "next-intl/middleware";
export default createMiddleware({ locales: ["ja", "en"], defaultLocale: "ja" });
export const config = { matcher: ["/((?!api|_next).*)"] };
翻訳ファイルは名前空間ごとに整理します:
{
"navigation": { "home": "ホーム", "about": "概要" },
"home": { "title": "ようこそ", "description": "ホームページへようこそ" }
}
コンポーネントではuseTranslationsフックを使用します:
import { useTranslations } from "next-intl";
function HomePage() {
const t = useTranslations("home");
return <h1>{t("title")}</h1>;
}
多言語サイトのSEO
クロス言語SEOには適切なhreflangタグと正規URLが必須です。各ページに自己参照と全代替hreflangリンクを含めます。
export async function generateMetadata({ params }: Props) {
const { locale } = await params;
return {
alternates: {
languages: {
ja: "/ja/about",
en: "/en/about",
"x-default": "/ja/about",
},
},
};
}
よくある落とし穴は、ロケール間の重複コンテンツ、hreflang戻りリンクの欠如、一貫性のないロケールURLパターンです。Googleのガイドラインでは各言語バージョンが他の全言語バージョンにリンクを戻すことが求められます。
まとめ
ほとんどのプロジェクトでは、next-intlを使用したサブパスルーティングが最適なバランスを提供します。適切なロケール検出ミドルウェア、名前空間ごとの翻訳ファイル整理、完全なhreflangメタデータの実装に投資してください。公式のNext.js i18nサンプルとnext-intlドキュメントが実装の参考になります。
