多言語対応の Next.js アプリで SEO を最適化する方法
多言語 SEO に特別な注意が必要な理由
Next.js アプリに i18n を導入する多くの開発者は、UI を翻訳した時点で完了だと考えがちです。 しかし、検索エンジンが各言語版を見つけられず、インデックスもできず、適切に関連付けられなければ、 せっかくの翻訳作業もユーザーの目には触れません。
SEO が適切に設定されていない多言語サイトでは、次のような問題が起こります。
- Google が 1 つの言語版しかインデックスせず、ほかを無視してしまう
- スペイン語で検索したユーザーに英語のページが表示される
- Google が
/en/aboutと/fr/aboutを同じページと見なして、重複コンテンツのペナルティを受ける - 検索結果のスニペットに誤った言語が表示される
朗報です: Next.js で多言語 SEO を正しく設定するのは難しくありません。 押さえるべきポイントは 6 つあり、このガイドでは gt-next を使ってそのすべてを説明します。
1. ロケールベースのURLルーティング
多言語SEOの基本は、言語ごとに別々のURLを用意することです。 検索エンジンが各言語版を個別にインデックスするには、それぞれに対応するクロール可能なURLが必要です。
つまり、ロケールをURLに含める ということです。cookieでも、クエリパラメータでも、Accept-Language の検出だけでもありません。
✅ generaltranslation.com/en/about
✅ generaltranslation.com/fr/about
✅ generaltranslation.com/es/about
❌ generaltranslation.com/about?lang=fr
❌ generaltranslation.com/about (with locale in a cookie)
gt-next でロケールルーティングを設定する
まず、ページを [locale] の動的セグメント配下に配置します。
app/
└── [locale]/
├── layout.tsx
├── page.tsx
└── about/
└── page.tsx
次に、プロジェクトのルートにミドルウェアを作成します (Next.js 16 以降では proxy.ts、Next.js 15 以下では middleware.ts) :
import { createNextMiddleware } from 'gt-next/middleware';
export default createNextMiddleware();
export const config = {
matcher: ['/((?!api|static|.*\\..*|_next).*)'],
};これにより、ロケール接頭辞付きのURLが自動的に設定されます。
デフォルトでは、デフォルトのロケール (例: English) には接頭辞が付かないため、/about はそのままですが、
Spanish のユーザーには /es/about、French のユーザーには /fr/about が表示されます。
2. HTML lang 属性の設定
<html> タグの lang 属性は、そのページがどの言語のページかをブラウザや検索エンジンに伝えます。
これは、アクセシビリティと SEO の両面で、最も簡単かつ効果の大きい対策の 1 つです。
これがないと、スクリーンリーダーは言語を推測するしかなく (しかもよく間違えます) 、検索エンジンもその言語分類に十分な確信を持てなくなります。
gt-next には、ルートレイアウトでこれを簡単に実現できる useLocale Hook が用意されています。
import { useLocale, GTProvider } from 'gt-next';
export default function RootLayout({ children }: { children: React.ReactNode }) {
const locale = useLocale();
return (
<html lang={locale}>
<body>
<GTProvider>
{children}
</GTProvider>
</body>
</html>
);
}useLocale は、BCP 47 のロケールコード (en-US、ar、zh-Hans など) を返します。
3. カノニカル URL
canonical タグは、ページの"正規"なURLがどれかを検索エンジンに伝えるためのものです。 多言語サイトでは、各言語版ページが正規URLとして自分自身を指定する必要があります。
<!-- /fr/about の場合 -->
<link rel="canonical" href="https://example.com/fr/about" />これにより、検索エンジンがフランス語ページを英語ページの重複ページとして扱うのを防げます。
Next.js では、metadata API を使って canonical を設定します。
これを gt-next の getLocale と組み合わせると、各ロケールに対応した正しい canonical を生成できます:
import { getLocale } from 'gt-next/server';
const BASE_URL = 'https://example.com';
export async function generateMetadata() {
const locale = await getLocale();
return {
alternates: {
canonical: `${BASE_URL}/${locale}/about`,
},
};
}
export default function AboutPage() {
return <h1>About Us</h1>;
}プレフィックスのないデフォルトのロケールについては、適宜調整してください:
import { getLocale, getDefaultLocale } from 'gt-next/server';
export async function generateMetadata() {
const locale = await getLocale();
const defaultLocale = getDefaultLocale();
const path = '/about';
const prefix = locale === defaultLocale ? '' : `/${locale}`;
return {
alternates: {
canonical: `${BASE_URL}${prefix}${path}`,
},
};
}4. hreflang タグ
hreflang タグは、多言語 SEO における最も重要なシグナルです。
検索エンジンに「このページには他の言語版もあり、それぞれの URL はこれです」と伝えます。
hreflang がないと、Google は検索結果にどの言語版を表示すべきかを推測するしかなく、しかもそれを間違えることがよくあります。
<link rel="alternate" hreflang="en" href="https://example.com/about" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/about" />
<link rel="alternate" hreflang="es" href="https://example.com/es/about" />
<link rel="alternate" hreflang="x-default" href="https://example.com/about" />x-default タグは、指定したどの言語もユーザーに該当しない場合に、どの URL を表示するかを検索エンジンに伝えます。
Next.js では、metadata API の alternates.languages プロパティで hreflang を追加できます。
以下は、gt-next で使える再利用可能なヘルパーです。
import { getLocale, getDefaultLocale } from 'gt-next/server';
const BASE_URL = 'https://example.com';
const SUPPORTED_LOCALES = ['en', 'fr', 'es'];
export async function getI18NMetadata(path: string) {
const locale = await getLocale();
const defaultLocale = getDefaultLocale();
const getUrl = (loc: string) => {
const prefix = loc === defaultLocale ? '' : `/${loc}`;
return `${BASE_URL}${prefix}${path}`;
};
const languages: Record<string, string> = {};
for (const loc of SUPPORTED_LOCALES) {
languages[loc] = getUrl(loc);
}
languages['x-default'] = getUrl(defaultLocale);
return {
alternates: {
canonical: getUrl(locale),
languages,
},
};
}次に、任意のページでこれを使います。
import { getI18NMetadata } from '@/lib/i18n-metadata';
export async function generateMetadata() {
return await getI18NMetadata('/about');
}これにより、1回の呼び出しで canonical とすべての hreflang タグをまとめて生成できます。
5. 翻訳済みメタデータ
検索エンジンは、検索結果にページのタイトルと説明文を表示します。 フランス語のページなのにこれらが英語のままだと、ユーザーにクリックされにくくなり、Google によって検索順位が下がる可能性もあります。
メタデータ文字列を翻訳するには、gt-next の getGT 関数を使用します。
import { getGT } from 'gt-next/server';
import { getI18NMetadata } from '@/lib/i18n-metadata';
export async function generateMetadata() {
const t = await getGT();
const i18nMeta = await getI18NMetadata('/about');
return {
title: t('About Us'),
description: t('Learn about our mission and team.'),
...i18nMeta,
};
}これにより、検索結果に各言語に対応したタイトルと説明文が表示され、 英語以外の検索クエリでのクリック率を大幅に高められます。
6. 多言語サイトマップ
サイトマップは、各言語版を含むサイト内のすべてのページを検索エンジンが見つけやすくするのに役立ちます。
多言語サイトでは、サイトマップに hreflang アノテーションも含める必要があります。
Next.js では、sitemap.ts ファイルを使ってプログラムでサイトマップを生成できます。
import { MetadataRoute } from 'next';
const BASE_URL = 'https://example.com';
const LOCALES = ['en', 'fr', 'es'];
const DEFAULT_LOCALE = 'en';
const PAGES = ['/', '/about', '/blog', '/contact'];
export default function sitemap(): MetadataRoute.Sitemap {
return PAGES.flatMap((path) => {
const getUrl = (locale: string) => {
const prefix = locale === DEFAULT_LOCALE ? '' : `/${locale}`;
return `${BASE_URL}${prefix}${path === '/' ? '' : path}`;
};
const languages: Record<string, string> = {};
for (const locale of LOCALES) {
languages[locale] = getUrl(locale);
}
return LOCALES.map((locale) => ({
url: getUrl(locale),
lastModified: new Date(),
alternates: { languages },
}));
});
}これにより、ページごと・ロケールごとに1つの項目を含むサイトマップが生成され、
各項目には、すべての言語版を指す hreflang アノテーションが含まれます。
チェックリスト
ここまでの内容を簡単にまとめると、次のとおりです。
| SEO 要件 | 実装 |
|---|---|
| URL にロケールを含める | [locale] 動的セグメントを使う createNextMiddleware() |
HTML lang 属性 | ルートレイアウトで useLocale() を使用 |
| カノニカル URL | getLocale() + Next.js metadata API alternates.canonical |
| hreflang タグ | 対応するすべてのロケールに対する Next.js metadata API alternates.languages |
| 翻訳済みメタデータ | ページタイトルと説明に getGT() を使用 |
| 多言語サイトマップ | ロケールごとのエントリと hreflang の代替情報を含む sitemap.ts |
次のステップ
- フルの i18n スタックをセットアップするには、gt-next クイックスタートを参照してください
- ルーティングを設定するには、Middleware ガイドを参照してください
- 多言語ページを静的生成するには、SSG ガイドを参照してください
- 右から左へ記述する言語については、RTL サポートを参照してください