如何在 React Router v7 中翻译页面元数据
为搜索和社交平台翻译元数据
问题
页面元数据(标题和描述)显示在页面本身之外,例如浏览器标签、书签、搜索结果和社交媒体预览中。如果这些元数据与页面语言不一致,会造成明显的不协调。例如,西班牙语页面却显示英文标题,会让用户在看到内容前就感到困惑。搜索引擎会将这种不匹配视为本地化质量差或页面质量低的信号,可能导致该页面在特定语言搜索结果中的排名下降。用户甚至可能在页面加载前就离开,误以为内容不是他们的语言。
解决方案
通过在路由模块中导出 meta 函数,将页面元数据翻译为当前语言。使用 react-intl 的 formatMessage API 和 Message Descriptor 来翻译标题和描述字符串,确保元数据与页面内容使用相同的翻译资源。这样可以保证浏览器标签、搜索结果和页面内容之间的一致性。
步骤
1. 创建辅助方法以在组件外访问 intl
intl 对象提供 formatMessage 方法,可以通过组件中的 useIntl hook 获取,也可以在非 React 环境下用 createIntl 直接创建。由于 meta 函数运行在 React 组件树之外,需要创建一个辅助方法,从你的 messages 构建 intl 实例。
import { createIntl, createIntlCache } from "react-intl";
const cache = createIntlCache();
export function createIntlForLocale(
locale: string,
messages: Record<string, string>,
) {
return createIntl(
{
locale,
messages,
},
cache,
);
}
该辅助方法会创建一个 intl 实例,使任何函数(不仅限于 React 组件)都能格式化消息。
2. 在父路由 loader 中加载消息
路由 loader 返回的数据可以通过 loaderData props 被组件访问。应在父路由中加载翻译消息,以便子路由也能使用。
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 参数,其中包含所有匹配路由的 loader 数据,使父级 loader 数据能够被子路由的 meta 函数访问。
3. 导出一个用于翻译元数据的 meta 函数
从你的路由模块中导出一个 meta 函数,该函数返回一个 meta 描述对象数组。通过 matches 访问父级 loader 数据,并使用你的 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 的消息描述对象,并返回当前语言环境下的翻译字符串。
4. 将翻译后的元数据字符串添加到消息文件中
将元数据的翻译键添加到每个语言环境的消息文件中,以便 formatMessage 能够找到对应内容。
{
"product.meta.title": "Détails du produit",
"product.meta.description": "Voir les informations détaillées sur ce produit"
}
当用户导航到此路由时,根布局中的 Meta 组件会渲染所有由路由 meta 导出创建的 meta 标签,显示与页面语言相匹配的翻译标题和描述。