如何优化多语言 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 中——而不是依赖 cookie、查询参数,或仅靠 Accept-Language 检测。
✅ generaltranslation.com/en/about
✅ generaltranslation.com/fr/about
✅ generaltranslation.com/es/about
❌ generaltranslation.com/about?lang=fr
❌ generaltranslation.com/about (with locale in a cookie)
使用 gt-next 配置区域设置路由
首先,将页面嵌套到 [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 钩子,让你可以在根布局中轻松完成这一点:
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
规范标签用于告诉搜索引擎,哪个 URL 是页面的"首选"版本。 对于多语言网站,每个语言版本都应将自身设为规范版本:
<!-- 在 /fr/about 页面 -->
<link rel="canonical" href="https://example.com/fr/about" />这样可以防止搜索引擎将你的法语页面判定为英语页面的重复内容。
在 Next.js 中,你可以通过 metadata API 设置规范 URL。
将它与 gt-next 的 getLocale 结合使用,就能为每个区域设置生成正确的规范 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');
}一次调用即可同时生成规范 URL 和所有 hreflang 标签。
5. 已翻译的元数据
搜索引擎会在搜索结果中显示你的页面标题和描述。 如果法语页面上的这些内容却是英文,用户点击的可能性会更低——Google 还可能下调该结果的排名。
使用 gt-next's 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('About Us'),
description: t('Learn about our mission and team.'),
...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 中包含区域设置 | createNextMiddleware() 配合 [locale] 动态段 |
HTML lang 属性 | 在根布局中使用 useLocale() |
| 规范 URL | getLocale() + Next.js metadata API alternates.canonical |
| Hreflang 标签 | Next.js metadata API alternates.languages,包含所有支持的 区域设置 |
| 翻译后的元数据 | 使用 getGT() 生成页面标题和描述 |
| 多语言站点地图 | sitemap.ts,包含各区域设置的条目和 hreflang 替代项 |
后续步骤
- gt-next 快速开始 用于搭建完整的 i18n 技术栈
- 中间件指南 用于配置路由
- SSG 指南 用于静态生成多语言页面
- RTL 支持 用于支持从右到左书写的语言