From 42735d5a2a621fa33f54f15bf854fea758b92d9c Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Sat, 26 Dec 2020 20:13:42 -0600 Subject: [PATCH 1/2] Ensure index rewrite is matched with i18n correctly --- packages/next/lib/load-custom-routes.ts | 4 +- .../next/next-server/server/next-server.ts | 5 +- .../i18n-support-index-rewrite/next.config.js | 31 +++++++ .../pages/[...slug].js | 33 ++++++++ .../test/index.test.js | 84 +++++++++++++++++++ 5 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 test/integration/i18n-support-index-rewrite/next.config.js create mode 100644 test/integration/i18n-support-index-rewrite/pages/[...slug].js create mode 100644 test/integration/i18n-support-index-rewrite/test/index.test.js diff --git a/packages/next/lib/load-custom-routes.ts b/packages/next/lib/load-custom-routes.ts index 448ba62cb91f3..61b388d1cc872 100644 --- a/packages/next/lib/load-custom-routes.ts +++ b/packages/next/lib/load-custom-routes.ts @@ -407,7 +407,9 @@ function processRoutes( r.source = `/:nextInternalLocale(${config.i18n.locales .map((locale: string) => escapeStringRegexp(locale)) - .join('|')})${r.source}` + .join('|')})${ + r.source === '/' && !config.trailingSlash ? '' : r.source + }` if (r.destination && r.destination?.startsWith('/')) { r.destination = `/:nextInternalLocale${ diff --git a/packages/next/next-server/server/next-server.ts b/packages/next/next-server/server/next-server.ts index ffc3e298cddfe..8eb9b990572e3 100644 --- a/packages/next/next-server/server/next-server.ts +++ b/packages/next/next-server/server/next-server.ts @@ -1366,8 +1366,11 @@ export default class Server { ? (req as any)._nextRewroteUrl : urlPathname - resolvedUrlPathname = removePathTrailingSlash(resolvedUrlPathname) urlPathname = removePathTrailingSlash(urlPathname) + resolvedUrlPathname = normalizeLocalePath( + removePathTrailingSlash(resolvedUrlPathname), + this.nextConfig.i18n?.locales + ).pathname const stripNextDataPath = (path: string) => { if (path.includes(this.buildId)) { diff --git a/test/integration/i18n-support-index-rewrite/next.config.js b/test/integration/i18n-support-index-rewrite/next.config.js new file mode 100644 index 0000000000000..f5cb940230994 --- /dev/null +++ b/test/integration/i18n-support-index-rewrite/next.config.js @@ -0,0 +1,31 @@ +module.exports = { + i18n: { + // localeDetection: false, + locales: ['nl-NL', 'nl-BE', 'nl', 'fr-BE', 'fr', 'en'], + defaultLocale: 'en', + domains: [ + { + // used for testing, this should not be needed in most cases + // as production domains should always use https + http: true, + domain: 'example.be', + defaultLocale: 'nl-BE', + locales: ['nl', 'nl-NL', 'nl-BE'], + }, + { + http: true, + domain: 'example.fr', + defaultLocale: 'fr', + locales: ['fr-BE'], + }, + ], + }, + async rewrites() { + return [ + { + source: '/', + destination: '/company/about-us', + }, + ] + }, +} diff --git a/test/integration/i18n-support-index-rewrite/pages/[...slug].js b/test/integration/i18n-support-index-rewrite/pages/[...slug].js new file mode 100644 index 0000000000000..655c4f0471f4d --- /dev/null +++ b/test/integration/i18n-support-index-rewrite/pages/[...slug].js @@ -0,0 +1,33 @@ +import React from 'react' + +const DynamicPage = (props) => ( + <> +
DynamicPage
+
{JSON.stringify(props)}
+ +) + +export const getStaticPaths = async () => ({ + fallback: false, + paths: [ + { + params: { + slug: ['hello'], + }, + }, + { + params: { + slug: ['company', 'about-us'], + }, + }, + ], +}) + +export const getStaticProps = async ({ params }) => ({ + props: { + params, + hello: 'world', + }, +}) + +export default DynamicPage diff --git a/test/integration/i18n-support-index-rewrite/test/index.test.js b/test/integration/i18n-support-index-rewrite/test/index.test.js new file mode 100644 index 0000000000000..d76ce0e5a8fbd --- /dev/null +++ b/test/integration/i18n-support-index-rewrite/test/index.test.js @@ -0,0 +1,84 @@ +/* eslint-env jest */ + +import { join } from 'path' +import assert from 'assert' +import cheerio from 'cheerio' +import webdriver from 'next-webdriver' +import { + launchApp, + killApp, + findPort, + nextBuild, + nextStart, + renderViaHTTP, + check, +} from 'next-test-utils' + +jest.setTimeout(1000 * 60 * 2) + +const appDir = join(__dirname, '..') +let appPort +let app + +const runTests = () => { + it('should rewrite index route correctly', async () => { + const html = await renderViaHTTP(appPort, '/') + const $ = cheerio.load(html) + + expect(JSON.parse($('#props').text())).toEqual({ + params: { + slug: ['company', 'about-us'], + }, + hello: 'world', + }) + }) + + it('should handle index rewrite on client correctly', async () => { + const browser = await webdriver(appPort, '/hello') + + expect(JSON.parse(await browser.elementByCss('#props').text())).toEqual({ + params: { + slug: ['hello'], + }, + hello: 'world', + }) + await browser.eval(`(function() { + window.beforeNav = 1 + window.next.router.push('/') + })()`) + + await check(async () => { + assert.deepEqual( + JSON.parse(await browser.elementByCss('#props').text()), + { + params: { + slug: ['company', 'about-us'], + }, + hello: 'world', + } + ) + return 'success' + }, 'success') + }) +} + +describe('Custom routes i18n', () => { + describe('dev mode', () => { + beforeAll(async () => { + appPort = await findPort() + app = await launchApp(appDir, appPort) + }) + afterAll(() => killApp(app)) + runTests(true) + }) + + describe('production mode', () => { + beforeAll(async () => { + await nextBuild(appDir) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(() => killApp(app)) + runTests() + }) +}) From e0d562abf9b95ade6ac4948cd82af1a55dd4c2ac Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Mon, 28 Dec 2020 11:21:16 -0600 Subject: [PATCH 2/2] Update tests for other locales --- .../pages/[...slug].js | 29 +++++--- .../test/index.test.js | 67 +++++++++++-------- 2 files changed, 59 insertions(+), 37 deletions(-) diff --git a/test/integration/i18n-support-index-rewrite/pages/[...slug].js b/test/integration/i18n-support-index-rewrite/pages/[...slug].js index 655c4f0471f4d..efc514023ee84 100644 --- a/test/integration/i18n-support-index-rewrite/pages/[...slug].js +++ b/test/integration/i18n-support-index-rewrite/pages/[...slug].js @@ -7,24 +7,33 @@ const DynamicPage = (props) => ( ) -export const getStaticPaths = async () => ({ - fallback: false, - paths: [ - { +export const getStaticPaths = async ({ locales }) => { + const paths = [] + + for (const locale of locales) { + paths.push({ params: { slug: ['hello'], }, - }, - { + locale, + }) + paths.push({ params: { slug: ['company', 'about-us'], }, - }, - ], -}) + locale, + }) + } + + return { + fallback: false, + paths, + } +} -export const getStaticProps = async ({ params }) => ({ +export const getStaticProps = async ({ params, locale }) => ({ props: { + locale, params, hello: 'world', }, diff --git a/test/integration/i18n-support-index-rewrite/test/index.test.js b/test/integration/i18n-support-index-rewrite/test/index.test.js index d76ce0e5a8fbd..2dd434ebc4e14 100644 --- a/test/integration/i18n-support-index-rewrite/test/index.test.js +++ b/test/integration/i18n-support-index-rewrite/test/index.test.js @@ -17,48 +17,61 @@ import { jest.setTimeout(1000 * 60 * 2) const appDir = join(__dirname, '..') +const locales = ['nl-NL', 'nl-BE', 'nl', 'fr-BE', 'fr', 'en'] let appPort let app const runTests = () => { it('should rewrite index route correctly', async () => { - const html = await renderViaHTTP(appPort, '/') - const $ = cheerio.load(html) + for (const locale of locales) { + const html = await renderViaHTTP( + appPort, + `/${locale === 'en' ? '' : locale}` + ) + const $ = cheerio.load(html) - expect(JSON.parse($('#props').text())).toEqual({ - params: { - slug: ['company', 'about-us'], - }, - hello: 'world', - }) + expect(JSON.parse($('#props').text())).toEqual({ + params: { + slug: ['company', 'about-us'], + }, + locale, + hello: 'world', + }) + } }) it('should handle index rewrite on client correctly', async () => { - const browser = await webdriver(appPort, '/hello') + for (const locale of locales) { + const browser = await webdriver( + appPort, + `${locale === 'en' ? '' : `/${locale}`}/hello` + ) - expect(JSON.parse(await browser.elementByCss('#props').text())).toEqual({ - params: { - slug: ['hello'], - }, - hello: 'world', - }) - await browser.eval(`(function() { - window.beforeNav = 1 - window.next.router.push('/') - })()`) + expect(JSON.parse(await browser.elementByCss('#props').text())).toEqual({ + params: { + slug: ['hello'], + }, + locale, + hello: 'world', + }) + await browser.eval(`(function() { + window.beforeNav = 1 + window.next.router.push('/') + })()`) - await check(async () => { - assert.deepEqual( - JSON.parse(await browser.elementByCss('#props').text()), - { + await check(async () => { + const html = await browser.eval('document.documentElement.innerHTML') + const props = JSON.parse(cheerio.load(html)('#props').text()) + assert.deepEqual(props, { params: { slug: ['company', 'about-us'], }, + locale, hello: 'world', - } - ) - return 'success' - }, 'success') + }) + return 'success' + }, 'success') + } }) }