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|.*\\..*).*)' ], };