处理不受支持的语言代码
问题
某些应用通过 URL 路径(例如,/en/、/fr/)来判断语言,但用户可以手动输入任意值,如 /xx/about。当该值不在支持的语言列表中时,应用可能会崩溃、显示通用错误,或出现未翻译内容,无法有效引导用户回到正常体验。
解决方案
使用中间件拦截所有传入请求。该中间件会根据一份权威的支持语言列表校验 URL 中的语言代码。如果代码不受支持,请求会在到达应用逻辑之前被重定向到“未找到”页面。
步骤
1. 定义支持的语言
创建一个中心化的配置文件,用于存储有效语言代码(locale)列表。这样可以让中间件和应用其他部分复用该列表。
// i18n.config.ts
export const locales = ['en', 'es', 'fr'];
2. 创建中间件文件
在项目根目录(或 src/ 目录)下新建名为 middleware.ts 的文件。Next.js 会自动检测并在请求时运行该文件。
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { locales } from './i18n.config';
export function middleware(request: NextRequest) {
// Logic will go here in the next step
}
3. 添加校验逻辑
在 middleware 函数内部,从请求中获取 pathname。我们需要检查路径的第一个片段(例如 en,在 /en/about 中),判断其是否为有效且受支持的语言。
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { locales } from './i18n.config';
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// 1. Handle the root path separately
// (This will be handled by the next recipe: "Detecting a user's
// preferred language"). For now, we just let it pass.
if (pathname === '/') {
return NextResponse.next();
}
// 2. Extract the language code from the path
const langCode = pathname.split('/')[1];
// 3. Check if the language code is in our list
if (locales.includes(langCode)) {
// Language is valid, continue to the requested page
return NextResponse.next();
}
// 4. If the language is not valid, rewrite to a 404 page
// This keeps the invalid URL in the browser bar
const url = request.nextUrl.clone();
url.pathname = `/404`; // Assumes you have an app/404.tsx file
return NextResponse.rewrite(url);
}
该逻辑会检查每个请求。如果 URL 是 /fr/about,langCode 为 fr,在 locales 中找到,请求继续处理。如果 URL 是 /xx/about,langCode 为 xx,未找到,则用户会直接看到 404 页面,应用不会尝试处理无效请求。
4. 配置 middleware 匹配器
为了提高 middleware 的效率,您应指定其运行的路径。我们希望它在页面请求时运行,但跳过静态文件和 API 路由。
在 config 文件的末尾添加一个 middleware.ts 对象。
// middleware.ts
// ... (the middleware function from above)
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).*)',
],
};
此正则表达式指示 middleware 在所有路径上运行,但会排除通常用于静态资源或 API 调用的路径。这样可以避免对每个图片、字体或数据请求进行不必要的校验。