如何在 Next.js(Pages Router)v16 中验证 URL 中的 locale 参数
优雅处理不支持的 locale 代码
问题
当 locale 代码成为 URL 结构的一部分时,它们就变成了需要验证的用户输入。访问者可以像输入 /xx/about 或 /gibberish/contact 一样轻松地输入 /en/about 这样的有效 locale。如果不进行验证,应用可能会崩溃、显示异常内容,或出现令人困惑的错误信息。遇到无效 locale URL 的用户需要明确的后续指引——要么重定向到有效 locale,要么返回合适的“未找到”页面,帮助他们理解问题所在。
解决方案
在请求到达页面组件之前,先将传入的 locale 参数与支持的 locale 列表进行校验。使用 Next.js 的 middleware 拦截请求,检查 URL 中的 locale 是否为受支持的值,并做出相应处理。对于无效的 locale,可以将用户重定向到默认 locale,或重写请求以显示 404 页面。这样可以确保只有有效的 locale 代码才能渲染内容,而无效代码则被优雅处理,不会破坏用户体验。
步骤
1. 在 Next.js 配置中定义支持的 locale
在 next.config.js 中配置 i18n 设置,声明应用支持哪些 locale 以及默认 locale。
module.exports = {
i18n: {
locales: ["en", "fr", "de"],
defaultLocale: "en",
localeDetection: false,
},
};
将 localeDetection 设置为 false,可以防止根据浏览器偏好自动重定向,从而让你完全掌控 locale 处理逻辑。
2. 创建 middleware 验证 locale 参数
在项目根目录下创建 middleware.ts 文件,或者如果你有 src 文件夹,也可以放在里面。
import { NextRequest, NextResponse } from "next/server";
const SUPPORTED_LOCALES = ["en", "fr", "de"];
const DEFAULT_LOCALE = "en";
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
const pathnameHasLocale = SUPPORTED_LOCALES.some(
(locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`,
);
if (!pathnameHasLocale) {
return;
}
const localeInPath = pathname.split("/")[1];
if (!SUPPORTED_LOCALES.includes(localeInPath)) {
const url = request.nextUrl.clone();
url.pathname = pathname.replace(`/${localeInPath}`, `/${DEFAULT_LOCALE}`);
return NextResponse.redirect(url);
}
}
export const config = {
matcher: ["/((?!_next|api|favicon.ico|.*\\..*).*)"],
};
中间件会从 URL 路径中提取 locale,与支持的 locale 数组进行校验,并在 locale 无效时将其重定向到默认 locale,同时保留其余路径。
3. 使用 404 响应处理无效的 locale
如果你希望显示 404 页面而不是重定向,可以将请求重写到自定义 404 页面。
import { NextRequest, NextResponse } from "next/server";
const SUPPORTED_LOCALES = ["en", "fr", "de"];
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
const pathnameHasLocale = SUPPORTED_LOCALES.some(
(locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`,
);
if (!pathnameHasLocale) {
return;
}
const localeInPath = pathname.split("/")[1];
if (!SUPPORTED_LOCALES.includes(localeInPath)) {
const url = request.nextUrl.clone();
url.pathname = "/404";
return NextResponse.rewrite(url);
}
}
export const config = {
matcher: ["/((?!_next|api|favicon.ico|.*\\..*).*)"],
};
在 pages/404.js 处创建一个自定义 404 页面,该页面会在构建时静态生成。
4. 创建自定义 404 页面
export default function Custom404() {
return (
<div>
<h1>404 - Page Not Found</h1>
<p>The page you are looking for does not exist.</p>
</div>
);
}
当中间件重写无效 locale 请求时,将显示此页面,为用户提供明确的提示,说明所请求的 URL 不可用。