From 52f459a6a6be685e0d5f5a90271c9c45f9283678 Mon Sep 17 00:00:00 2001 From: Luigi Colombi Date: Mon, 10 May 2021 14:11:19 +0200 Subject: [PATCH] I18n context initial props (#21930) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../next/next-server/lib/router/router.ts | 3 + packages/next/next-server/server/render.tsx | 3 + .../build-output/test/index.test.js | 2 +- .../i18n-support-custom-error/next.config.js | 6 + .../i18n-support-custom-error/pages/[slug].js | 44 ++++++ .../i18n-support-custom-error/pages/_error.js | 20 +++ .../i18n-support-custom-error/pages/index.js | 3 + .../test/index.test.js | 131 ++++++++++++++++++ 8 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 test/integration/i18n-support-custom-error/next.config.js create mode 100644 test/integration/i18n-support-custom-error/pages/[slug].js create mode 100644 test/integration/i18n-support-custom-error/pages/_error.js create mode 100644 test/integration/i18n-support-custom-error/pages/index.js create mode 100644 test/integration/i18n-support-custom-error/test/index.test.js diff --git a/packages/next/next-server/lib/router/router.ts b/packages/next/next-server/lib/router/router.ts index 024d601214eff..de1a91b942c72 100644 --- a/packages/next/next-server/lib/router/router.ts +++ b/packages/next/next-server/lib/router/router.ts @@ -1378,6 +1378,9 @@ export default class Router implements BaseRouter { pathname, query, asPath: as, + locale: this.locale, + locales: this.locales, + defaultLocale: this.defaultLocale, } as any ) ) diff --git a/packages/next/next-server/server/render.tsx b/packages/next/next-server/server/render.tsx index 6f63cd520dabe..b00e19fb162d4 100644 --- a/packages/next/next-server/server/render.tsx +++ b/packages/next/next-server/server/render.tsx @@ -594,6 +594,9 @@ export async function renderToHTML( pathname, query, asPath, + locale: renderOpts.locale, + locales: renderOpts.locales, + defaultLocale: renderOpts.defaultLocale, AppTree: (props: any) => { return ( diff --git a/test/integration/build-output/test/index.test.js b/test/integration/build-output/test/index.test.js index f02640cf54f72..55e800f4c31a5 100644 --- a/test/integration/build-output/test/index.test.js +++ b/test/integration/build-output/test/index.test.js @@ -99,7 +99,7 @@ describe('Build Output', () => { expect(indexSize.endsWith('B')).toBe(true) // should be no bigger than 64.8 kb - expect(parseFloat(indexFirstLoad)).toBeCloseTo(63.5, 1) + expect(parseFloat(indexFirstLoad)).toBeCloseTo(63.6, 1) expect(indexFirstLoad.endsWith('kB')).toBe(true) expect(parseFloat(err404Size)).toBeCloseTo(3.04, 1) diff --git a/test/integration/i18n-support-custom-error/next.config.js b/test/integration/i18n-support-custom-error/next.config.js new file mode 100644 index 0000000000000..548eeb7f713ce --- /dev/null +++ b/test/integration/i18n-support-custom-error/next.config.js @@ -0,0 +1,6 @@ +module.exports = { + i18n: { + locales: ['en', 'fr', 'de', 'it'], + defaultLocale: 'en', + }, +} diff --git a/test/integration/i18n-support-custom-error/pages/[slug].js b/test/integration/i18n-support-custom-error/pages/[slug].js new file mode 100644 index 0000000000000..4cc9eb2cac788 --- /dev/null +++ b/test/integration/i18n-support-custom-error/pages/[slug].js @@ -0,0 +1,44 @@ +import { useRouter } from 'next/router' + +const SlugPage = (props) => { + const router = useRouter() + + return router.isFallback ? null : ( + <> +
{props.title}
+
{JSON.stringify(props)}
+ + ) +} + +export const getStaticProps = async ({ locale, params }) => { + if (params.slug === 'my-custom-gone-path') { + return { + notFound: true, + } + } + return { + props: { + locale, + params, + title: params.slug, + }, + } +} + +export const getStaticPaths = async ({ locales }) => { + const mySlugs = ['my-custom-path-1', 'my-custom-path-2'] + + return { + paths: locales.reduce( + (paths, locale) => [ + ...paths, + ...mySlugs.map((slug) => ({ locale, params: { slug } })), + ], + [] + ), + fallback: 'blocking', + } +} + +export default SlugPage diff --git a/test/integration/i18n-support-custom-error/pages/_error.js b/test/integration/i18n-support-custom-error/pages/_error.js new file mode 100644 index 0000000000000..b4856fb5cd0ea --- /dev/null +++ b/test/integration/i18n-support-custom-error/pages/_error.js @@ -0,0 +1,20 @@ +function CustomError(props) { + return ( + <> +
My Custom {props.statusCode} page
+
{JSON.stringify(props)}
+ + ) +} + +CustomError.getInitialProps = ({ res, err, ...context }) => { + // 410 - GONE + if (res && context.asPath === '/my-custom-gone-path') { + res.statusCode = 410 + } + + const statusCode = res ? res.statusCode : err ? err.statusCode : 404 + return { statusCode, locale: context.locale } +} + +export default CustomError diff --git a/test/integration/i18n-support-custom-error/pages/index.js b/test/integration/i18n-support-custom-error/pages/index.js new file mode 100644 index 0000000000000..cec75d9a29fe1 --- /dev/null +++ b/test/integration/i18n-support-custom-error/pages/index.js @@ -0,0 +1,3 @@ +export default function IndexPage() { + return

Hi 👋

+} diff --git a/test/integration/i18n-support-custom-error/test/index.test.js b/test/integration/i18n-support-custom-error/test/index.test.js new file mode 100644 index 0000000000000..60d64158d3f27 --- /dev/null +++ b/test/integration/i18n-support-custom-error/test/index.test.js @@ -0,0 +1,131 @@ +/* eslint-env jest */ + +import { join } from 'path' +import webdriver from 'next-webdriver' +import { + launchApp, + killApp, + findPort, + nextBuild, + nextStart, +} from 'next-test-utils' + +jest.setTimeout(1000 * 60 * 2) + +const appDir = join(__dirname, '..') +const locales = ['en', 'fr', 'de', 'it'] +let appPort +let app + +const runTests = () => { + it('should localized [slug] routes render correctly', async () => { + for (const locale of locales) { + const browser = await webdriver( + appPort, + `${locale === 'en' ? '' : `/${locale}`}/my-custom-path-1` + ) + + expect(JSON.parse(await browser.elementByCss('#props').text())).toEqual({ + locale, + params: { + slug: 'my-custom-path-1', + }, + title: 'my-custom-path-1', + }) + } + }) + + it('handle custom http status maintaining locale props in custom _error page', async () => { + for (const locale of locales) { + const browser = await webdriver( + appPort, + `${locale === 'en' ? '' : `/${locale}`}/my-custom-gone-path` + ) + + expect( + JSON.parse(await browser.elementByCss('#error-props').text()) + ).toEqual( + expect.objectContaining({ + locale, + statusCode: 410, + }) + ) + } + }) + + it('handle default http status maintaining locale props in custom _error page', async () => { + for (const locale of locales) { + const browser = await webdriver( + appPort, + `${locale === 'en' ? '' : `/${locale}`}/my-custom-gone-path/other-path` + ) + + expect( + JSON.parse(await browser.elementByCss('#error-props').text()) + ).toEqual( + expect.objectContaining({ + locale, + statusCode: 404, + }) + ) + } + }) + + it('should work also on client side routing', async () => { + for (const locale of locales) { + const browser = await webdriver( + appPort, + `${locale === 'en' ? '' : `/${locale}`}/my-custom-path-1` + ) + + expect(JSON.parse(await browser.elementByCss('#props').text())).toEqual( + expect.objectContaining({ + locale, + params: { slug: 'my-custom-path-1' }, + title: 'my-custom-path-1', + }) + ) + + await browser.eval('window.next.router.push("/my-custom-path-2")') + + expect(JSON.parse(await browser.elementByCss('#props').text())).toEqual( + expect.objectContaining({ + locale, + params: { slug: 'my-custom-path-2' }, + title: 'my-custom-path-2', + }) + ) + + await browser.eval('window.next.router.push("/my-custom-gone-path")') + + expect( + JSON.parse(await browser.elementByCss('#error-props').text()) + ).toEqual( + expect.objectContaining({ + locale, + }) + ) + } + }) +} + +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() + }) +})