如何在 Next.js(Pages Router)v16 中关联不同语言版本
为搜索引擎添加语言版本链接
问题
当网站以多种语言提供相同内容时,搜索引擎会遇到每种语言版本对应不同 URL 的情况,但无法理解它们之间的关系。例如,法语用户搜索时,可能会看到英文页面的排名高于法文页面,即使两者都存在。同样,英文页面及其法语翻译可能被视为相互竞争的重复内容,而不是协调的替代版本。如果没有明确的信号将这些语言版本关联起来,搜索引擎就无法根据用户的语言偏好准确地提供最合适的版本,导致权重分散和用户体验不佳。
解决方案
在每个页面的 head 区域添加带有 hreflang 属性的 link 元素,列出所有语言版本(包括当前页面本身)。每个页面变体都必须包含一组完全相同的链接,指向所有可用的语言版本。这种双向链接可以确保搜索引擎将这些页面识别为翻译版本,而不是重复内容,从而根据浏览器偏好和搜索上下文为用户提供正确的语言页面。
步骤
1. 创建生成多语言链接的组件
从 next/head 导入 Head 组件以修改页面元数据。通过 useRouter 钩子获取 locale 信息,动态构建所有可用语言版本的链接。
import Head from "next/head";
import { useRouter } from "next/router";
interface AlternateLinksProps {
path?: string;
}
export default function AlternateLinks({ path }: AlternateLinksProps) {
const router = useRouter();
const { locales, locale: currentLocale, asPath } = router;
const canonicalPath = path || asPath;
if (!locales) {
return null;
}
return (
<Head>
{locales.map((locale) => (
<link
key={locale}
rel="alternate"
hrefLang={locale}
href={`${process.env.NEXT_PUBLIC_SITE_URL}/${locale}${canonicalPath}`}
/>
))}
</Head>
);
}
该组件会遍历路由中的可用 locale,并为所有页面动态生成 link 元素。
2. 在有翻译的页面中添加该组件
在每个存在多语言版本的页面组件中引入 AlternateLinks 组件。
import AlternateLinks from "@/components/AlternateLinks";
export default function AboutPage() {
return (
<>
<AlternateLinks />
<main>
<h1>About Us</h1>
</main>
</>
);
}
该组件确保每个页面都包含完整的 alternate 链接集合,满足所有变体相互引用的要求。
3. 为当前语言环境添加自引用链接
每个页面都必须包含一个指示自身语言的自引用 hreflang 标签。该组件已通过遍历所有语言环境(包括当前环境)自动处理此需求。
export default function AlternateLinks({ path }: AlternateLinksProps) {
const router = useRouter();
const { locales, asPath } = router;
const canonicalPath = path || asPath;
if (!locales) {
return null;
}
return (
<Head>
{locales.map((locale) => (
<link
key={locale}
rel="alternate"
hrefLang={locale}
href={`${process.env.NEXT_PUBLIC_SITE_URL}/${locale}${canonicalPath}`}
/>
))}
</Head>
);
}
现在每个页面都包含指向自身的 hreflang 标签,以及指向其他版本的标签。
4. 添加 x-default 备用链接
添加 x-default 链接,用于指定当用户的语言不可用时应显示哪个版本。
export default function AlternateLinks({ path }: AlternateLinksProps) {
const router = useRouter();
const { locales, defaultLocale, asPath } = router;
const canonicalPath = path || asPath;
if (!locales || !defaultLocale) {
return null;
}
return (
<Head>
{locales.map((locale) => (
<link
key={locale}
rel="alternate"
hrefLang={locale}
href={`${process.env.NEXT_PUBLIC_SITE_URL}/${locale}${canonicalPath}`}
/>
))}
<link
rel="alternate"
hrefLang="x-default"
href={`${process.env.NEXT_PUBLIC_SITE_URL}/${defaultLocale}${canonicalPath}`}
/>
</Head>
);
}
x-default 链接会将语言偏好未匹配的用户引导到默认语言环境。