混合/pages与/app目录的Next.js应用国际化处理方案咨询
混合/pages与/app架构下的Next.js国际化方案
不用退回到静态路由,有成熟的混合方案可以同时支撑两套路由的国际化需求,核心思路是保留原next.config.js的i18n配置(为/pages服务),同时给/app路由补充适配逻辑,确保两套路由的国际化行为一致。
1. 保留原next.config.js的i18n配置
虽然App Router会抛出警告,但这个配置是/pages路由依赖的核心,必须保留。警告不会影响功能运行,可以忽略,或者后续通过webpack配置过滤特定警告信息。
你的原配置可继续使用:
i18n: { locales: ['en', 'es', 'fr'], defaultLocale: 'en', localeDetection: false, }
2. 给App Router适配现有国际化规则
(1)创建匹配的路由结构
在/app目录下按照/app/[lang]/...的结构组织页面,比如:
/app/en/about对应/pages/about(en locale)/app/es/about对应/pages/about(es locale)
这样能和/pages的路由逻辑对齐,避免用户混淆。
(2)用中间件统一处理Locale逻辑
创建middleware.ts文件,统一处理路由的Locale检测、重定向和cookie设置,确保/pages和/app的行为一致:
import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; const locales = ['en', 'es', 'fr']; const defaultLocale = 'en'; export function middleware(request: NextRequest) { // 跳过API、静态资源等不需要处理的路径 const skipPaths = ['/api', '/_next/static', '/_next/image', '/favicon.ico']; if (skipPaths.some(path => request.nextUrl.pathname.startsWith(path))) { return NextResponse.next(); } const pathname = request.nextUrl.pathname; // 提取URL中的Locale前缀 const localeMatch = pathname.match(/^\/([a-z]{2})\/?/); const currentLocale = localeMatch ? localeMatch[1] : defaultLocale; // /pages路由交给原i18n配置处理 if (!localeMatch && !pathname.startsWith('/app')) { return NextResponse.next(); } // /app路由无Locale前缀时,重定向到默认Locale路由 if (!localeMatch) { return NextResponse.redirect(new URL(`/${defaultLocale}${pathname}`, request.url)); } // 验证Locale合法性,非法则重定向到默认Locale if (!locales.includes(currentLocale)) { const cleanedPath = pathname.replace(/^\/[a-z]{2}/, ''); return NextResponse.redirect(new URL(`/${defaultLocale}${cleanedPath}`, request.url)); } // 设置Locale Cookie,保持全局状态一致 const response = NextResponse.next(); response.cookies.set('NEXT_LOCALE', currentLocale); return response; } export const config = { matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'], };
(3)在App Router中传递Locale
在/app/[lang]/layout.tsx中创建布局组件,提取并传递Locale给子组件:
import { ReactNode } from 'react'; type LocaleLayoutProps = { children: ReactNode; params: { lang: string }; }; export default function LocaleLayout({ children, params }: LocaleLayoutProps) { // 可将Locale存入React Context,方便全局组件调用 return <div lang={params.lang}>{children}</div>; }
3. 共享国际化资源
将翻译文件(比如public/locales/en.json、public/locales/es.json)放在公共目录,让/pages和/app可以通过相同的工具函数加载:
// utils/i18n.ts export async function getTranslations(locale: string) { const translationModule = await import(`../public/locales/${locale}.json`); return translationModule.default; }
- 在/pages中,通过
getServerSideProps或getStaticProps获取Locale和翻译; - 在/app中,通过
generateStaticParams(静态生成)或异步组件加载翻译。
4. 最佳实践
- 统一路由规则:确保/pages和/app的同功能页面路由结构一致,避免用户困惑;
- 集中处理Locale逻辑:所有Locale相关的检测、重定向都通过中间件完成,不要在两套路由中重复实现;
- 逐步迁移:优先迁移国际化逻辑简单的页面,复杂页面(如需要动态Locale切换的)放在后期处理,迁移完成后再移除
next.config.js的i18n配置,切换到App Router原生国际化方案; - 全面测试:覆盖所有Locale下的/pages和/app页面,验证路由跳转、翻译显示、Locale切换等功能是否正常。
内容的提问来源于stack exchange,提问作者Baldini
相关产品推荐
相关产品推荐

