检测用户的首选语言
将新访客重定向到其最可能使用的语言
问题
当用户首次访问应用的根路径(例如,/)时,通常会显示默认语言(如 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)都不会被此逻辑处理,直接通过。