Resolve App Router routes when Pages Router i18n domains are configured#94606
Open
RobVermeer wants to merge 1 commit into
Open
Resolve App Router routes when Pages Router i18n domains are configured#94606RobVermeer wants to merge 1 commit into
RobVermeer wants to merge 1 commit into
Conversation
When the Pages Router `i18n` config is set (e.g. with domain routing), the locale prefix was stripped from every request before route matching. App Router routes whose first segment is the locale (e.g. `app/[lang]/test`) therefore could never match a locale-prefixed pathname such as `/nl-NL/test` (whether reached directly or via a `proxy.ts` rewrite of `/test`), and returned 404. Statically prerendering such routes also failed the build with "export path doesn't match the page". App Router routes are not locale-aware: the locale is part of the route path itself, so the Pages Router locale machinery must not strip it for them. This is fixed at the three points where the locale prefix was being removed: - resolve-routes (dev/prod routing): App Router dynamic routes are now also matched against the full, un-stripped pathname so a leading `[lang]` segment can capture the locale. Pages Router routes still match the stripped path. - base-server (invoke path handling): when the resolved output is an App Router route that actually consumes the locale prefix, the locale prefix is kept in the pathname (the basePath is still stripped) and the locale is treated as explicit, instead of being stripped like a Pages Router route. - export worker (static generation): App Router export paths keep their locale prefix and match params against the full path. All changes are gated so non-i18n apps and Pages Router behavior are unaffected. Adds e2e fixtures covering both routers under domain-based i18n across direct and proxy-rewritten access, with and without a basePath. Fixes vercel#86048
debebbf to
41c1891
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What?
App Router routes that use a dynamic locale segment (e.g.
app/[lang]/test) returned 404 — andnext buildfailed — whenever the Pages Routeri18nconfig (withdomains) was set. This makes both routers work together under domain-based i18n.Fixes #86048
Why?
Pages Router i18n treats the locale as metadata and strips the
/<locale>prefix from a request before route matching. App Router has no i18n system: when you localize withapp/[lang]/..., the locale is a real route segment that must stay in the path so[lang]can capture it.With
i18nconfigured, the Pages-Router stripping ran for every request and removed the prefix App Router needs, so/[lang]/testcould never match/nl-NL/test(whether reached directly or via aproxy.tsrewrite of/test), and statically prerendering it failed the build withexport path '/test' doesn't match the '/[lang]/test' page.How?
The locale prefix was being stripped in three places; each now preserves it for App Router routes that actually consume it, while Pages Router behavior is unchanged:
server/lib/router-utils/resolve-routes.ts(dev/prod routing): dynamic App Router routes are also matched against the full, un-stripped pathname so a leading[lang]segment can capture the locale. Pages Router routes still match the locale-stripped pathname.server/base-server.ts(invoke-path handling): when the resolved output is an App Router route whose regex matches the locale-prefixed path, the prefix is kept (and the locale treated as explicit) instead of being stripped.export/worker.ts(static generation): App Router export paths keep their locale prefix and match params against the full path.Every branch is gated so non-i18n apps, Pages Router routing, and App Router routes that don't model the locale (e.g.
/about,/items/[id]) are unaffected.This is an alternative approach to #86116 — it additionally fixes direct
/<locale>/<route>access and static generation, which that draft documents as a known limitation.Verification
test/e2e/i18n-app-pages-domainpasses 7/7 in all four modes (dev+webpack, start+webpack, dev+turbo, start+turbo): Pages Router root, App Router viaproxy.tsrewrite, direct locale-prefixed URLs, a nested/[lang]/blog/[slug]route, and production static generation.test/e2e/i18n-support(103 passed) andtest/e2e/i18n-data-route(12 passed).pnpm --filter=next types, prettier, and eslint pass.Related issues
This is the same underlying root cause behind several long-standing reports of the Pages Router
i18nconfig breaking App Router routing (~76 👍 combined):[locale]parameter, i18n enabled for pages (NEXT-1180)Possibly related but not verified here (different symptoms): #84371, #90131.