返回

如何为多语言 Next.js 应用优化 SEO

General Translation avatarGeneral Translation
guideseointernationalizationnextjsi18nroutingmetadata

为什么多语言 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-USarzh-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() 设置页面标题和描述
多语言 sitemapsitemap.ts 中为每个 locale 添加条目和 hreflang 替代版本链接

后续步骤