複数言語のサイトマップインデックスの作成

SEOのための大規模多言語サイトの管理

問題

サイトには多数の言語にわたる大量のページがあります。すべての言語のすべてのURLをリストアップした単一の巨大なsitemap.xmlファイルは非効率的で、管理が困難であり、ファイルサイズの制限を超える可能性があります。これにより、1つの言語のURLを更新することや、検索エンジンがすべてのコンテンツを効率的に発見することが困難になります。

解決策

sitemap.xmlファイルを「サイトマップインデックス」として機能させ、他の言語固有のサイトマップ(例:sitemap-en.xmlsitemap-es.xml)を指すようにします。このアプローチはコンテンツを整理し、管理が容易になり、新しい言語やページが追加されても効率的にスケールします。

手順

1. サイトマップインデックスルートの作成

sitemap.tsファイルの代わりに、標準のルートハンドラーを作成する必要があります。これにより、サイトマップインデックスを生成するための完全な制御が可能になります。

app/sitemap.xml/route.tsを作成します。

// app/sitemap.xml/route.ts
import { locales, siteBaseUrl } from '@/i18n-config';

export async function GET() {
  const sitemapIndex = `<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="[http://www.sitemaps.org/schemas/sitemap/0.9](http://www.sitemaps.org/schemas/sitemap/0.9)">
  ${locales
    .map((locale) => {
      return `
    <sitemap>
      <loc>${siteBaseUrl}/sitemap-${locale}.xml</loc>
      <lastmod>${new Date().toISOString()}</lastmod>
    </sitemap>`;
    })
    .join('')}
</sitemapindex>
`;

  return new Response(sitemapIndex, {
    headers: {
      'Content-Type': 'application/xml',
    },
  });
}

このファイルは、サイトのサイトマップがsitemap-en.xmlsitemap-es.xmlなどの他のファイルを指すインデックスであることを検索エンジンに伝えます。

2. 言語サイトマップの動的ルートの作成

次に、各言語固有のサイトマップを生成する動的ルートを作成します。

app/sitemap-[lang].xml/route.tsを作成します。

// app/sitemap-[lang].xml/route.ts
import { locales, siteBaseUrl } from '@/i18n-config';

// This tells Next.js which sitemaps to build at build time
export async function generateStaticParams() {
  return locales.map((lang) => ({
    lang,
  }));
}

// A helper function to get all pages for a language
// In a real app, this would fetch from a CMS or database
async function getPagesForLanguage(lang: string): Promise<string[]> {
  // These are relative paths, *without* the lang prefix
  // e.g., '/', '/about', '/blog/my-post'
  return ['/', '/about', '/contact'];
}

export async function GET(
  request: Request,
  { params }: { params: { lang: string } }
) {
  const { lang } = params;

  if (!locales.includes(lang)) {
    return new Response('Not Found', { status: 404 });
  }

  const pages = await getPagesForLanguage(lang);

  const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="[http://www.sitemaps.org/schemas/sitemap/0.9](http://www.sitemaps.org/schemas/sitemap/0.9)">
  ${pages
    .map((page) => {
      const path = page === '/' ? '' : page;
      return `
    <url>
      <loc>${siteBaseUrl}/${lang}${path}</loc>
      <lastmod>${new Date().toISOString()}</lastmod>
    </url>`;
    })
    .join('')}
</urlset>
`;

  return new Response(sitemap, {
    headers: {
      'Content-Type': 'application/xml',
    },
  });
}

これで、sitemap.xmlファイルはクリーンなインデックスとなり、各言語のURLはそれぞれのファイル(例:/sitemap-en.xml)にきちんと整理され、この動的ルートによって生成されます。