处理不支持的语言代码
问题
一个应用程序使用 URL 路径(例如 /en/,/fr/)来确定语言,但用户可以手动输入任何值,例如 /xx/about。当该值与支持的语言不匹配时,应用程序可能会崩溃、显示通用错误或显示未翻译的内容,无法引导用户回到有效的体验。
解决方案
使用中间件拦截所有传入请求。此中间件将根据支持语言的明确列表验证 URL 中的语言代码。如果代码不受支持,请求将在到达应用程序逻辑之前被重写到“未找到”页面。
步骤
1. 定义支持的语言
创建一个中央配置文件来存储有效语言代码(语言环境)的列表。这使得该列表可以被中间件和应用程序的其他部分重复使用。
// 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) {
// 下一步将添加逻辑
}
3. 添加验证逻辑
在 middleware 函数中,从请求中获取 pathname。我们需要检查路径的第一个部分(例如 /en/about 中的 en),并查看它是否是有效的支持语言。
// 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. 单独处理根路径
// (这将在下一个教程中处理:“检测用户的首选语言”)。目前,我们只是让它通过。
if (pathname === '/') {
return NextResponse.next();
}
// 2. 从路径中提取语言代码
const langCode = pathname.split('/')[1];
// 3. 检查语言代码是否在我们的列表中
if (locales.includes(langCode)) {
// 语言有效,继续请求的页面
return NextResponse.next();
}
// 4. 如果语言无效,重写到 404 页面
// 这会在浏览器地址栏中保留无效的 URL
const url = request.nextUrl.clone();
url.pathname = `/404`; // 假设您有一个 app/404.tsx 文件
return NextResponse.rewrite(url);
}
此逻辑会检查每个请求。如果 URL 是 /fr/about,langCode 是 fr,它在 locales 中找到,请求继续。如果 URL 是 /xx/about,langCode 是 xx,它未找到,用户将看到 404 页面,而应用程序不会尝试处理无效请求。
4. 配置中间件匹配器
为了让您的中间件更高效,您应该指定它运行的路径。我们希望它运行在页面请求上,但跳过静态文件和 API 路由。
在您的 middleware.ts 文件底部添加一个 config 对象。
// middleware.ts
// ...(上面的中间件函数)
export const config = {
matcher: [
// 跳过所有以以下内容开头的路径:
// - api(API 路由)
// - _next/static(静态文件)
// - _next/image(图像优化文件)
// - favicon.ico(网站图标文件)
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
};
这个正则表达式告诉中间件运行在所有路径上,除了那些通常用于静态资源或 API 调用的路径。这可以避免对每个图像、字体或数据请求进行不必要的验证。