持久化用户的语言选择
使用 Cookie 记住用户选择
问题
用户在网站上手动选择了“法语”。当他们关闭浏览器后,再次输入网站主地址(例如,example.com),应用会恢复为默认语言(如英文)或自动检测的语言。由于未能记住用户的选择,用户每次开启新会话时都需要重新找到语言切换器并再次选择语言。
解决方案
当用户选择语言时,将该选择存储在 Cookie 中。在中间件中,当用户访问根路径(/)时,应在检查 Accept-Language 头部之前,先检查此 Cookie。如果找到有效的 Cookie,则将用户重定向到其所选语言的根路径(例如,/fr),覆盖任何浏览器默认设置。
步骤
1. 定义语言配置
创建一个中心化的配置文件,用于存储支持的语言、默认语言以及用于保存用户偏好的 Cookie 名称。
// i18n.config.ts
export const locales = ['en', 'es', 'fr'];
export const defaultLocale = 'en';
export const localeCookieName = 'NEXT_LOCALE';
2. 安装语言解析器
你仍然需要一个解析 Accept-Language 头部的解析器,如果没有设置 Cookie,将作为回退方案使用。
npm install accept-language-parser
3. 创建中间件
在项目根目录下创建 middleware.ts 文件。该中间件会首先检查 NEXT_LOCALE Cookie。如果未找到,则回退检查 Accept-Language 头部。此逻辑仅适用于对根路径(/)的请求。
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import parser from 'accept-language-parser';
import {
locales,
defaultLocale,
localeCookieName,
} from './i18n.config';
function getPreferredLocale(request: NextRequest) {
// 1. Check for the cookie
const cookie = request.cookies.get(localeCookieName);
if (cookie) {
const locale = cookie.value;
if (locales.includes(locale)) {
return locale;
}
}
// 2. Check the Accept-Language header
const acceptLang = request.headers.get('Accept-Language');
if (acceptLang) {
const bestMatch = parser.pick(locales, acceptLang, {
loose: true,
});
if (bestMatch) {
return bestMatch;
}
}
// 3. Return the default
return 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 (from cookie or header)
const bestLocale = getPreferredLocale(request);
// 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: [
// We only want to run this on the root path for now
'/',
// We also need to match non-root paths to let them pass
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
};
该中间件的逻辑现在能够在用户访问站点根目录时,正确地将用户的显式选择(cookie)优先于其隐式偏好(浏览器头部信息)进行处理。