casualroom/apps/fenghuo/web/middleware.ts

112 lines
3.0 KiB
TypeScript
Executable File

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { I18N_CONFIG } from '@nice/i18n';
// 使用统一的 i18n 配置
const { supportedLocales, defaultLocale } = I18N_CONFIG;
export type Locale = typeof supportedLocales[number];
function getLocale(request: NextRequest): string {
// 从 Cookie 获取
const cookieLocale = request.cookies.get('NEXT_LOCALE')?.value;
if (cookieLocale && (supportedLocales as readonly string[]).includes(cookieLocale)) {
return cookieLocale;
}
// 从 Accept-Language 头获取
const acceptLanguage = request.headers.get('accept-language') || '';
const browserLocales = acceptLanguage
.split(',')
.map(lang => lang.split(';')[0]?.trim())
.filter(Boolean)
.map(lang => lang?.split('-')[0])
.filter(Boolean);
for (const browserLocale of browserLocales) {
if (browserLocale && (supportedLocales as readonly string[]).includes(browserLocale)) {
return browserLocale;
}
}
return defaultLocale;
}
export function middleware(request: NextRequest) {
const { pathname, search } = request.nextUrl;
// 跳过不需要处理的路径
if (
// API 路由
pathname.startsWith('/api/') ||
// Next.js 内部路径
pathname.startsWith('/_next/') ||
// 翻译文件路径
pathname.startsWith('/locales/') ||
// 静态文件
pathname.includes('.') ||
// 特定文件
pathname === '/favicon.ico' ||
pathname === '/sitemap.xml' ||
pathname === '/robots.txt' ||
// 图片文件
/\.(jpg|jpeg|png|gif|svg|ico|webp)$/i.test(pathname) ||
// CSS/JS 文件
/\.(css|js|mjs)$/i.test(pathname)
) {
return NextResponse.next();
}
// 检查是否已经有语言前缀
const hasLocalePrefix = supportedLocales.some(
locale => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
);
if (!hasLocalePrefix) {
// 获取用户的语言偏好
const locale = getLocale(request);
// 重定向到带语言前缀的 URL
const newUrl = new URL(`/${locale}${pathname}`, request.url);
newUrl.search = search;
const response = NextResponse.redirect(newUrl);
// 设置 Cookie 保存语言偏好
response.cookies.set('NEXT_LOCALE', locale, {
maxAge: 365 * 24 * 60 * 60, // 1 year
path: '/',
sameSite: 'lax'
});
return response;
}
// 验证语言是否支持
const pathLocale = pathname.split('/')[1];
if (pathLocale && !(supportedLocales as readonly string[]).includes(pathLocale)) {
// 不支持的语言,重定向到默认语言
const newPathname = pathname.replace(`/${pathLocale}`, `/${defaultLocale}`);
const newUrl = new URL(newPathname, request.url);
newUrl.search = search;
return NextResponse.redirect(newUrl);
}
// 添加语言信息到请求头,供服务端组件使用
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-pathname', pathname);
return NextResponse.next({
request: {
headers: requestHeaders,
}
});
}
// Middleware matcher configuration
export const config = {
matcher: [
'/((?!api|_next/static|_next/image|favicon.ico|locales|.*\\..*).*)'
],
};