Skip to content

Commit 431ad4b

Browse files
committed
Update domain locale handling to re-add restricing locales to domain
1 parent 9d838df commit 431ad4b

File tree

5 files changed

+91
-54
lines changed

5 files changed

+91
-54
lines changed

packages/next/build/webpack/loaders/next-serverless-loader.ts

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -262,33 +262,30 @@ const nextServerlessLoader: loader.Loader = function () {
262262
...parsedUrl,
263263
pathname: localePathResult.pathname,
264264
})
265+
req.__nextStrippedLocale = true
265266
parsedUrl.pathname = localePathResult.pathname
267+
}
268+
269+
// If a detected locale is a domain specific locale and we aren't already
270+
// on that domain and path prefix redirect to it to prevent duplicate
271+
// content from multiple domains
272+
if (detectedDomain) {
273+
const localeToCheck = localePathResult.detectedLocale
274+
? detectedLocale
275+
: acceptPreferredLocale
266276
267-
// check if the locale prefix matches a domain's defaultLocale
268-
// and we're on a locale specific domain if so redirect to that domain
269-
// if (detectedDomain) {
270-
// const matchedDomain = detectDomainLocale(
271-
// i18n.domains,
272-
// undefined,
273-
// detectedLocale
274-
// )
275-
276-
// if (matchedDomain) {
277-
// localeDomainRedirect = \`http\${
278-
// matchedDomain.http ? '' : 's'
279-
// }://\${matchedDomain.domain}\`
280-
// }
281-
// }
282-
} else if (detectedDomain) {
283277
const matchedDomain = detectDomainLocale(
284278
i18n.domains,
285279
undefined,
286-
acceptPreferredLocale
280+
localeToCheck
287281
)
288282
289283
if (matchedDomain && matchedDomain.domain !== detectedDomain.domain) {
290284
localeDomainRedirect = \`http\${matchedDomain.http ? '' : 's'}://\${
291285
matchedDomain.domain
286+
}/\${localeToCheck === matchedDomain.defaultLocale
287+
? ''
288+
: localeToCheck
292289
}\`
293290
}
294291
}

packages/next/next-server/lib/i18n/detect-domain-locale.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export function detectDomainLocale(
33
| Array<{
44
http?: boolean
55
domain: string
6+
locales?: string[]
67
defaultLocale: string
78
}>
89
| undefined,
@@ -13,17 +14,23 @@ export function detectDomainLocale(
1314
| {
1415
http?: boolean
1516
domain: string
17+
locales?: string[]
1618
defaultLocale: string
1719
}
1820
| undefined
1921

2022
if (domainItems) {
23+
if (detectedLocale) {
24+
detectedLocale = detectedLocale.toLowerCase()
25+
}
26+
2127
for (const item of domainItems) {
2228
// remove port if present
2329
const domainHostname = item.domain?.split(':')[0].toLowerCase()
2430
if (
2531
hostname === domainHostname ||
26-
detectedLocale?.toLowerCase() === item.defaultLocale.toLowerCase()
32+
detectedLocale === item.defaultLocale.toLowerCase() ||
33+
item.locales?.some((locale) => locale.toLowerCase() === detectedLocale)
2734
) {
2835
domainItem = item
2936
break

packages/next/next-server/server/next-server.ts

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -342,32 +342,27 @@ export default class Server {
342342
})
343343
;(req as any).__nextStrippedLocale = true
344344
parsedUrl.pathname = localePathResult.pathname
345+
}
346+
347+
// If a detected locale is a domain specific locale and we aren't already
348+
// on that domain and path prefix redirect to it to prevent duplicate
349+
// content from multiple domains
350+
if (detectedDomain) {
351+
const localeToCheck = localePathResult.detectedLocale
352+
? detectedLocale
353+
: acceptPreferredLocale
345354

346-
// check if the locale prefix matches a domain's defaultLocale
347-
// and we're on a locale specific domain if so redirect to that domain
348-
// if (detectedDomain) {
349-
// const matchedDomain = detectDomainLocale(
350-
// i18n.domains,
351-
// undefined,
352-
// detectedLocale
353-
// )
354-
355-
// if (matchedDomain) {
356-
// localeDomainRedirect = `http${matchedDomain.http ? '' : 's'}://${
357-
// matchedDomain?.domain
358-
// }`
359-
// }
360-
// }
361-
} else if (detectedDomain) {
362355
const matchedDomain = detectDomainLocale(
363356
i18n.domains,
364357
undefined,
365-
acceptPreferredLocale
358+
localeToCheck
366359
)
367360

368361
if (matchedDomain && matchedDomain.domain !== detectedDomain.domain) {
369362
localeDomainRedirect = `http${matchedDomain.http ? '' : 's'}://${
370363
matchedDomain.domain
364+
}/${
365+
localeToCheck === matchedDomain.defaultLocale ? '' : localeToCheck
371366
}`
372367
}
373368
}

test/integration/i18n-support/next.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ module.exports = {
1111
http: true,
1212
domain: 'example.be',
1313
defaultLocale: 'nl-BE',
14+
locales: ['nl', 'nl-NL', 'nl-BE'],
1415
},
1516
{
1617
http: true,
1718
domain: 'example.fr',
1819
defaultLocale: 'fr',
20+
locales: ['fr-BE'],
1921
},
2022
],
2123
},

