From e58132c4737fffe74d2e6ebdd724e219bc03e088 Mon Sep 17 00:00:00 2001 From: hammadxcm Date: Thu, 23 Apr 2026 18:32:16 +0500 Subject: [PATCH 1/2] fix(i18n): use correct defaultLocale when domains array is present on default host When a `domains` array is configured in i18n and the request is served from the default domain (e.g., localhost), `defaultLocale` was incorrectly set to a configured domain's value because `detectDomainLocale` matched on the detected locale rather than hostname. Remove `domainLocale?.defaultLocale` from the fallback chain in `RouteModule.prepare()` since `base-server.ts` already sets the correct value in request metadata via hostname-only matching. Fixes #90131 --- .../src/server/route-modules/route-module.ts | 4 +- test/e2e/i18n-support/shared.ts | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/packages/next/src/server/route-modules/route-module.ts b/packages/next/src/server/route-modules/route-module.ts index 1159e775f175..9fab5d280911 100644 --- a/packages/next/src/server/route-modules/route-module.ts +++ b/packages/next/src/server/route-modules/route-module.ts @@ -763,9 +763,7 @@ export abstract class RouteModule< } const defaultLocale = - getRequestMeta(req, 'defaultLocale') || - domainLocale?.defaultLocale || - i18n?.defaultLocale + getRequestMeta(req, 'defaultLocale') || i18n?.defaultLocale // Ensure parsedUrl.pathname includes locale before processing // rewrites or they won't match correctly. diff --git a/test/e2e/i18n-support/shared.ts b/test/e2e/i18n-support/shared.ts index c371ee3fd321..2b492fd07f22 100644 --- a/test/e2e/i18n-support/shared.ts +++ b/test/e2e/i18n-support/shared.ts @@ -2645,6 +2645,43 @@ export function runTests(ctx) { expect(JSON.parse($2('#router-locales').text())).toEqual(locales) }) + it('should not use domain defaultLocale when visiting domain locale on default host', async () => { + // Visit /go/another on localhost (no host header matching a configured domain) + // defaultLocale should be en-US (main config), not go (example.com domain) + const res = await fetchViaHTTP( + ctx.appPort, + `${ctx.basePath}/go/another`, + undefined, + { + redirect: 'manual', + } + ) + + expect(res.status).toBe(200) + const html = await res.text() + const $ = cheerio.load(html) + expect($('#router-locale').text()).toBe('go') + expect($('#router-default-locale').text()).toBe('en-US') + expect(JSON.parse($('#props').text()).defaultLocale).toBe('en-US') + + // Also test with the other domain locale (do) + const res2 = await fetchViaHTTP( + ctx.appPort, + `${ctx.basePath}/do/another`, + undefined, + { + redirect: 'manual', + } + ) + + expect(res2.status).toBe(200) + const html2 = await res2.text() + const $2 = cheerio.load(html2) + expect($2('#router-locale').text()).toBe('do') + expect($2('#router-default-locale').text()).toBe('en-US') + expect(JSON.parse($2('#props').text()).defaultLocale).toBe('en-US') + }) + it('should not strip locale prefix for default locale with locale domains', async () => { const res = await fetchViaHTTP( ctx.appPort, From 1dc69675c86568d4ecea89529cf9ff671116544f Mon Sep 17 00:00:00 2001 From: hammadxcm Date: Tue, 26 May 2026 23:03:13 +0500 Subject: [PATCH 2/2] test(i18n): cover domain secondary-locale match branch on default host Adds /go-BE and /do-BE cases that exercise the item.locales match branch of detectDomainLocale (distinct from the defaultLocale match), verifying the request defaultLocale stays en-US on the default host. --- test/e2e/i18n-support/shared.ts | 39 +++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/e2e/i18n-support/shared.ts b/test/e2e/i18n-support/shared.ts index 2b492fd07f22..615e71160e34 100644 --- a/test/e2e/i18n-support/shared.ts +++ b/test/e2e/i18n-support/shared.ts @@ -2682,6 +2682,45 @@ export function runTests(ctx) { expect(JSON.parse($2('#props').text()).defaultLocale).toBe('en-US') }) + it("should not use domain defaultLocale when visiting a domain's secondary locale on default host", async () => { + // `go-BE`/`do-BE` are not a domain's defaultLocale; they only appear in a + // domain's `locales` array. This exercises the `item.locales` match branch + // of detectDomainLocale (distinct from the defaultLocale match above), and + // the request's defaultLocale should still be en-US (main config). + const res = await fetchViaHTTP( + ctx.appPort, + `${ctx.basePath}/go-BE/another`, + undefined, + { + redirect: 'manual', + } + ) + + expect(res.status).toBe(200) + const html = await res.text() + const $ = cheerio.load(html) + expect($('#router-locale').text()).toBe('go-BE') + expect($('#router-default-locale').text()).toBe('en-US') + expect(JSON.parse($('#props').text()).defaultLocale).toBe('en-US') + + // Also test with the other domain's secondary locale (do-BE) + const res2 = await fetchViaHTTP( + ctx.appPort, + `${ctx.basePath}/do-BE/another`, + undefined, + { + redirect: 'manual', + } + ) + + expect(res2.status).toBe(200) + const html2 = await res2.text() + const $2 = cheerio.load(html2) + expect($2('#router-locale').text()).toBe('do-BE') + expect($2('#router-default-locale').text()).toBe('en-US') + expect(JSON.parse($2('#props').text()).defaultLocale).toBe('en-US') + }) + it('should not strip locale prefix for default locale with locale domains', async () => { const res = await fetchViaHTTP( ctx.appPort,