import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; const locales = ['zh-CN', 'zh-TW', 'en', 'ko', 'ja']; const defaultLocale = 'zh-CN'; function getLocale(request: NextRequest): string { // Check if there is any supported locale in the pathname const pathname = request.nextUrl.pathname; const pathnameIsMissingLocale = locales.every( (locale) => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}`, ); // If there's no locale in the pathname, detect from headers if (pathnameIsMissingLocale) { const acceptLanguage = request.headers.get('accept-language'); if (acceptLanguage) { // Parse accept-language header const languages = acceptLanguage.split(',').map((lang) => lang.split(';')[0].trim()); // Find the first matching locale for (const lang of languages) { if (lang.startsWith('zh-TW') || lang.startsWith('zh-HK')) { return 'zh-TW'; } else if (lang.startsWith('zh')) { return 'zh-CN'; } else if (lang.startsWith('ko')) { return 'ko'; } else if (lang.startsWith('ja')) { return 'ja'; } else if (lang.startsWith('en')) { return 'en'; } } } return defaultLocale; } // Extract locale from pathname const locale = pathname.split('/')[1]; return locales.includes(locale) ? locale : defaultLocale; } export function middleware(request: NextRequest) { const pathname = request.nextUrl.pathname; // Skip middleware for sitemap.xml, robots.txt and other static files const staticFiles = ['/sitemap.xml', '/robots.txt', '/favicon.ico', '/manifest.json']; if (staticFiles.includes(pathname)) { return NextResponse.next(); } // 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) { const locale = getLocale(request); return NextResponse.redirect( new URL(`/${locale}${pathname.startsWith('/') ? '' : '/'}${pathname}`, request.url), ); } } export const config = { // Matcher ignoring static files, API routes, and special files matcher: [ /* * Match all request paths except for the ones starting with: * - api (API routes) * - _next/static (static files) * - _next/image (image optimization files) * - favicon.ico, sitemap.xml, robots.txt (static files) */ '/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt|manifest.json).*)', ], };