test/integration/i18n-support/test/index.test.js

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,13 @@ function runTests(isDev) {
5252
http: true,
5353
domain: 'example.be',
5454
defaultLocale: 'nl-BE',
55+
locales: ['nl', 'nl-NL', 'nl-BE'],
5556
},
5657
{
5758
http: true,
5859
domain: 'example.fr',
5960
defaultLocale: 'fr',
61+
locales: ['fr-BE'],
6062
},
6163
],
6264
})
@@ -661,27 +663,58 @@ function runTests(isDev) {
661663
})
662664

663665
it('should handle locales with domain', async () => {
664-
const checkDomainLocales = async (domainDefault = '', domain = '') => {
665-
for (const locale of locales) {
666-
// skip other domains' default locale since we redirect these
667-
if (['fr', 'nl-BE'].includes(locale) && locale !== domainDefault) {
668-
continue
666+
const domainItems = [
667+
{
668+
// used for testing, this should not be needed in most cases
669+
// as production domains should always use https
670+
http: true,
671+
domain: 'example.be',
672+
defaultLocale: 'nl-BE',
673+
locales: ['nl', 'nl-NL', 'nl-BE'],
674+
},
675+
{
676+
http: true,
677+
domain: 'example.fr',
678+
defaultLocale: 'fr',
679+
locales: ['fr-BE'],
680+
},
681+
]
682+
const domainLocales = domainItems.reduce((prev, cur) => {
683+
return [...prev, ...cur.locales]
684+
}, [])
685+
686+
const checkDomainLocales = async (
687+
domainDefault = '',
688+
domain = '',
689+
locale = ''
690+
) => {
691+
const res = await fetchViaHTTP(
692+
appPort,
693+
`/${locale === domainDefault ? '' : locale}`,
694+
undefined,
695+
{
696+
headers: {
697+
host: domain,
698+
},
699+
redirect: 'manual',
669700
}
701+
)
702+
const expectedDomainItem = domainItems.find(
703+
(item) => item.defaultLocale === locale || item.locales.includes(locale)
704+
)
705+
const shouldRedirect = expectedDomainItem.domain !== domain
670706

671-
const res = await fetchViaHTTP(
672-
appPort,
673-
`/${locale === domainDefault ? '' : locale}`,
674-
undefined,
675-
{
676-
headers: {
677-
host: domain,
678-
},
679-
redirect: 'manual',
680-
}
681-
)
707+
expect(res.status).toBe(shouldRedirect ? 307 : 200)
682708

683-
expect(res.status).toBe(200)
709+
if (shouldRedirect) {
710+
const parsedUrl = url.parse(res.headers.get('location'), true)
684711

712+
expect(parsedUrl.pathname).toBe(
713+
`/${expectedDomainItem.defaultLocale === locale ? '' : locale}`
714+
)
715+
expect(parsedUrl.query).toEqual({})
716+
expect(parsedUrl.hostname).toBe(expectedDomainItem.domain)
717+
} else {
685718
const html = await res.text()
686719
const $ = cheerio.load(html)
687720

@@ -691,8 +724,11 @@ function runTests(isDev) {
691724
}
692725
}
693726

694-
await checkDomainLocales('nl-BE', 'example.be')
695-
await checkDomainLocales('fr', 'example.fr')
727+
for (const item of domainItems) {
728+
for (const locale of domainLocales) {
729+
await checkDomainLocales(item.defaultLocale, item.domain, locale)
730+
}
731+
}
696732
})
697733

698734
it('should generate AMP pages with all locales', async () => {

0 commit comments

Comments
 (0)