70 lines
2.3 KiB
TypeScript
70 lines
2.3 KiB
TypeScript
|
|
import { NextRequest, NextResponse } from 'next/server';
|
||
|
|
import { locales, defaultLocale } from './lib/i18n';
|
||
|
|
|
||
|
|
export function middleware(request: NextRequest) {
|
||
|
|
// 在静态导出模式下,中间件不会执行
|
||
|
|
// 这个文件保留用于开发和服务器模式
|
||
|
|
|
||
|
|
// 在静态导出模式下跳过中间件逻辑
|
||
|
|
if (process.env.NODE_ENV === 'production' && process.env.NEXT_OUTPUT_MODE === 'export') {
|
||
|
|
return NextResponse.next();
|
||
|
|
}
|
||
|
|
|
||
|
|
const pathname = request.nextUrl.pathname;
|
||
|
|
|
||
|
|
// Check if there is any supported locale in the pathname
|
||
|
|
const pathnameIsMissingLocale = locales.every(
|
||
|
|
(locale) => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}`,
|
||
|
|
);
|
||
|
|
|
||
|
|
// Redirect if there is no locale
|
||
|
|
if (pathnameIsMissingLocale) {
|
||
|
|
// Get locale from Accept-Language header or use default
|
||
|
|
const locale = getLocaleFromRequest(request) || defaultLocale;
|
||
|
|
|
||
|
|
// Don't redirect for default locale on root path
|
||
|
|
if (locale === defaultLocale && pathname === '/') {
|
||
|
|
return NextResponse.next();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Redirect to locale-prefixed path for non-default locales
|
||
|
|
if (locale !== defaultLocale) {
|
||
|
|
return NextResponse.redirect(
|
||
|
|
new URL(`/${locale}${pathname === '/' ? '' : pathname}`, request.url),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return NextResponse.next();
|
||
|
|
}
|
||
|
|
|
||
|
|
function getLocaleFromRequest(request: NextRequest): string | undefined {
|
||
|
|
// Get locale from cookie first
|
||
|
|
const localeCookie = request.cookies.get('locale')?.value;
|
||
|
|
if (localeCookie && locales.includes(localeCookie as any)) {
|
||
|
|
return localeCookie;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get locale from Accept-Language header
|
||
|
|
const acceptLanguage = request.headers.get('accept-language');
|
||
|
|
if (acceptLanguage) {
|
||
|
|
const preferredLocale = acceptLanguage
|
||
|
|
.split(',')
|
||
|
|
.map((lang) => lang.split(';')[0].trim())
|
||
|
|
.find((lang) => locales.includes(lang as any));
|
||
|
|
|
||
|
|
if (preferredLocale) {
|
||
|
|
return preferredLocale;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return defaultLocale;
|
||
|
|
}
|
||
|
|
|
||
|
|
export const config = {
|
||
|
|
matcher: [
|
||
|
|
// Skip all internal paths (_next), static files, and manifest files
|
||
|
|
'/((?!_next|api|favicon.ico|site.webmanifest|.*\\..*).*)',
|
||
|
|
],
|
||
|
|
};
|