You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

混合/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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 23:39:50