如何在 Next.js(Pages Router)v16 中翻译页面元数据

为搜索和社交平台翻译元数据

问题

页面元数据(标题和描述)显示在页面内容之外,例如浏览器标签、书签、搜索引擎结果和社交媒体预览中。如果元数据与页面语言不一致,用户在看到内容前就会感受到突兀的不协调。例如,西班牙语页面却显示英文标题,会让访问者感到困惑,并且反映出较差的本地化质量。

搜索引擎会将语言不匹配视为本地化质量低的信号,可能导致在特定语言的搜索结果中排名下降。社交媒体平台在链接预览中显示错误的语言,也会降低国际用户的参与度。确保所有触点(包括元数据)的一致翻译,是实现专业多语言体验的关键。

解决方案

使用与页面内容相同的翻译资源来翻译页面元数据。通过 react-intl 的 useIntl hook 获取翻译字符串,并将其放入页面的 <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 hook 获取 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"