返回

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

Team avatarTeam
指南seo国际化nextjsi18n路由元数据

为什么多语言 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-USarzh-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()
规范 URLgetLocale() + Next.js metadata API alternates.canonical
Hreflang 标签Next.js metadata API alternates.languages,包含所有支持的 区域设置
翻译后的元数据使用 getGT() 生成页面标题和描述
多语言站点地图sitemap.ts,包含各区域设置的条目和 hreflang 替代项

后续步骤