检测用户的首选语言

将新访客重定向到其最可能使用的语言

问题

当用户首次访问应用的根路径(例如,/)时,通常会显示默认语言(如 English )。这会给其他语言用户带来不便,迫使他们手动查找语言切换器,尽管他们的浏览器已经传递了语言偏好。

解决方案

使用中间件拦截对根路径(/)的请求。检查用户的 Accept-Language HTTP 头以获取其首选语言。如果该语言在应用支持的范围内,则将用户重定向到该语言的根路径(例如,/fr)。如果不支持,则重定向到默认语言(例如,/en)。

步骤

1. 安装语言解析器

Accept-Language 头可能很复杂(例如,fr-CH, fr;q=0.9, en;q=0.8)。一个小型库可以帮助解析该头部,并从支持的语言列表中找到最佳匹配项。

在终端中运行以下命令:

npm install accept-language-parser

2. 定义支持的语言和默认值

创建一个中心化配置文件,用于存储支持的语言列表并定义默认语言。如果用户的浏览器偏好未匹配到任何支持的语言,则会使用该默认值。

// i18n.config.ts

export const locales = ['en', 'es', 'fr'];
export const defaultLocale = 'en';

3. 创建中间件

在项目根目录下创建 middleware.ts 文件。该文件会在每次请求时运行,使你能够检查路径和头部信息。

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import parser from 'accept-language-parser';
import { locales, defaultLocale } from './i18n.config';

// Helper function to find the best language match
function getBestLocale(acceptLangHeader: string | null) {
  if (!acceptLangHeader) {
    return defaultLocale;
  }

  // Use the parser to find the best supported language
  const bestMatch = parser.pick(locales, acceptLangHeader, {
    loose: true,
  });

  return bestMatch || defaultLocale;
}

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  // 1. Check if the request is for the root path
  if (pathname === '/') {
    // Get the user's preferred language
    const acceptLang = request.headers.get('Accept-Language');
    const bestLocale = getBestLocale(acceptLang);

    // Redirect to the best-matched language path
    request.nextUrl.pathname = `/${bestLocale}`;
    return NextResponse.redirect(request.nextUrl);
  }

  // 2. For all other paths, continue as normal
  return NextResponse.next();
}

export const config = {
  matcher: [
    // Skip all paths that start with:
    // - api (API routes)
    // - _next/static (static files)
    // - _next/image (image optimization files)
    // - favicon.ico (favicon file)
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
};

此代码仅对根路径(/)执行逻辑。如果用户访问 /,则会检查其 Accept-Language 头,找到最佳匹配(例如,es),并重定向到 /es。其他所有请求(如 /en/about)都不会被此逻辑处理,直接通过。