Skip to content

Fix adapter i18n App route handling - #97500

Draft
timneutkens wants to merge 1 commit into
codex/versioned-i18n-pages-api-routingfrom
codex/fix-app-i18n-route-matching
Draft

timneutkens wants to merge 1 commit into
codex/versioned-i18n-pages-api-routingfrom
codex/fix-app-i18n-route-matching

Conversation

@timneutkens

@timneutkens timneutkens commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fix adapter-generated routing metadata for App Pages and App Routes in projects that also configure Pages i18n.

Dynamic App routes now use their literal pathname ownership instead of receiving a synthetic nextLocale prefix in their matcher and destination. Locale-like App prerenders are also linked to their App output before Pages locale normalization, preventing them from being associated with the wrong output.

This PR is stacked on #97366, which introduces routing manifest v2 and keeps Pages API routes canonical. The built-in router/runtime behavior is isolated in #98095 so the adapter contract and ownership model can be reviewed separately.

Adds adapter assertions for App Page and App Route matchers, destinations, prerender parent linkage, and output deduplication, along with deployment-compatible coverage for the shared i18n routing fixture.

Verification

  • pnpm build-all
  • NEXT_TEST_PREFER_OFFLINE=1 pnpm test-start-webpack test/production/adapter-config-i18n/adapter-config-i18n.test.ts (4 passed)
  • Prettier and ESLint checks on all changed files

@github-actions

github-actions Bot commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor

Failing test suites

Commit: 9561de0 | About building and testing Next.js

pnpm test-dev-experimental-turbo test/e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts (turbopack) (Experimental) (job)

  • i18n-app-pages-domain-routing > keeps Pages Router domain locale handling (DD)
  • i18n-app-pages-domain-routing > routes a proxy locale rewrite to a dynamic App page (DD)
Expand output

● i18n-app-pages-domain-routing › keeps Pages Router domain locale handling

expect(received).toBe(expected) // Object.is equality

Expected: "nl-NL"
Received: "en-US"

  33 |
  34 |     expect(response.status).toBe(200)
