diff --git a/packages/next/build/webpack/loaders/next-serverless-loader.ts b/packages/next/build/webpack/loaders/next-serverless-loader.ts index 39bc53e13dcb..6d669310f7fa 100644 --- a/packages/next/build/webpack/loaders/next-serverless-loader.ts +++ b/packages/next/build/webpack/loaders/next-serverless-loader.ts @@ -241,10 +241,10 @@ const nextServerlessLoader: loader.Loader = function () { } const denormalizedPagePath = denormalizePagePath(parsedUrl.pathname || '/') - const detectedDefaultLocale = !detectedLocale || detectedLocale === defaultLocale + const detectedDefaultLocale = !detectedLocale || detectedLocale.toLowerCase() === defaultLocale.toLowerCase() const shouldStripDefaultLocale = detectedDefaultLocale && - denormalizedPagePath === \`/\${defaultLocale}\` + denormalizedPagePath.toLowerCase() === \`/\${defaultLocale.toLowerCase()}\` const shouldAddLocalePrefix = !detectedDefaultLocale && denormalizedPagePath === '/' diff --git a/packages/next/next-server/lib/i18n/detect-locale-cookie.ts b/packages/next/next-server/lib/i18n/detect-locale-cookie.ts index 735862651901..09c8341fa0a9 100644 --- a/packages/next/next-server/lib/i18n/detect-locale-cookie.ts +++ b/packages/next/next-server/lib/i18n/detect-locale-cookie.ts @@ -1,15 +1,10 @@ import { IncomingMessage } from 'http' export function detectLocaleCookie(req: IncomingMessage, locales: string[]) { - let detectedLocale: string | undefined - if (req.headers.cookie && req.headers.cookie.includes('NEXT_LOCALE')) { const { NEXT_LOCALE } = (req as any).cookies - - if (locales.some((locale: string) => NEXT_LOCALE === locale)) { - detectedLocale = NEXT_LOCALE - } + return locales.find( + (locale: string) => NEXT_LOCALE.toLowerCase() === locale.toLowerCase() + ) } - - return detectedLocale } diff --git a/packages/next/next-server/lib/i18n/normalize-locale-path.ts b/packages/next/next-server/lib/i18n/normalize-locale-path.ts index a46bb4733407..60298ca20f4d 100644 --- a/packages/next/next-server/lib/i18n/normalize-locale-path.ts +++ b/packages/next/next-server/lib/i18n/normalize-locale-path.ts @@ -10,7 +10,7 @@ export function normalizeLocalePath( const pathnameParts = pathname.split('/') ;(locales || []).some((locale) => { - if (pathnameParts[1] === locale) { + if (pathnameParts[1].toLowerCase() === locale.toLowerCase()) { detectedLocale = locale pathnameParts.splice(1, 1) pathname = pathnameParts.join('/') || '/' diff --git a/packages/next/next-server/server/next-server.ts b/packages/next/next-server/server/next-server.ts index 96e429fb4b3f..1afeba5ce1f0 100644 --- a/packages/next/next-server/server/next-server.ts +++ b/packages/next/next-server/server/next-server.ts @@ -324,9 +324,11 @@ export default class Server { const denormalizedPagePath = denormalizePagePath(pathname || '/') const detectedDefaultLocale = - !detectedLocale || detectedLocale === defaultLocale + !detectedLocale || + detectedLocale.toLowerCase() === defaultLocale.toLowerCase() const shouldStripDefaultLocale = - detectedDefaultLocale && denormalizedPagePath === `/${defaultLocale}` + detectedDefaultLocale && + denormalizedPagePath.toLowerCase() === `/${defaultLocale.toLowerCase()}` const shouldAddLocalePrefix = !detectedDefaultLocale && denormalizedPagePath === '/' diff --git a/test/integration/i18n-support/test/index.test.js b/test/integration/i18n-support/test/index.test.js index 9c795b7837dd..429be47a832b 100644 --- a/test/integration/i18n-support/test/index.test.js +++ b/test/integration/i18n-support/test/index.test.js @@ -173,6 +173,15 @@ function runTests() { expect($('#router-as-path').text()).toBe('/gsp') expect($('#router-pathname').text()).toBe('/gsp') expect(JSON.parse($('#router-locales').text())).toEqual(locales) + + // make sure locale is case-insensitive + const html2 = await renderViaHTTP(appPort, `/${locale.toUpperCase()}/gsp`) + const $2 = cheerio.load(html2) + expect($2('html').attr('lang')).toBe(locale) + expect($2('#router-locale').text()).toBe(locale) + expect($2('#router-as-path').text()).toBe('/gsp') + expect($2('#router-pathname').text()).toBe('/gsp') + expect(JSON.parse($2('#router-locales').text())).toEqual(locales) } }) @@ -193,6 +202,21 @@ function runTests() { expect(parsedUrl.pathname).toBe('/') expect(parsedUrl.query).toEqual({}) + + // make sure locale is case-insensitive + const res2 = await fetchViaHTTP(appPort, '/eN-Us', undefined, { + redirect: 'manual', + headers: { + 'Accept-Language': 'en-US;q=0.9', + }, + }) + + expect(res2.status).toBe(307) + + const parsedUrl2 = url.parse(res.headers.get('location'), true) + + expect(parsedUrl2.pathname).toBe('/') + expect(parsedUrl2.query).toEqual({}) }) it('should load getStaticProps page correctly SSR (default locale no prefix)', async () => { @@ -391,8 +415,8 @@ function runTests() { it('should navigate client side for default locale with no prefix', async () => { const browser = await webdriver(appPort, '/') // make sure default locale is used in case browser isn't set to - // favor en-US by default - await browser.manage().addCookie({ name: 'NEXT_LOCALE', value: 'en-US' }) + // favor en-US by default, (we use all caps to ensure it's case-insensitive) + await browser.manage().addCookie({ name: 'NEXT_LOCALE', value: 'EN-US' }) await browser.get(browser.initUrl) const checkIndexValues = async () => {