From cc89b83b3fc0acb2d93d3f300f578db194712fb5 Mon Sep 17 00:00:00 2001 From: Nourman Hajar Date: Fri, 23 Feb 2024 16:18:19 +0700 Subject: [PATCH 1/3] fix: avoid infinite redirect on the second trigger of getLocaleRedirect --- .../src/shared/lib/i18n/get-locale-redirect.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/next/src/shared/lib/i18n/get-locale-redirect.ts b/packages/next/src/shared/lib/i18n/get-locale-redirect.ts index 6016b8e2a0580..102b9997e4a37 100644 --- a/packages/next/src/shared/lib/i18n/get-locale-redirect.ts +++ b/packages/next/src/shared/lib/i18n/get-locale-redirect.ts @@ -5,6 +5,7 @@ import { denormalizePagePath } from '../page-path/denormalize-page-path' import { detectDomainLocale } from './detect-domain-locale' import { formatUrl } from '../router/utils/format-url' import { getCookieParser } from '../../../server/api-utils/get-cookie-parser' +import { removePathPrefix } from '../router/utils/remove-path-prefix' interface Options { defaultLocale: string @@ -105,7 +106,21 @@ export function getLocaleRedirect({ } } - if (detectedLocale.toLowerCase() !== defaultLocale.toLowerCase()) { + let invokePath = + typeof headers?.['x-invoke-path'] === 'string' + ? headers['x-invoke-path'] + : undefined + if (nextConfig.basePath && invokePath) { + invokePath = removePathPrefix(invokePath, nextConfig.basePath) + } + + // Avoid infinite redirects when the detected user locale is same as a non-default domain locale + const isDetectedLocaleSameAsInvokePath = invokePath === `/${detectedLocale}` + + if ( + !isDetectedLocaleSameAsInvokePath && + detectedLocale.toLowerCase() !== defaultLocale.toLowerCase() + ) { return formatUrl({ ...urlParsed, pathname: `${nextConfig.basePath || ''}/${detectedLocale}${ From 6d6cec73f894cfe688f074d9a27c51d9eb16612c Mon Sep 17 00:00:00 2001 From: Nourman Hajar Date: Fri, 23 Feb 2024 16:18:42 +0700 Subject: [PATCH 2/3] test: add tests for preferred locale detection --- .../app/middleware.js | 4 ++ .../app/next.config.js | 10 +++ .../app/pages/index.js | 21 +++++++ .../app/pages/new.js | 21 +++++++ .../i18n-preferred-locale-detection.test.ts | 62 +++++++++++++++++++ 5 files changed, 118 insertions(+) create mode 100644 test/e2e/i18n-preferred-locale-detection/app/middleware.js create mode 100644 test/e2e/i18n-preferred-locale-detection/app/next.config.js create mode 100644 test/e2e/i18n-preferred-locale-detection/app/pages/index.js create mode 100644 test/e2e/i18n-preferred-locale-detection/app/pages/new.js create mode 100644 test/e2e/i18n-preferred-locale-detection/i18n-preferred-locale-detection.test.ts diff --git a/test/e2e/i18n-preferred-locale-detection/app/middleware.js b/test/e2e/i18n-preferred-locale-detection/app/middleware.js new file mode 100644 index 0000000000000..6f6759070eb1c --- /dev/null +++ b/test/e2e/i18n-preferred-locale-detection/app/middleware.js @@ -0,0 +1,4 @@ +export async function middleware() { + const noop = () => {} + noop() +} diff --git a/test/e2e/i18n-preferred-locale-detection/app/next.config.js b/test/e2e/i18n-preferred-locale-detection/app/next.config.js new file mode 100644 index 0000000000000..34fa51cf311e7 --- /dev/null +++ b/test/e2e/i18n-preferred-locale-detection/app/next.config.js @@ -0,0 +1,10 @@ +module.exports = { + i18n: { + locales: ['en', 'id'], + defaultLocale: 'en', + }, + experimental: { + clientRouterFilter: true, + clientRouterFilterRedirects: true, + }, +} diff --git a/test/e2e/i18n-preferred-locale-detection/app/pages/index.js b/test/e2e/i18n-preferred-locale-detection/app/pages/index.js new file mode 100644 index 0000000000000..5fda4389161c3 --- /dev/null +++ b/test/e2e/i18n-preferred-locale-detection/app/pages/index.js @@ -0,0 +1,21 @@ +import Link from 'next/link' + +export const getServerSideProps = async ({ locale }) => { + return { + props: { + locale, + }, + } +} + +export default function Home({ locale }) { + return ( +
+
Index
+
{locale}
+ + To new + +
+ ) +} diff --git a/test/e2e/i18n-preferred-locale-detection/app/pages/new.js b/test/e2e/i18n-preferred-locale-detection/app/pages/new.js new file mode 100644 index 0000000000000..fa58d3f8c6c1b --- /dev/null +++ b/test/e2e/i18n-preferred-locale-detection/app/pages/new.js @@ -0,0 +1,21 @@ +import Link from 'next/link' + +export const getServerSideProps = async ({ locale }) => { + return { + props: { + locale, + }, + } +} + +export default function New({ locale }) { + return ( +
+
New
+
{locale}
+ + To index (No Locale Specified) + +
+ ) +} diff --git a/test/e2e/i18n-preferred-locale-detection/i18n-preferred-locale-detection.test.ts b/test/e2e/i18n-preferred-locale-detection/i18n-preferred-locale-detection.test.ts new file mode 100644 index 0000000000000..d6099fbe3ed43 --- /dev/null +++ b/test/e2e/i18n-preferred-locale-detection/i18n-preferred-locale-detection.test.ts @@ -0,0 +1,62 @@ +import { join } from 'path' +import { FileRef, nextTestSetup } from 'e2e-utils' + +describe('i18-preferred-locale-redirect', () => { + const { next } = nextTestSetup({ + files: new FileRef(join(__dirname, './app/')), + }) + + it('should request a path prefixed with my preferred detected locale when accessing index', async () => { + const browser = await next.browser('/new', { + locale: 'id', + }) + + let requestedPreferredLocalePathCount = 0 + browser.on('request', (request: any) => { + if (new URL(request.url(), 'http://n').pathname === '/id') { + requestedPreferredLocalePathCount++ + } + }) + + const goToIndex = async () => { + await browser.get(next.url) + } + + await expect(goToIndex()).resolves.not.toThrow(/ERR_TOO_MANY_REDIRECTS/) + + await browser.waitForElementByCss('#index') + + expect(await browser.elementByCss('#index').text()).toBe('Index') + expect(await browser.elementByCss('#current-locale').text()).toBe('id') + + expect(requestedPreferredLocalePathCount).toBe(1) + }) + + it('should not request a path prefixed with my preferred detected locale when clicking link to index from a non-locale-prefixed path', async () => { + const browser = await next.browser('/new', { + locale: 'id', + }) + + await browser + .waitForElementByCss('#to-index') + .click() + .waitForElementByCss('#index') + + expect(await browser.elementByCss('#index').text()).toBe('Index') + expect(await browser.elementByCss('#current-locale').text()).toBe('en') + }) + + it('should request a path prefixed with my preferred detected locale when clicking link to index from a locale-prefixed path', async () => { + const browser = await next.browser('/id/new', { + locale: 'id', + }) + + await browser + .waitForElementByCss('#to-index') + .click() + .waitForElementByCss('#index') + + expect(await browser.elementByCss('#index').text()).toBe('Index') + expect(await browser.elementByCss('#current-locale').text()).toBe('id') + }) +}) From 4c461b2293ae8db88dc7e692053fd70cf3ba130c Mon Sep 17 00:00:00 2001 From: samcx Date: Tue, 27 Feb 2024 13:33:27 -0800 Subject: [PATCH 3/3] test: remove any in favor of proper type --- .../i18n-preferred-locale-detection.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/e2e/i18n-preferred-locale-detection/i18n-preferred-locale-detection.test.ts b/test/e2e/i18n-preferred-locale-detection/i18n-preferred-locale-detection.test.ts index d6099fbe3ed43..457968d4aba78 100644 --- a/test/e2e/i18n-preferred-locale-detection/i18n-preferred-locale-detection.test.ts +++ b/test/e2e/i18n-preferred-locale-detection/i18n-preferred-locale-detection.test.ts @@ -1,3 +1,4 @@ +import type { Request } from 'playwright' import { join } from 'path' import { FileRef, nextTestSetup } from 'e2e-utils' @@ -12,7 +13,7 @@ describe('i18-preferred-locale-redirect', () => { }) let requestedPreferredLocalePathCount = 0 - browser.on('request', (request: any) => { + browser.on('request', (request: Request) => { if (new URL(request.url(), 'http://n').pathname === '/id') { requestedPreferredLocalePathCount++ }