如何为多语言 Next.js 应用优化 SEO
为什么多语言 SEO 需要特别重视
大多数在 Next.js 应用中加入 i18n 的开发者,只关注把 UI 翻译好,就算大功告成。 但如果搜索引擎无法找到、索引,并正确关联你的各语言版本, 所有这些翻译工作在搜索结果中都形同隐形。
一个没有正确配置 SEO 的多语言网站会遇到这些问题:
- Google 可能只索引一个语言版本,忽略其他版本
- 使用西班牙语搜索的用户却被返回英文页面
- 因为 Google 将
/en/about和/fr/about视为同一个页面而触发重复内容惩罚 - 搜索结果摘要中显示的语言不正确
**好消息是:**在 Next.js 中把多语言 SEO 配置好并不复杂。 你只需要处理好五个关键点,而本指南会通过 gt-next 带你逐一搞定。
1. 基于语言环境的 URL 路由
多语言 SEO 的基础,是为每种语言提供不同的 URL。 搜索引擎需要彼此独立、可抓取的 URL,才能分别索引每个语言版本。
这意味着必须在 URL 中包含语言环境(locale-in-the-URL) —— 而不是依赖 cookies、查询参数,或仅靠 Accept-Language 检测。
✅ generaltranslation.com/en/about
✅ generaltranslation.com/fr/about
✅ generaltranslation.com/es/about
❌ generaltranslation.com/about?lang=fr
❌ generaltranslation.com/about (语言区域存储在 cookie 中)
使用 gt-next 设置 locale 路由
首先,将你的页面嵌套在一个 [locale] 动态路由段下:
app/
└── [locale]/
├── layout.tsx
├── page.tsx
└── about/
└── page.tsx
然后在项目根目录创建一个中间件文件(Next.js 16 及以上使用 proxy.ts,Next.js 15 及以下使用 middleware.ts):
import { createNextMiddleware } from 'gt-next/middleware';
export default createNextMiddleware();
export const config = {
matcher: ['/((?!api|static|.*\\..*|_next).*)'],
};这样会自动为你的 URL 添加语言前缀。
默认情况下,默认语言(例如英语)不会带前缀 —— /about 保持不变,
而西班牙语用户会看到 /es/about,法语用户会看到 /fr/about。
2. 设置 HTML lang 属性
<html> 标签上的 lang 属性会告诉浏览器和搜索引擎页面使用的是哪种语言。
这是你在无障碍和 SEO 方面能做的最简单且最有影响力的事情之一。
如果不设置,屏幕阅读器会猜测语言(往往是错的),而搜索引擎对其语言分类的信心也会降低。
gt-next 提供了 useLocale hook,让你在根布局中即可轻松实现这一点:
import { useLocale, GTProvider } from 'gt-next';
export default function RootLayout({ children }: { children: React.ReactNode }) {
const locale = useLocale();
return (
<html lang={locale}>
<body>
<GTProvider>
{children}
</GTProvider>
</body>
</html>
);
}useLocale 返回 BCP 47 语言标记(例如,en-US、ar、zh-Hans)。
3. 规范 URL
canonical 标签会告诉搜索引擎哪个 URL 是页面的“主要”版本。 对于多语言站点,每个语言版本都应将自身对应的 URL 标记为规范:
<!-- 在 /fr/about 上 -->
<link rel="canonical" href="https://example.com/fr/about" />这可以防止搜索引擎将你的法语页面视为英文页面的重复内容。
在 Next.js 中,你可以通过 metadata API 设置规范 URL(canonical)。
将其与 gt-next 的 getLocale 结合使用,为每个 locale 生成正确的规范 URL:
import { getLocale } from 'gt-next/server';
const BASE_URL = 'https://example.com';
export async function generateMetadata() {
const locale = await getLocale();
return {
alternates: {
canonical: `${BASE_URL}/${locale}/about`,
},
};
}
export default function AboutPage() {
return <h1>About Us</h1>;
}对于默认语言环境(无前缀)的情况,请作相应调整:
import { getLocale, getDefaultLocale } from 'gt-next/server';
export async function generateMetadata() {
const locale = await getLocale();
const defaultLocale = getDefaultLocale();
const path = '/about'; // 页面路径
const prefix = locale === defaultLocale ? '' : `/${locale}`;
return {
alternates: {
canonical: `${BASE_URL}${prefix}${path}`,
},
};
}4. Hreflang 标签
Hreflang 标签是多语言 SEO 中最重要的信号。 它们会告诉搜索引擎:「这个页面还提供以下其他语言版本,对应的 URL 如下。」
如果没有 hreflang,Google 只能猜测在搜索结果中应该展示哪个语言版本——而且经常会猜错。
<link rel="alternate" hreflang="en" href="https://example.com/about" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/about" />
<link rel="alternate" hreflang="es" href="https://example.com/es/about" />
<link rel="alternate" hreflang="x-default" href="https://example.com/about" />x-default 标签会告诉搜索引擎,当没有任何指定语言与用户匹配时应该显示哪个 URL。
在 Next.js 中,你可以通过 metadata API 的 alternates.languages 属性来添加 hreflang。
下面是一个可与 gt-next 搭配使用的可复用辅助函数:
import { getLocale, getDefaultLocale } from 'gt-next/server';
const BASE_URL = 'https://example.com';
const SUPPORTED_LOCALES = ['en', 'fr', 'es'];
export async function getI18NMetadata(path: string) {
const locale = await getLocale();
const defaultLocale = getDefaultLocale();
const getUrl = (loc: string) => {
const prefix = loc === defaultLocale ? '' : `/${loc}`;
return `${BASE_URL}${prefix}${path}`;
};
const languages: Record<string, string> = {};
for (const loc of SUPPORTED_LOCALES) {
languages[loc] = getUrl(loc);
}
languages['x-default'] = getUrl(defaultLocale);
return {
alternates: {
canonical: getUrl(locale),
languages,
},
};
}然后在任何页面中使用它:
import { getI18NMetadata } from '@/lib/i18n-metadata';
export async function generateMetadata() {
return await getI18NMetadata('/about');
}这会在一次调用中同时生成 canonical 标签和所有 hreflang 标签。
5. 已翻译的元数据
搜索引擎会在结果中显示你的页面标题和描述。 如果一个法语页面的这些内容是英文,用户点击该结果的可能性会降低,并且 Google 可能会下调该结果的排名。
使用 gt-next 的 getGT 函数来翻译元数据字符串:
import { getGT } from 'gt-next/server';
import { getI18NMetadata } from '@/lib/i18n-metadata';
export async function generateMetadata() {
const t = await getGT();
const i18nMeta = await getI18NMetadata('/about');
return {
title: t('关于我们'),
description: t('了解我们的使命与团队。'),
...i18nMeta,
};
}这会使你的搜索结果展示本地化的标题和描述, 从而显著提升非英文搜索查询的点击率。
6. 多语言站点地图
站点地图有助于搜索引擎发现你所有的页面,包括每个语言版本。 对于多语言站点,你还应在站点地图中包含 hreflang 标记。
Next.js 通过 sitemap.ts 文件支持以编程方式生成的站点地图:
import { MetadataRoute } from 'next';
const BASE_URL = 'https://example.com';
const LOCALES = ['en', 'fr', 'es'];
const DEFAULT_LOCALE = 'en';
const PAGES = ['/', '/about', '/blog', '/contact'];
export default function sitemap(): MetadataRoute.Sitemap {
return PAGES.flatMap((path) => {
const getUrl = (locale: string) => {
const prefix = locale === DEFAULT_LOCALE ? '' : `/${locale}`;
return `${BASE_URL}${prefix}${path === '/' ? '' : path}`;
};
const languages: Record<string, string> = {};
for (const locale of LOCALES) {
languages[locale] = getUrl(locale);
}
return LOCALES.map((locale) => ({
url: getUrl(locale),
lastModified: new Date(),
alternates: { languages },
}));
});
}这会生成一个站点地图,其中每个页面在每种语言环境下都会有一个条目, 并且每个条目都包含指向所有语言版本的 hreflang 标记。
检查清单
以下是本文内容的快速总结:
| SEO 要求 | 实现方式 |
|---|---|
| URL 中的 locale | 使用带有 [locale] 动态段的 createNextMiddleware() |
HTML lang 属性 | 在根布局中使用 useLocale() |
| 规范 URL(Canonical URLs) | getLocale() + Next.js metadata API 中的 alternates.canonical |
| Hreflang 标签 | 使用包含所有受支持的 locale 的 Next.js metadata API alternates.languages |
| 已翻译的 metadata | 使用 getGT() 设置页面标题和描述 |
| 多语言 sitemap | 在 sitemap.ts 中为每个 locale 添加条目和 hreflang 替代版本链接 |
后续步骤
- 通过 gt-next 快速入门 来设置完整的 i18n 技术栈
- 查看 Middleware 指南 以配置路由
- 查看 SSG 指南 以静态生成多语言页面
- 查看 RTL 支持 以支持从右到左书写的语言