今回やること
Next.jsで多言語対応を行う時は、翻訳ファイルだけでなくURL設計、言語切替、SEOも一緒に考える必要があります。
国際化は後から足すほど影響範囲が広がります。最初にロケールの持ち方とURL方針を決めることが重要です。
この記事では次を学びます。
- i18nルーティング
- 翻訳ファイル管理
- next-intl設定
- 動的ロケール切替
- SEO対応
Step 1: セットアップ
実践メモ: next-intlはApp Router対応の国際化ライブラリです。Server ComponentsとClient Componentsの両方で翻訳関数が使えます。
npm install next-intl
Step 2: 翻訳ファイル
// messages/ja.json
{
"home": {
"title": "ホームページ",
"welcome": "ようこそ、{name}さん"
},
"nav": {
"home": "ホーム",
"about": "会社概要",
"contact": "お問い合わせ"
}
}
// messages/en.json
{
"home": {
"title": "Home Page",
"welcome": "Welcome, {name}"
},
"nav": {
"home": "Home",
"about": "About",
"contact": "Contact"
}
}
Step 3: next.config.js
const withNextIntl = require('next-intl/plugin')();
module.exports = withNextIntl({
// 他の設定
});
Step 4: i18n設定
// i18n.ts
import { getRequestConfig } from 'next-intl/server';
export default getRequestConfig(async ({ locale }) => ({
messages: (await import(`./messages/${locale}.json`)).default
}));
注意: ミドルウェアのmatcher設定で静的ファイルパスを除外しないと、画像やCSSにもリダイレクトが適用されてしまいます。
Step 5: ミドルウェア
// middleware.ts
import createMiddleware from 'next-intl/middleware';
export default createMiddleware({
locales: ['ja', 'en'],
defaultLocale: 'ja'
});
export const config = {
matcher: ['/((?!api|_next|.*\\..*).*)']
};
Step 6: レイアウト
// app/[locale]/layout.tsx
import { NextIntlClientProvider } from 'next-intl';
import { getMessages } from 'next-intl/server';
export default async function LocaleLayout({
children,
params: { locale }
}: {
children: React.ReactNode;
params: { locale: string };
}) {
const messages = await getMessages();
return (
<html lang={locale}>
<body>
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}
Step 7: 翻訳の使用
// app/[locale]/page.tsx
import { useTranslations } from 'next-intl';
export default function HomePage() {
const t = useTranslations('home');
return (
<div>
<h1>{t('title')}</h1>
<p>{t('welcome', { name: 'Alice' })}</p>
</div>
);
}
Step 8: ロケール切替
'use client';
import { useLocale } from 'next-intl';
import { useRouter, usePathname } from 'next/navigation';
export function LocaleSwitcher() {
const locale = useLocale();
const router = useRouter();
const pathname = usePathname();
const switchLocale = (newLocale: string) => {
const newPath = pathname.replace(`/${locale}`, `/${newLocale}`);
router.push(newPath);
};
return (
<select value={locale} onChange={(e) => switchLocale(e.target.value)}>
<option value="ja">日本語</option>
<option value="en">English</option>
</select>
);
}
SEO対応
// app/[locale]/page.tsx
export async function generateMetadata({ params: { locale } }) {
const t = await getTranslations({ locale, namespace: 'home' });
return {
title: t('title'),
alternates: {
languages: {
ja: '/ja',
en: '/en',
},
},
};
}
多言語対応で最初に決めること
国際化は、翻訳ファイルを用意するだけでは終わりません。URLにロケールを含めるか、デフォルト言語をどう扱うか、SEOのalternate設定をどう出すかを先に決める必要があります。後からURL設計を変えると、リンクや検索評価への影響が大きくなります。
翻訳キーは画面ごとに整理すると運用しやすくなります。日本語の文章そのものをキーにすると、文言変更のたびにキーも変わりやすいため、home.titleやnav.contactのように役割で命名するほうが保守しやすくなります。
運用でつまずきやすい点
- 翻訳漏れがある画面だけ英語やキー名のまま表示される
- 日付、通貨、数値の表示形式が言語ごとに合っていない
- 画像内テキストが翻訳対象から漏れる
- canonicalとalternateの指定が矛盾する
動作確認
多言語対応を入れたら、次を確認します。
| 確認項目 | 見る場所 | OKの状態 |
|---|---|---|
| URL | /ja と /en | 各言語ページが表示される |
| 翻訳 | 画面文言 | キー名がそのまま表示されない |
| 言語切替 | 切替UI | 同じページの別言語へ移動する |
| SEO | HTML head | alternates.languages が出る |
| 404 | 存在しないlocale | 想定通りに扱える |
翻訳漏れは見た目だけでは気づきにくいため、主要画面を言語ごとに確認します。
練習
次の小さな多言語ページを作ってください。
jaとenの翻訳JSONを作る- トップページのタイトルと説明文を翻訳する
/ja/aboutと/en/aboutを表示する- 言語切替コンポーネントを作る
generateMetadataで言語ごとのtitleを出す
次のステップ
Next.jsのルーティング自体が不安な場合は Next.js App Router実践ガイド を先に確認してください。SEO観点は 静的サイト・動的サイトの違い と リダイレクトとcanonical も関連します。
まとめ
next-intlでNext.jsアプリを簡単に多言語対応できます。ルーティングとSEOも適切に設定しましょう。