React Router v7でページメタデータを翻訳する方法
検索とソーシャルメディア向けにメタデータを翻訳
問題
ページメタデータ(タイトルと説明)は、ページ自体の外側、つまりブラウザのタブ、ブックマーク、検索結果、ソーシャルメディアのプレビューに表示されます。このメタデータがページの言語と一致しない場合、不快な不整合が生じます。英語のタイトルを持つスペイン語のページは、ユーザーがコンテンツを見る前に混乱させます。検索エンジンは、この不一致をページが適切にローカライズされていない、または品質が低いというシグナルとして解釈し、言語固有の検索結果でのランキングを下げる可能性があります。ユーザーは、ページが自分の言語ではないと判断し、読み込まれる前にページを離れる可能性があります。
解決策
ルートモジュールからmeta関数をエクスポートすることで、現在の言語に合わせてページメタデータを翻訳します。react-intlのformatMessage APIをMessage Descriptorsと共に使用してタイトルと説明文字列を翻訳し、メタデータがページコンテンツと同じ翻訳リソースを使用するようにします。これにより、ブラウザのタブ、検索結果、ページ自体に表示される内容の一貫性が保たれます。
手順
1. コンポーネント外でintlにアクセスするヘルパーを作成
intlオブジェクトはformatMessageを提供し、コンポーネント内ではuseIntlフックを介してアクセスできます。また、React以外の環境ではcreateIntlで直接作成できます。meta関数はReactコンポーネントツリーの外で実行されるため、メッセージからintlインスタンスを構築するヘルパーを作成します。
import { createIntl, createIntlCache } from "react-intl";
const cache = createIntlCache();
export function createIntlForLocale(
locale: string,
messages: Record<string, string>,
) {
return createIntl(
{
locale,
messages,
},
cache,
);
}
このヘルパーは、Reactコンポーネントだけでなく、あらゆる関数でメッセージをフォーマットできるintlインスタンスを作成します。
2. 親ルートのローダーでメッセージを読み込む
ルートローダーは、コンポーネントがloaderDataプロップを介してアクセスするデータを返します。子ルートで使用できるように、親ルートで翻訳メッセージを読み込みます。
import type { Route } from "./+types/root";
export async function loader({ request }: Route.LoaderArgs) {
const url = new URL(request.url);
const locale = url.pathname.split("/")[1] || "en";
const messages = await import(`../translations/${locale}.json`);
return {
locale,
messages: messages.default,
};
}
meta関数は、一致したすべてのルートからのローダーデータを含むmatchesパラメータを受け取り、親ローダーデータを子ルートのmeta関数からアクセス可能にします。
3. メタデータを翻訳するmeta関数をエクスポートする
ルートモジュールからmeta記述子オブジェクトの配列を返すmeta関数をエクスポートします。matchesから親ローダーデータにアクセスし、intlヘルパーを使用して文字列を翻訳します。
import type { Route } from "./+types/product";
import { createIntlForLocale } from "~/utils/intl";
export function meta({ matches }: Route.MetaArgs) {
const rootMatch = matches.find((match) => match.id === "root");
const { locale, messages } = rootMatch?.data || {
locale: "en",
messages: {},
};
const intl = createIntlForLocale(locale, messages);
return [
{
title: intl.formatMessage({
id: "product.meta.title",
defaultMessage: "Product Details",
}),
},
{
name: "description",
content: intl.formatMessage({
id: "product.meta.description",
defaultMessage: "View detailed information about this product",
}),
},
];
}
formatMessage関数は、idとdefaultMessageを持つMessage Descriptorを受け取り、現在のロケールに対応する翻訳済み文字列を返します。
4. 翻訳されたメタデータ文字列をメッセージファイルに追加する
formatMessageが見つけられるように、各ロケールのメッセージファイルにメタデータ翻訳キーを追加します。
{
"product.meta.title": "Détails du produit",
"product.meta.description": "Voir les informations détaillées sur ce produit"
}
ユーザーがこのルートに移動すると、ルートレイアウトのMetaコンポーネントがルートのmetaエクスポートによって作成されたすべてのメタタグをレンダリングし、ページの言語に一致する翻訳済みのタイトルと説明を表示します。