当我们开发一个支持多语言的 Next.js 网站时,常常需要解决以下问题:
-
用户首次访问时,应该显示哪个语言版本? 🤔
比如,用户访问
/
时,是展示/en
还是/de
? -
SEO 是否能够抓取所有语言版本的页面? 🔍
搜索引擎如何识别
/en
和/de
,并在搜索结果中正确展示? -
中间件是否会干扰静态资源的加载? 🚧
比如
/favicon.ico
或/_next
这样的资源路径是否会被错误地处理? -
访问未带前缀的页面是否需要跳转? 🧭
比如用户访问
/blog/123
时,是否应该自动跳转到/en/blog/123
或/zh-CN/blog/123
?这些问题的核心都与 中间件(Middleware) 的配置密切相关。如果中间件设置不当,不仅会影响用户体验,还会导致搜索引擎抓取异常,最终影响网站的 SEO 表现。
本篇文章将通过案例为你详细解析这些问题,并手把手教你如何配置中间件,让多语言网站更加智能 🌟、高效 🚀 且 SEO 友好 💡。
问题 1:用户首次访问时,应该显示哪个语言版本?
问题背景
当用户访问网站的根路径 /
时,他们希望直接进入自己的偏好语言页面(比如 /zh
中文版)。如果默认跳转到 /en
(英文版),可能会让不熟悉英文的用户感到困惑,降低用户体验。
那么问题来了:如何在用户首次访问时,自动检测并跳转到他们期望的语言页面呢?
中间件的解决方案:语言检测逻辑
中间件通过以下优先级,动态检测用户的语言:
- 路径前缀(最高优先级)
如果用户访问路径中包含语言前缀(如/en/about
或/zh/about
),中间件会直接使用该语言。 - Cookie
中间件会检查是否存在NEXT_LOCALE
Cookie,这个 Cookie 会保存用户之前选择的语言。例如,如果用户上次访问了/zh
,中间件会自动记住并再次跳转到/zh
。 - 浏览器语言头
如果路径和 Cookie 中都没有语言信息,中间件会读取浏览器的Accept-Language
请求头,选择最适合的语言。
例如,浏览器语言头显示用户的首选语言为zh
(中文),中间件会选择中文页面。 - 默认语言(最低优先级)
如果以上条件都无法确定语言,中间件会使用defaultLocale
中配置的默认语言。比如设置为en
,用户会被跳转到英文页面。
代码示例:语言检测与跳转
typescript
// middleware.ts
import createMiddleware from 'next-intl/middleware';
export default createMiddleware({
locales: ['en', 'zh'], // 支持的语言:英文和中文
defaultLocale: 'en', // 默认语言为英文
});
export const config = {
matcher: ['/', '/(en|zh)/:path*'], // 匹配根路径和带语言前缀的路径
};
实际流程演示
- 用户首次访问
/
:- 浏览器的
Accept-Language
请求头为zh
。 - 中间件重定向到
/zh
,并设置 CookieNEXT_LOCALE=zh
。
- 浏览器的
- 用户再次访问
/
:- 中间件检查 Cookie
NEXT_LOCALE
,发现值为zh
,直接跳转到/zh
。
- 中间件检查 Cookie
通过这样的逻辑,我们可以为每位用户提供符合他们偏好的语言页面,极大提升用户体验。
问题 2:SEO 是否能够抓取所有语言版本的页面?
问题背景
对于多语言网站来说,SEO 优化的重点有两个:
- 是否能抓取到所有语言版本的页面?
比如,搜索引擎是否能分别抓取/en/about
(英文版)和/zh/about
(中文版)? - 不同语言页面的关联是否正确?
如果没有正确配置hreflang
标签,不同语言页面可能被搜索引擎视为重复内容,从而影响排名。
中间件的解决方案
- 为每种语言生成独立路径
中间件通过路径前缀(如/en
和/zh
),为每种语言生成独立页面 URL,确保搜索引擎能够抓取到每个版本。 - 生成
hreflang
标签
配置hreflang
标签,明确指示页面的语言版本关联关系,帮助搜索引擎正确识别。
代码示例:SEO 配置
中间件配置:
typescript
// middleware.ts
import createMiddleware from 'next-intl/middleware';
export default createMiddleware({
locales: ['en', 'zh'], // 支持英文和中文
defaultLocale: 'en', // 默认语言为英文
});
export const config = {
matcher: ['/', '/(en|zh)/:path*'], // 匹配路径
};
Sitemap 配置:
typescript
// next-sitemap.config.js
module.exports = {
siteUrl: 'https://example.com',
generateRobotsTxt: true,
alternateRefs: [
{ href: 'https://example.com/en', hreflang: 'en' },
{ href: 'https://example.com/zh', hreflang: 'zh' },
],
};
SEO 效果展示
/en/about
和/zh/about
被搜索引擎视为独立页面。hreflang
标签告诉搜索引擎这些页面是不同语言的版本,避免重复内容问题。
问题 3:中间件是否会干扰静态资源的加载?
问题背景
中间件默认会拦截所有请求路径,包括静态资源路径(如 /favicon.ico
或 /_next/static
)。如果中间件误处理了这些路径,可能导致:
- 静态资源无法加载,页面展示异常。
- 性能下降,因为中间件错误地拦截了无关请求。
中间件的解决方案
通过配置 matcher
,明确排除静态资源路径,仅处理与国际化相关的路径。
代码示例:排除静态资源路径
typescript
export const config = {
matcher: [
'/((?!api|_next|.*\\..*).*)', // 匹配非静态资源路径
'/([\\w-]+)?/dynamic-route/(.+)', // 显式匹配动态路径
],
};
效果展示
- 路径
/favicon.ico
和/_next/static
不会被中间件处理。 - 中间件只针对
/en
和/zh
等与语言相关的路径生效。
问题 4:访问未带前缀的页面是否需要跳转?
问题背景
在多语言网站中,用户可能直接访问未带语言前缀的路径,例如 /blog/123
。这种情况下:
- 如果网站没有设计无前缀的页面,则需要跳转到对应语言前缀的页面(如
/en/blog/123
)。 - 如果网站支持无前缀的默认语言页面,则可以不跳转,直接展示默认语言内容。
那么问题来了:如何根据需求正确设置页面跳转?
中间件的解决方案:配置跳转逻辑
在中间件中可以通过 matcher
配置对未带前缀的路径进行拦截并自动跳转。例如:
- 检测用户的语言偏好(
Accept-Language
或 Cookie); - 重定向到对应语言前缀的页面。
代码示例
以下是一个通用的配置,处理未带前缀的 /blog/...
路径跳转到对应语言页面:
typescript
// middleware.ts
import createMiddleware from 'next-intl/middleware';
export default createMiddleware({
locales: ['en', 'zh-CN', 'zh-HK'], // 支持的语言
defaultLocale: 'en', // 默认语言为英文
});
export const config = {
matcher: [
'/',
'/blog/:path*', // 未带前缀的博客路径
'/(en|zh-CN|zh-HK)/:path*', // 带前缀的其他路径
'/review',
],
};
实际场景演示
- 用户访问
/blog/123
:- 中间件会检测用户的语言(例如,
Accept-Language
为zh-CN
)。 - 跳转到
/zh-CN/blog/123
。
- 中间件会检测用户的语言(例如,
- 用户访问
/blog
:- 如果
Cookie
中记录的语言是en
,则跳转到/en/blog
。
- 如果
- 用户访问未匹配的路径:
- 默认跳转到
/en
,或返回 404。
- 默认跳转到
小结
配置跳转逻辑可以实现:
- 自动将未带语言前缀的页面跳转到带前缀的对应页面;
- 确保所有页面都有一致的 URL 格式,有助于 SEO 和用户体验。
**总结 **📝
- 用户语言的自动检测与跳转 :
中间件通过路径前缀、Cookie 和浏览器语言头动态检测语言,并自动跳转到用户期望的页面。 - SEO 的多语言支持 :
中间件为每种语言生成独立路径,并结合hreflang
标签,确保搜索引擎正确抓取和展示不同语言页面。 - 避免静态资源的干扰 :
通过配置matcher
,中间件仅处理国际化相关路径,确保静态资源和其他非国际化路径正常加载。
合理配置中间件,不仅能提升用户体验,还能让你的多语言网站在 SEO 表现上更上一层楼! 🌟