> 35 |     expect(load(await response.text())('#pages-locale').text()).toBe('nl-NL')
     |                                                                 ^
  36 |   })
  37 |
  38 |   it('routes a proxy locale rewrite to a dynamic App page', async () => {

  at Object.toBe (e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts:35:65)

● i18n-app-pages-domain-routing › routes a proxy locale rewrite to a dynamic App page

expect(received).toBe(expected) // Object.is equality

Expected: "nl-NL"
Received: "en-US"

  40 |
  41 |     expect(response.status).toBe(200)
> 42 |     expect(load(await response.text())('#app-locale').text()).toBe('nl-NL')
     |                                                               ^
  43 |   })
  44 |
  45 |   it('routes an explicit locale pathname to a dynamic App page', async () => {

  at Object.toBe (e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts:42:63)

pnpm test-dev-experimental-turbo test/development/app-dir/lazy-dynamic-imports/lazy-dynamic-imports.test.ts (turbopack) (Experimental) (job)

  • lazy-dynamic-imports > does not parse a dynamic import target before activation (DD)
Expand output

● lazy-dynamic-imports › does not parse a dynamic import target before activation

expect(received).toContain(expected) // indexOf

Matcher error: received value must not be null nor undefined

Received has value: null

  257 |         await browser.elementByCss('#load-parse-error').click()
  258 |         await waitForRedbox(browser)
> 259 |         expect(await getRedboxSource(browser)).toContain(
      |                                                ^
  260 |           'parse-error-proves-target-was-analyzed'
  261 |         )
  262 |       } finally {

  at Object.toContain (development/app-dir/lazy-dynamic-imports/lazy-dynamic-imports.test.ts:259:48)

pnpm test-start-experimental-turbo test/e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts (turbopack) (Experimental) (job)

  • i18n-app-pages-domain-routing > keeps Pages Router domain locale handling (DD)
  • i18n-app-pages-domain-routing > routes a proxy locale rewrite to a dynamic App page (DD)
Expand output

● i18n-app-pages-domain-routing › keeps Pages Router domain locale handling

expect(received).toBe(expected) // Object.is equality

Expected: "nl-NL"
Received: "en-US"

  33 |
  34 |     expect(response.status).toBe(200)
> 35 |     expect(load(await response.text())('#pages-locale').text()).toBe('nl-NL')
     |                                                                 ^
  36 |   })
  37 |
  38 |   it('routes a proxy locale rewrite to a dynamic App page', async () => {

  at Object.toBe (e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts:35:65)

● i18n-app-pages-domain-routing › routes a proxy locale rewrite to a dynamic App page

expect(received).toBe(expected) // Object.is equality

Expected: "nl-NL"
Received: "en-US"

  40 |
  41 |     expect(response.status).toBe(200)
> 42 |     expect(load(await response.text())('#app-locale').text()).toBe('nl-NL')
     |                                                               ^
  43 |   })
  44 |
  45 |   it('routes an explicit locale pathname to a dynamic App page', async () => {

  at Object.toBe (e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts:42:63)

pnpm test-dev test/e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts (job)

  • i18n-app-pages-domain-routing > keeps Pages Router domain locale handling (DD)
  • i18n-app-pages-domain-routing > routes a proxy locale rewrite to a dynamic App page (DD)
Expand output

● i18n-app-pages-domain-routing › keeps Pages Router domain locale handling

expect(received).toBe(expected) // Object.is equality

Expected: "nl-NL"
Received: "en-US"

  33 |
  34 |     expect(response.status).toBe(200)
> 35 |     expect(load(await response.text())('#pages-locale').text()).toBe('nl-NL')
     |                                                                 ^
  36 |   })
  37 |
  38 |   it('routes a proxy locale rewrite to a dynamic App page', async () => {

  at Object.toBe (e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts:35:65)

● i18n-app-pages-domain-routing › routes a proxy locale rewrite to a dynamic App page

expect(received).toBe(expected) // Object.is equality

Expected: "nl-NL"
Received: "en-US"

  40 |
  41 |     expect(response.status).toBe(200)
> 42 |     expect(load(await response.text())('#app-locale').text()).toBe('nl-NL')
     |                                                               ^
  43 |   })
  44 |
  45 |   it('routes an explicit locale pathname to a dynamic App page', async () => {

  at Object.toBe (e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts:42:63)

pnpm test-dev test/e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts (job)

  • i18n-app-pages-domain-routing > keeps Pages Router domain locale handling (DD)
  • i18n-app-pages-domain-routing > routes a proxy locale rewrite to a dynamic App page (DD)
Expand output

● i18n-app-pages-domain-routing › keeps Pages Router domain locale handling

expect(received).toBe(expected) // Object.is equality

Expected: "nl-NL"
Received: "en-US"

  33 |
  34 |     expect(response.status).toBe(200)
> 35 |     expect(load(await response.text())('#pages-locale').text()).toBe('nl-NL')
     |                                                                 ^
  36 |   })
  37 |
  38 |   it('routes a proxy locale rewrite to a dynamic App page', async () => {

  at Object.toBe (e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts:35:65)

● i18n-app-pages-domain-routing › routes a proxy locale rewrite to a dynamic App page

expect(received).toBe(expected) // Object.is equality

Expected: "nl-NL"
Received: "en-US"

  40 |
  41 |     expect(response.status).toBe(200)
> 42 |     expect(load(await response.text())('#app-locale').text()).toBe('nl-NL')
     |                                                               ^
  43 |   })
  44 |
  45 |   it('routes an explicit locale pathname to a dynamic App page', async () => {

  at Object.toBe (e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts:42:63)

pnpm test-start test/e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts (job)

  • i18n-app-pages-domain-routing > keeps Pages Router domain locale handling (DD)
  • i18n-app-pages-domain-routing > routes a proxy locale rewrite to a dynamic App page (DD)
Expand output

● i18n-app-pages-domain-routing › keeps Pages Router domain locale handling

expect(received).toBe(expected) // Object.is equality

Expected: "nl-NL"
Received: "en-US"

  33 |
  34 |     expect(response.status).toBe(200)
> 35 |     expect(load(await response.text())('#pages-locale').text()).toBe('nl-NL')
     |                                                                 ^
  36 |   })
  37 |
  38 |   it('routes a proxy locale rewrite to a dynamic App page', async () => {

  at Object.toBe (e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts:35:65)

● i18n-app-pages-domain-routing › routes a proxy locale rewrite to a dynamic App page

expect(received).toBe(expected) // Object.is equality

Expected: "nl-NL"
Received: "en-US"

  40 |
  41 |     expect(response.status).toBe(200)
> 42 |     expect(load(await response.text())('#app-locale').text()).toBe('nl-NL')
     |                                                               ^
  43 |   })
  44 |
  45 |   it('routes an explicit locale pathname to a dynamic App page', async () => {

  at Object.toBe (e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts:42:63)

pnpm test-start test/production/adapter-config-i18n/adapter-config-i18n.test.ts (job)

  • adapter config with i18n routes > keeps dynamic Pages API routes in the canonical namespace (DD)
  • adapter config with i18n routes > keeps dynamic App routes in the literal namespace (DD)
Expand output

● adapter config with i18n routes › keeps dynamic Pages API routes in the canonical namespace

expect(received).toBe(expected) // Object.is equality

Expected: "/api/proxy/[[...slug]]?nxtPslug=$nxtPslug"
Received: "/api/proxy/[[...slug]]$2?nxtPslug=$nxtPslug"

  25 |     expect(apiRoute).toBeDefined()
  26 |     expect(apiRoute?.sourceRegex).not.toContain('nextLocale')
> 27 |     expect(apiRoute?.destination).toBe(
     |                                   ^
  28 |       '/api/proxy/[[...slug]]?nxtPslug=$nxtPslug'
  29 |     )
  30 |

  at Object.toBe (production/adapter-config-i18n/adapter-config-i18n.test.ts:27:35)

● adapter config with i18n routes › keeps dynamic App routes in the literal namespace

expect(received).toBe(expected) // Object.is equality

Expected: "/[lang]?nxtPlang=$nxtPlang"
Received: "/[lang]$2?nxtPlang=$nxtPlang"

  58 |     expect(appPageRoute).toBeDefined()
  59 |     expect(appPageRoute?.sourceRegex).not.toContain('nextLocale')
> 60 |     expect(appPageRoute?.destination).toBe('/[lang]?nxtPlang=$nxtPlang')
     |                                       ^
  61 |
  62 |     expect(appRoute).toBeDefined()
  63 |     expect(appRoute?.sourceRegex).not.toContain('nextLocale')

  at Object.toBe (production/adapter-config-i18n/adapter-config-i18n.test.ts:60:39)

pnpm test-deploy test/e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts (job)

  • i18n-app-pages-domain-routing > routes an explicit locale pathname to a dynamic App page (DD)
Expand output

● i18n-app-pages-domain-routing › routes an explicit locale pathname to a dynamic App page

expect(received).toBe(expected) // Object.is equality

Expected: "en-US"
Received: "nl-NL"

  47 |
  48 |     expect(response.status).toBe(200)
> 49 |     expect(load(await response.text())('#app-locale').text()).toBe('en-US')
     |                                                               ^
  50 |   })
  51 |
  52 |   it('matches App catch-all params against the locale-prefixed pathname', async () => {

  at Object.toBe (e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts:49:63)

pnpm test-deploy-experimental-turbo test/e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts (turbopack) (Experimental) (job)

  • i18n-app-pages-domain-routing > routes an explicit locale pathname to a dynamic App page (DD)
Expand output

● i18n-app-pages-domain-routing › routes an explicit locale pathname to a dynamic App page

expect(received).toBe(expected) // Object.is equality

Expected: "en-US"
Received: "nl-NL"

  47 |
  48 |     expect(response.status).toBe(200)
> 49 |     expect(load(await response.text())('#app-locale').text()).toBe('en-US')
     |                                                               ^
  50 |   })
  51 |
  52 |   it('matches App catch-all params against the locale-prefixed pathname', async () => {

  at Object.toBe (e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts:49:63)

pnpm test-start test/production/adapter-config-i18n/adapter-config-i18n.test.ts (job)

  • adapter config with i18n routes > keeps dynamic Pages API routes in the canonical namespace (DD)
  • adapter config with i18n routes > keeps dynamic App routes in the literal namespace (DD)
Expand output

● adapter config with i18n routes › keeps dynamic Pages API routes in the canonical namespace

expect(received).toBe(expected) // Object.is equality

Expected: "/api/proxy/[[...slug]]?nxtPslug=$nxtPslug"
Received: "/api/proxy/[[...slug]]$2?nxtPslug=$nxtPslug"

  25 |     expect(apiRoute).toBeDefined()
  26 |     expect(apiRoute?.sourceRegex).not.toContain('nextLocale')
> 27 |     expect(apiRoute?.destination).toBe(
     |                                   ^
  28 |       '/api/proxy/[[...slug]]?nxtPslug=$nxtPslug'
  29 |     )
  30 |

  at Object.toBe (production/adapter-config-i18n/adapter-config-i18n.test.ts:27:35)

● adapter config with i18n routes › keeps dynamic App routes in the literal namespace

expect(received).toBe(expected) // Object.is equality

Expected: "/[lang]?nxtPlang=$nxtPlang"
Received: "/[lang]$2?nxtPlang=$nxtPlang"

  58 |     expect(appPageRoute).toBeDefined()
  59 |     expect(appPageRoute?.sourceRegex).not.toContain('nextLocale')
> 60 |     expect(appPageRoute?.destination).toBe('/[lang]?nxtPlang=$nxtPlang')
     |                                       ^
  61 |
  62 |     expect(appRoute).toBeDefined()
  63 |     expect(appRoute?.sourceRegex).not.toContain('nextLocale')

  at Object.toBe (production/adapter-config-i18n/adapter-config-i18n.test.ts:60:39)

pnpm test-start test/e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts (job)

  • i18n-app-pages-domain-routing > keeps Pages Router domain locale handling (DD)
  • i18n-app-pages-domain-routing > routes a proxy locale rewrite to a dynamic App page (DD)
Expand output

● i18n-app-pages-domain-routing › keeps Pages Router domain locale handling

expect(received).toBe(expected) // Object.is equality

Expected: "nl-NL"
Received: "en-US"

  33 |
  34 |     expect(response.status).toBe(200)
> 35 |     expect(load(await response.text())('#pages-locale').text()).toBe('nl-NL')
     |                                                                 ^
  36 |   })
  37 |
  38 |   it('routes a proxy locale rewrite to a dynamic App page', async () => {

  at Object.toBe (e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts:35:65)

● i18n-app-pages-domain-routing › routes a proxy locale rewrite to a dynamic App page

expect(received).toBe(expected) // Object.is equality

Expected: "nl-NL"
Received: "en-US"

  40 |
  41 |     expect(response.status).toBe(200)
> 42 |     expect(load(await response.text())('#app-locale').text()).toBe('nl-NL')
     |                                                               ^
  43 |   })
  44 |
  45 |   it('routes an explicit locale pathname to a dynamic App page', async () => {

  at Object.toBe (e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts:42:63)

pnpm test-dev-turbo test/e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts (turbopack) (job)

  • i18n-app-pages-domain-routing > keeps Pages Router domain locale handling (DD)
  • i18n-app-pages-domain-routing > routes a proxy locale rewrite to a dynamic App page (DD)
Expand output

● i18n-app-pages-domain-routing › keeps Pages Router domain locale handling

expect(received).toBe(expected) // Object.is equality

Expected: "nl-NL"
Received: "en-US"

  33 |
  34 |     expect(response.status).toBe(200)
> 35 |     expect(load(await response.text())('#pages-locale').text()).toBe('nl-NL')
     |                                                                 ^
  36 |   })
  37 |
  38 |   it('routes a proxy locale rewrite to a dynamic App page', async () => {

  at Object.toBe (e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts:35:65)

● i18n-app-pages-domain-routing › routes a proxy locale rewrite to a dynamic App page

expect(received).toBe(expected) // Object.is equality

Expected: "nl-NL"
Received: "en-US"

  40 |
  41 |     expect(response.status).toBe(200)
> 42 |     expect(load(await response.text())('#app-locale').text()).toBe('nl-NL')
     |                                                               ^
  43 |   })
  44 |
  45 |   it('routes an explicit locale pathname to a dynamic App page', async () => {

  at Object.toBe (e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts:42:63)

pnpm test-start-turbo test/e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts (turbopack) (job)

  • i18n-app-pages-domain-routing > keeps Pages Router domain locale handling (DD)
  • i18n-app-pages-domain-routing > routes a proxy locale rewrite to a dynamic App page (DD)
Expand output

● i18n-app-pages-domain-routing › keeps Pages Router domain locale handling

expect(received).toBe(expected) // Object.is equality

Expected: "nl-NL"
Received: "en-US"

  33 |
  34 |     expect(response.status).toBe(200)
> 35 |     expect(load(await response.text())('#pages-locale').text()).toBe('nl-NL')
     |                                                                 ^
  36 |   })
  37 |
  38 |   it('routes a proxy locale rewrite to a dynamic App page', async () => {

  at Object.toBe (e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts:35:65)

● i18n-app-pages-domain-routing › routes a proxy locale rewrite to a dynamic App page

expect(received).toBe(expected) // Object.is equality

Expected: "nl-NL"
Received: "en-US"

  40 |
  41 |     expect(response.status).toBe(200)
> 42 |     expect(load(await response.text())('#app-locale').text()).toBe('nl-NL')
     |                                                               ^
  43 |   })
  44 |
  45 |   it('routes an explicit locale pathname to a dynamic App page', async () => {

  at Object.toBe (e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts:42:63)

pnpm test-start-turbo test/production/adapter-config-i18n/adapter-config-i18n.test.ts (turbopack) (job)

  • adapter config with i18n routes > keeps dynamic Pages API routes in the canonical namespace (DD)
  • adapter config with i18n routes > keeps dynamic App routes in the literal namespace (DD)
Expand output

● adapter config with i18n routes › keeps dynamic Pages API routes in the canonical namespace

expect(received).toBe(expected) // Object.is equality

Expected: "/api/proxy/[[...slug]]?nxtPslug=$nxtPslug"
Received: "/api/proxy/[[...slug]]$2?nxtPslug=$nxtPslug"

  25 |     expect(apiRoute).toBeDefined()
  26 |     expect(apiRoute?.sourceRegex).not.toContain('nextLocale')
> 27 |     expect(apiRoute?.destination).toBe(
     |                                   ^
  28 |       '/api/proxy/[[...slug]]?nxtPslug=$nxtPslug'
  29 |     )
  30 |

  at Object.toBe (production/adapter-config-i18n/adapter-config-i18n.test.ts:27:35)

● adapter config with i18n routes › keeps dynamic App routes in the literal namespace

expect(received).toBe(expected) // Object.is equality

Expected: "/[lang]?nxtPlang=$nxtPlang"
Received: "/[lang]$2?nxtPlang=$nxtPlang"

  58 |     expect(appPageRoute).toBeDefined()
  59 |     expect(appPageRoute?.sourceRegex).not.toContain('nextLocale')
> 60 |     expect(appPageRoute?.destination).toBe('/[lang]?nxtPlang=$nxtPlang')
     |                                       ^
  61 |
  62 |     expect(appRoute).toBeDefined()
  63 |     expect(appRoute?.sourceRegex).not.toContain('nextLocale')

  at Object.toBe (production/adapter-config-i18n/adapter-config-i18n.test.ts:60:39)

@github-actions

github-actions Bot commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor

Stats from current PR

🔴 1 regression, 1 improvement

Metric Canary PR Change Trend
node_modules Size 554 MB 554 MB 🔴 +63.1 kB (+0%) ██▁▁▁
Webpack Build Time 25.704s 25.119s 🟢 585ms (-2%) ▃▂▆▁▆
📊 All Metrics
📖 Metrics Glossary

Dev Server Metrics:

  • Listen = TCP port starts accepting connections
  • First Request = HTTP server returns successful response
  • Cold = Fresh build (no cache)
  • Warm = With cached build artifacts

Build Metrics:

  • Fresh = Clean build (no .next directory)
  • Cached = With existing .next directory

Change Thresholds:

  • Time: Changes < 50ms AND < 10%, OR < 2% are insignificant
  • Size: Changes < 1KB AND < 1% are insignificant
  • All other changes are flagged to catch regressions

⚡ Dev Server

Metric Canary PR Change Trend
Cold (Listen) 761ms 813ms ✓ ▁▅█▅▅
Cold (Ready in log) 767ms 792ms ✓ ▁▄█▅▄
Cold (First Request) 1.230s 1.268s ✓ ▁▅█▅▄
Warm (Listen) 810ms 813ms ✓ ▁████
Warm (Ready in log) 781ms 797ms ✓ ▁▆▅▆▅
Warm (First Request) 1.224s 1.263s ✓ ▁▅▂▅▃
📦 Dev Server (Webpack) (Legacy)

📦 Dev Server (Webpack)

Metric Canary PR Change Trend
Cold (Listen) 815ms 863ms ✓ █▁█▁▁
Cold (Ready in log) 810ms 823ms ✓ ▅▂▅▁▅
Cold (First Request) 3.442s 3.493s ✓ ▄▁▄▁▄
Warm (Listen) 864ms 813ms ✓ ▇▅▇▁▅
Warm (Ready in log) 820ms 811ms ✓ ▁▃▅▁▅
Warm (First Request) 3.461s 3.461s ✓ ▁▂▄▂▄

⚡ Production Builds

Metric Canary PR Change Trend
Fresh Build 5.582s 5.676s ✓ ▁▇█▇█
Cached Build 2.842s 2.909s ✓ ▁▄█▃▃
📦 Production Builds (Webpack) (Legacy)

📦 Production Builds (Webpack)

Metric Canary PR Change Trend
Fresh Build 25.704s 25.119s 🟢 585ms (-2%) ▃▂▆▁▆
Cached Build 25.129s 25.146s ✓ ▂▄▆▁▄
node_modules Size 554 MB 554 MB 🔴 +63.1 kB (+0%) ██▁▁▁
📦 Bundle Sizes

Bundle Sizes

⚡ Turbopack

Client

Main Bundles
Canary PR Change
017_4moyv5m34.js gzip 117 B 117 B ✓
050icza-xjz0i.js gzip 5.73 kB N/A -
05jocc85-b73_.js gzip 341 B N/A -
0cz1d0mv5g_q7.js gzip 39.4 kB 39.4 kB ✓
0e5lgz-xcf6ln.js gzip 273 B 273 B ✓
0enhddgqs2dl-.js gzip 265 B 265 B ✓
0h34mejwa_7pd.js gzip 354 B 354 B ✓
0rci1f3or1a19.js gzip 13.3 kB N/A -
0w853zxni3ro0.js gzip 1.36 kB N/A -
19gb-1_5udxfb.js gzip 13.2 kB N/A -
1elt1qium-r2m.css gzip 115 B 115 B ✓
1j69gdzx96ry9.js gzip 5.91 kB N/A -
1m5vdnlf0deiv.js gzip 411 B 411 B ✓
1nigpy_mr0xvj.js gzip 339 B N/A -
1p0l8mn2wz5f2.js gzip 1.97 kB N/A -
1p7che5q-whyo.js gzip 117 B N/A -
1sqoyy39jpgkq.js gzip 121 B 121 B ✓
1tf1phijqlx9j.js gzip 220 B 220 B ✓
2b05_424u1zq-.js gzip 120 B N/A -
2c1v76a5-ve3u.js gzip 71.6 kB N/A -
2f1u17u5c8iny.js gzip 8.79 kB N/A -
2ixp3xn2_vz0r.js gzip 3.57 kB N/A -
2kci0vnt-49o-.js gzip 113 B 113 B ✓
2s60hgv8qgm1c.js gzip 65.6 kB N/A -
2sxgl_rcrrn_p.js gzip 399 B N/A -
2vst0p6zpfrr_.js gzip 7.53 kB N/A -
2vt62i17bhbdu.js gzip 2.35 kB N/A -
3-57trblvs1hc.js gzip 155 B N/A -
342di-crrg9kq.js gzip 119 B N/A -
36-p0_enxkb2k.js gzip 47 kB N/A -
36hfc2_979aw2.js gzip 119 B N/A -
38wfp6jjrrdfm.js gzip 131 B 131 B ✓
3dl1ilk37-30g.js gzip 117 B N/A -
3kvxagoobts7h.js gzip 117 B N/A -
3r0475panhk6m.js gzip 123 B N/A -
3s0i-weykgzrt.js gzip 125 B N/A -
3wijk2ywqgsv9.js gzip 2.52 kB N/A -
41u5s3oe2-erp.js gzip 2.29 kB N/A -
turbopack-0d..9ejy.js gzip 3.74 kB 3.74 kB ✓
02zc7sqlpqxn3.js gzip N/A 119 B -
0jwho9fkrb_t3.js gzip N/A 2.29 kB -
0longconn5mlm.js gzip N/A 123 B -
0p4l3ozbez2cz.js gzip N/A 2.35 kB -
0skdm-9rpid8g.js gzip N/A 117 B -
0wslrlyk8d3lb.js gzip N/A 117 B -
0wv-h56sz83wn.js gzip N/A 120 B -
137xup2t69ovc.js gzip N/A 1.97 kB -
1fk10s3m1oecx.js gzip N/A 400 B -
1g60xde_dv17t.js gzip N/A 8.79 kB -
1uzv47btzam64.js gzip N/A 5.73 kB -
20qhkyyj2_tla.js gzip N/A 116 B -
2imyufe18bsb7.js gzip N/A 1.36 kB -
2mr-6hunyw7uo.js gzip N/A 7.53 kB -
2nsrcvgnlgkby.js gzip N/A 3.56 kB -
2p4n3-8-xixh_.js gzip N/A 341 B -
34knadd6wu9qg.js gzip N/A 119 B -
39jq2b6t6z6up.js gzip N/A 339 B -
3a9a9tvkag_-j.js gzip N/A 2.52 kB -
3b4-wgmojlaav.js gzip N/A 71.7 kB -
3be8e3ccm9e51.js gzip N/A 5.91 kB -
3nhnanx_3z6fp.js gzip N/A 65.6 kB -
3skgwg3_u6i_e.js gzip N/A 13.2 kB -
3t121tj-h0kbm.js gzip N/A 125 B -
3tt4y5c9oeg16.js gzip N/A 47 kB -
3wjy00evyxo9u.js gzip N/A 152 B -
3y9tus7kb5su0.js gzip N/A 13.3 kB -
Total 300 kB 300 kB ⚠️ +6 B

Server

Middleware
Canary PR Change
middleware-b..fest.js gzip 1.02 kB 1.02 kB ✓
Total 1.02 kB 1.02 kB ✅ -2 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 847 B 848 B ✓
Total 847 B 848 B ⚠️ +1 B
Build Cache
Canary PR Change
00000001.sst gzip 14.5 MB 13.5 MB 🟢 1.04 MB (-7%)
00000002.sst gzip 16.4 MB 13.6 MB 🟢 2.79 MB (-17%)
00000003.sst gzip 13 MB 13.3 MB 🔴 +339 kB (+3%)
00000004.sst gzip 11.4 MB 14.8 MB 🔴 +3.46 MB (+30%)
00000005.sst gzip 2.8 MB 2.8 MB ✓
00000006.sst gzip 10.2 MB 10.2 MB 🔴 +11.2 kB (+0%)
00000007.sst gzip 59 B 59 B ✓
00000008.meta gzip 298 kB 89 B 🟢 298 kB (-100%)
00000009.meta gzip 89 B 298 kB 🔴 +298 kB (+334616%)
00000010.meta gzip 298 kB 298 kB ✓
00000011.meta gzip 298 kB 298 kB ✓
00000012.sst gzip 52.9 kB 52.7 kB ✓
00000013.sst gzip 1.8 MB 1.76 MB 🟢 42.8 kB (-2%)
00000014.sst gzip 59 B 59 B ✓
00000015.meta gzip 116 B 116 B ✓
00000016.meta gzip 328 kB 328 kB ✓
00000017.meta gzip 407 kB 406 kB ✓
00000018.sst gzip 53.8 kB 53.5 kB ✓
00000019.sst gzip 1.38 MB 1.4 MB 🔴 +22.7 kB (+2%)
00000020.sst gzip 59 B 59 B ✓
00000021.meta gzip 116 B 116 B ✓
00000022.meta gzip 328 kB 328 kB ✓
00000023.meta gzip 374 kB 374 kB ✓
00000024.sst gzip 53.9 kB 53.6 kB ✓
00000025.sst gzip 1.38 MB 1.4 MB 🔴 +22 kB (+2%)
00000026.sst gzip 59 B 59 B ✓
00000027.meta gzip 116 B 116 B ✓
00000028.meta gzip 328 kB 328 kB ✓
00000029.meta gzip 374 kB 374 kB ✓
00000030.sst gzip 53.1 kB 52.9 kB ✓
00000031.sst gzip 1.38 MB 1.4 MB 🔴 +22.1 kB (+2%)
00000032.sst gzip 59 B 59 B ✓
00000033.meta gzip 116 B 116 B ✓
00000034.meta gzip 328 kB 328 kB ✓
00000035.meta gzip 374 kB 374 kB ✓
00000036.sst gzip 53.9 kB 53.5 kB ✓
00000037.sst gzip 1.38 MB 1.4 MB 🔴 +22 kB (+2%)
00000038.sst gzip 59 B 59 B ✓
00000039.meta gzip 116 B 116 B ✓
00000040.meta gzip 328 kB 328 kB ✓
00000041.meta gzip 374 kB 374 kB ✓
CURRENT gzip 94 B 94 B ✓
LOG gzip 679 B 665 B 🟢 14 B (-2%)
Total 80.2 MB 80.2 MB ⚠️ +18.7 kB

📦 Webpack

Client

Main Bundles
Canary PR Change
3322-HASH.js gzip 66.1 kB N/A -
4191.HASH.js gzip 169 B N/A -
7920-HASH.js gzip 4.67 kB N/A -
9784-HASH.js gzip 5.63 kB N/A -
b1ad9f4c-HASH.js gzip 63.5 kB N/A -
framework-HASH.js gzip 59.7 kB 59.7 kB ✓
main-app-HASH.js gzip 253 B 255 B ✓
main-HASH.js gzip 40.2 kB 40.2 kB ✓
webpack-HASH.js gzip 1.68 kB 1.68 kB ✓
3577.HASH.js gzip N/A 168 B -
578-HASH.js gzip N/A 66.2 kB -
8590-HASH.js gzip N/A 5.61 kB -
9750-HASH.js gzip N/A 4.68 kB -
a8984546-HASH.js gzip N/A 63.5 kB -
Total 242 kB 242 kB ⚠️ +32 B
Polyfills
Canary PR Change
polyfills-HASH.js gzip 39.4 kB 39.4 kB ✓
Total 39.4 kB 39.4 kB ✓
Pages
Canary PR Change
_app-HASH.js gzip 194 B 193 B ✓
_error-HASH.js gzip 181 B 182 B ✓
css-HASH.js gzip 334 B 331 B ✓
dynamic-HASH.js gzip 1.81 kB 1.81 kB ✓
edge-ssr-HASH.js gzip 255 B 253 B ✓
head-HASH.js gzip 349 B 351 B ✓
hooks-HASH.js gzip 382 B 384 B ✓
image-HASH.js gzip 581 B 582 B ✓
index-HASH.js gzip 260 B 259 B ✓
link-HASH.js gzip 2.48 kB 2.48 kB ✓
routerDirect..HASH.js gzip 317 B 318 B ✓
script-HASH.js gzip 384 B 386 B ✓
withRouter-HASH.js gzip 316 B 315 B ✓
1afbb74e6ecf..834.css gzip 106 B 106 B ✓
Total 7.95 kB 7.96 kB ⚠️ +4 B

Server

Edge SSR
Canary PR Change
edge-ssr.js gzip 129 kB 129 kB ✓
page.js gzip 294 kB 294 kB ✓
Total 424 kB 423 kB ✅ -700 B
Middleware
Canary PR Change
middleware-b..fest.js gzip 618 B 618 B ✓
middleware-r..fest.js gzip 156 B 156 B ✓
middleware.js gzip 46.1 kB 45.6 kB 🟢 490 B (-1%)
edge-runtime..pack.js gzip 842 B 842 B ✓
Total 47.7 kB 47.2 kB ✅ -490 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 717 B 718 B ✓
Total 717 B 718 B ⚠️ +1 B
Build Cache
Canary PR Change
0.pack gzip 4.83 MB 4.82 MB 🟢 9.03 kB (0%)
index.pack gzip 123 kB 125 kB 🔴 +1.25 kB (+1%)
index.pack.old gzip 124 kB 123 kB ✓
Total 5.08 MB 5.07 MB ✅ -8.75 kB

🔄 Shared (bundler-independent)

Runtimes
Canary PR Change
app-page-exp...dev.js gzip 375 kB 375 kB ✓
app-page-exp..prod.js gzip 207 kB 207 kB ✓
app-page-tur...dev.js gzip 375 kB 375 kB ✓
app-page-tur..prod.js gzip 207 kB 207 kB ✓
app-page-tur...dev.js gzip 371 kB 371 kB ✓
app-page-tur..prod.js gzip 204 kB 204 kB ✓
app-page.run...dev.js gzip 371 kB 371 kB ✓
app-page.run..prod.js gzip 205 kB 205 kB ✓
app-route-ex...dev.js gzip 83.3 kB 83.3 kB ✓
app-route-ex..prod.js gzip 56.3 kB 56.3 kB ✓
app-route-tu...dev.js gzip 83.4 kB 83.4 kB ✓
app-route-tu..prod.js gzip 56.3 kB 56.4 kB ✓
app-route-tu...dev.js gzip 82.9 kB 82.9 kB ✓
app-route-tu..prod.js gzip 56.1 kB 56.1 kB ✓
app-route.ru...dev.js gzip 82.9 kB 82.9 kB ✓
app-route.ru..prod.js gzip 56.1 kB 56.1 kB ✓
dev-validati...dev.js gzip 134 kB 134 kB ✓
dev-validati...dev.js gzip 134 kB 134 kB ✓
dev-validati...dev.js gzip 132 kB 132 kB ✓
dev-validati...dev.js gzip 132 kB 132 kB ✓
dist_client_...dev.js gzip 324 B 324 B ✓
dist_client_...dev.js gzip 326 B 326 B ✓
dist_client_...dev.js gzip 318 B 318 B ✓
dist_client_...dev.js gzip 317 B 317 B ✓
pages-api-tu...dev.js gzip 46.5 kB 46.5 kB ✓
pages-api-tu..prod.js gzip 34.7 kB 34.7 kB ✓
pages-api.ru...dev.js gzip 46.5 kB 46.5 kB ✓
pages-api.ru..prod.js gzip 34.6 kB 34.7 kB ✓
pages-turbo....dev.js gzip 55.3 kB 55.3 kB ✓
pages-turbo...prod.js gzip 40.2 kB 40.2 kB ✓
pages.runtim...dev.js gzip 55.3 kB 55.3 kB ✓
pages.runtim..prod.js gzip 40.2 kB 40.2 kB ✓
server.runti..prod.js gzip 66.7 kB 66.7 kB ✓
use-cache-pr...dev.js gzip 72.8 kB 72.8 kB ✓
use-cache-pr...dev.js gzip 72.8 kB 72.8 kB ✓
use-cache-pr...dev.js gzip 71 kB 71 kB ✓
use-cache-pr...dev.js gzip 71 kB 71 kB ✓
Total 4.11 MB 4.11 MB ⚠️ +336 B
📝 Changed Files (25 files)

Files with changes:

  • app-page-exp..ntime.dev.js
  • app-page-exp..time.prod.js
  • app-page-tur..ntime.dev.js
  • app-page-tur..time.prod.js
  • app-page-tur..ntime.dev.js
  • app-page-tur..time.prod.js
  • app-page.runtime.dev.js
  • app-page.runtime.prod.js
  • app-route-ex..ntime.dev.js
  • app-route-ex..time.prod.js
  • app-route-tu..ntime.dev.js
  • app-route-tu..time.prod.js
  • app-route-tu..ntime.dev.js
  • app-route-tu..time.prod.js
  • app-route.runtime.dev.js
  • app-route.ru..time.prod.js
  • pages-api-tu..ntime.dev.js
  • pages-api-tu..time.prod.js
  • pages-api.runtime.dev.js
  • pages-api.ru..time.prod.js
  • ... and 5 more
View diffs
app-page-exp..ntime.dev.js

Diff too large to display

app-page-exp..time.prod.js

Diff too large to display

app-page-tur..ntime.dev.js

Diff too large to display

app-page-tur..time.prod.js

Diff too large to display

app-page-tur..ntime.dev.js

Diff too large to display

app-page-tur..time.prod.js

Diff too large to display

app-page.runtime.dev.js

Diff too large to display

app-page.runtime.prod.js

Diff too large to display

app-route-ex..ntime.dev.js

Diff too large to display

app-route-ex..time.prod.js

Diff too large to display

app-route-tu..ntime.dev.js

Diff too large to display

app-route-tu..time.prod.js

Diff too large to display

app-route-tu..ntime.dev.js

Diff too large to display

app-route-tu..time.prod.js

Diff too large to display

app-route.runtime.dev.js

Diff too large to display

app-route.ru..time.prod.js

Diff too large to display

pages-api-tu..ntime.dev.js

Diff too large to display

pages-api-tu..time.prod.js

Diff too large to display

pages-api.runtime.dev.js

Diff too large to display

pages-api.ru..time.prod.js

Diff too large to display

pages-turbo...ntime.dev.js

Diff too large to display

pages-turbo...time.prod.js

Diff too large to display

pages.runtime.dev.js

Diff too large to display

pages.runtime.prod.js

Diff too large to display

server.runtime.prod.js

Diff too large to display

📎 Tarball URL
https://vercel-packages.vercel.app/next/commits/db85d879fa589bc49ef2616f96f6eb48e5288462/next

Commit: db85d87

@timneutkens
timneutkens force-pushed the codex/fix-app-i18n-route-matching branch from a0240d9 to 2cd2f35 Compare August 18, 2026 11:17
@timneutkens
timneutkens changed the base branch from canary to codex/versioned-i18n-pages-api-routing August 18, 2026 11:17
@timneutkens
timneutkens force-pushed the codex/fix-app-i18n-route-matching branch from 3afcbdb to 71f4fac Compare August 19, 2026 16:33
@timneutkens
timneutkens force-pushed the codex/fix-app-i18n-route-matching branch from 80b454d to db85d87 Compare August 21, 2026 12:34
@timneutkens
timneutkens force-pushed the codex/fix-app-i18n-route-matching branch from db85d87 to d5379c2 Compare August 31, 2026 08:30
@timneutkens timneutkens changed the title Fix App Router locale path matching with Pages i18n Fix adapter i18n App route handling Aug 31, 2026
@timneutkens
timneutkens force-pushed the codex/fix-app-i18n-route-matching branch from d5379c2 to 6cb8ca1 Compare August 31, 2026 09:38
timneutkens added a commit that referenced this pull request Sep 16, 2026
## Summary

Fix hybrid App Router and Pages Router projects using Pages i18n so
explicit or rewritten locale segments remain available to App Router
route matching, while Pages routes continue using locale-normalized
paths.

The built-in filesystem matcher now carries each route owner through
`RouteKind`, avoids duplicate App routes being treated as Pages routes
in production, and preserves literal App path parameters through the
router server, direct route-module invocation, and static export paths.
The same ownership information is maintained by the development bundler.

Adds regression coverage for domain locales, proxy rewrites, exact and
dynamic App pages, catch-all parameters, route handlers, inferred
default locales, and Pages routes. Adapter-generated routing metadata is
intentionally handled in the follow-up layers #97366 and #97500.

Closes #86048

## Verification

- `pnpm build-all`
- `NEXT_TEST_PREFER_OFFLINE=1 pnpm test-start-webpack
test/e2e/app-dir/i18n-app-pages-domain-routing/i18n-app-pages-domain-routing.test.ts`
(9 passed)
- Prettier and ESLint checks on all changed files

<!-- NEXT_JS_LLM -->
@timneutkens
timneutkens force-pushed the codex/fix-app-i18n-route-matching branch from 6cb8ca1 to 9561de0 Compare September 16, 2026 08:40

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant