如何翻译 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,
  );
}

这个帮助程序创建了一个 intl 实例,可以在任何函数中格式化消息,而不仅限于 React 组件。

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 函数,该函数返回一个元描述符对象数组。从 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 的消息描述符,并返回当前语言环境的翻译字符串。

4. 将翻译后的元数据字符串添加到您的消息文件中

将元数据翻译键添加到每个语言环境的消息文件中,以便 formatMessage 可以找到它们。

{
  "product.meta.title": "产品详情",
  "product.meta.description": "查看此产品的详细信息"
}

当用户导航到此路由时,根布局中的 Meta 组件会渲染所有由路由 meta 导出创建的 meta 标签,显示与页面语言匹配的翻译标题和描述。