Next.js(ページルーター)v16でページメタデータを翻訳する方法

検索とソーシャルメディア向けのメタデータを翻訳する

問題

ページのメタデータ(タイトルや説明文)は、ページコンテンツ自体の外部に表示されます。これはブラウザのタブ、ブックマーク、検索エンジンの結果、ソーシャルメディアのプレビューに表示されます。メタデータがページの言語と一致しない場合、ユーザーはコンテンツを見る前に不自然な不一致を経験します。スペイン語のページに英語のタイトルがあると、訪問者を混乱させ、ローカリゼーションの品質が低いことを示します。

検索エンジンは言語の不一致を低品質なローカリゼーションの指標として解釈し、言語固有の検索結果でのランキングが下がる可能性があります。ソーシャルメディアプラットフォームはリンクプレビューで間違った言語を表示し、国際的な視聴者からのエンゲージメントを減少させます。メタデータを含むすべての接点での一貫した翻訳は、プロフェッショナルな多言語体験に不可欠です。

解決策

ページコンテンツと同じ翻訳リソースを使用してページメタデータを翻訳します。react-intlのuseIntlフックを使用して翻訳された文字列にアクセスし、ページの<Head>コンポーネントに配置します。これにより、ブラウザのタブ、検索結果、ソーシャルプレビューで、タイトルと説明が現在のロケールと一致することが保証されます。

表示されるコンテンツと同じメッセージカタログからメタデータを取得することで、一貫性を維持し、翻訳作業の重複を避けることができます。このアプローチは、静的ページとメタデータがページ固有のデータに依存する動的ルートの両方に有効です。

ステップ

1. 翻訳ファイルにメタデータメッセージを追加する

他の翻訳コンテンツと同じ構造を使用して、翻訳カタログにページタイトルと説明のメッセージディスクリプタを定義します。

{
"home.title": "Welcome to Our Store",
"home.description": "Discover amazing products at great prices",
"products.title": "Our Products",
"products.description": "Browse our full catalog of items"
}

翻訳されたメタデータが必要な各ページには、そのタイトルと説明に対応するメッセージIDが必要です。

2. 翻訳されたメタデータコンポーネントを作成する

useIntlフックを使用してformatMessage関数にアクセスし、Headコンポーネント内で翻訳された文字列をレンダリングします。

import Head from "next/head";
import { useIntl } from "react-intl";

export default function HomePage() {
  const intl = useIntl();

  return (
    <>
      <Head>
        <title>{intl.formatMessage({ id: "home.title" })}</title>
        <meta
          name="description"
          content={intl.formatMessage({ id: "home.description" })}
        />
      </Head>
      <main>
        <h1>{intl.formatMessage({ id: "home.title" })}</h1>
      </main>
    </>
  );
}

HeadコンポーネントはNext.jsに組み込まれたReactコンポーネントで、ページの<head>を変更することができます。formatMessage関数は現在のロケールに対応する翻訳された文字列を返します。

3. Open Graphとソーシャルメタデータを追加する

このパターンを拡張して、ソーシャルメディアプレビュー用のOpen GraphとTwitterカードのメタデータを含めます。

import Head from "next/head";
import { useIntl } from "react-intl";

export default function ProductsPage() {
  const intl = useIntl();
  const title = intl.formatMessage({ id: "products.title" });
  const description = intl.formatMessage({ id: "products.description" });

  return (
    <>
      <Head>
        <title>{title}</title>
        <meta name="description" content={description} />
        <meta property="og:title" content={title} />
        <meta property="og:description" content={description} />
        <meta name="twitter:title" content={title} />
        <meta name="twitter:description" content={description} />
      </Head>
      <main>
        <h1>{title}</h1>
      </main>
    </>
  );
}

フォーマットされたメッセージを変数に格納することで、同じ翻訳に対してformatMessageを複数回呼び出す必要がなくなり、JSXがより簡潔になります。

4. パラメータ化されたページの動的メタデータを処理する

動的ルートを持つページでは、ルートパラメータと翻訳された文字列を組み合わせて、コンテキストに応じたメタデータを作成します。

import Head from "next/head";
import { useIntl } from "react-intl";
import { useRouter } from "next/router";

export default function ProductDetailPage() {
  const intl = useIntl();
  const router = useRouter();
  const { id } = router.query;

  const title = intl.formatMessage(
    { id: "product.detail.title" },
    { productId: id },
  );

  return (
    <>
      <Head>
        <title>{title}</title>
      </Head>
      <main>
        <h1>{title}</h1>
      </main>
    </>
  );
}

メッセージディスクリプタは変数の補間をサポートしており、製品IDや名前などの動的な値を翻訳されたメタデータ文字列に挿入することができます。対応するメッセージは"Product {productId} - Our Store"のようになります。