Next.js(Pages Router)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 Cardメタデータを含めます。
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"のようになります。