diff --git a/packages/next/src/server/lib/i18n-provider.ts b/packages/next/src/server/lib/i18n-provider.ts index 5582113a7e5e..6a5f316a48e4 100644 --- a/packages/next/src/server/lib/i18n-provider.ts +++ b/packages/next/src/server/lib/i18n-provider.ts @@ -118,8 +118,8 @@ export class I18NProvider { // query and strip it from the pathname. if (analysis.detectedLocale) { if (analysis.detectedLocale !== detectedLocale) { - throw new Error( - `Invariant: The detected locale does not match the locale in the query. Expected to find '${detectedLocale}' in '${pathname}' but found '${analysis.detectedLocale}'}` + console.warn( + `The detected locale does not match the locale in the query. Expected to find '${detectedLocale}' in '${pathname}' but found '${analysis.detectedLocale}'}` ) } diff --git a/packages/next/src/server/next-server.ts b/packages/next/src/server/next-server.ts index 338df2ce1edd..708fdbcc75f2 100644 --- a/packages/next/src/server/next-server.ts +++ b/packages/next/src/server/next-server.ts @@ -1095,9 +1095,9 @@ export default class NextNodeServer extends BaseServer< throw new Error('Invariant: pathname is undefined') } - // This is a catch-all route, there should be no fallbacks so mark it as - // such. - addRequestMeta(req, 'bubbleNoFallback', true) + // When in minimal mode we do not bubble the fallback as the + // router-server is not present to handle the error + addRequestMeta(req, 'bubbleNoFallback', this.minimalMode ? undefined : true) // This is needed to expose render404 and nextConfig // for environments without router-server diff --git a/test/e2e/i18n-fallback-collision/i18n-fallback-collision.test.ts b/test/e2e/i18n-fallback-collision/i18n-fallback-collision.test.ts new file mode 100644 index 000000000000..5c89029aec20 --- /dev/null +++ b/test/e2e/i18n-fallback-collision/i18n-fallback-collision.test.ts @@ -0,0 +1,45 @@ +import { nextTestSetup } from 'e2e-utils' + +describe('i18n-disallow-multiple-locales', () => { + const { next } = nextTestSetup({ + files: __dirname, + }) + + it.each([ + ['/non-existent'], + ['/es/non-existent'], + ['/first/non-existent'], + ['/es/first/non-existent'], + ['/first/second/non-existent'], + ['/es/first/second/non-existent'], + ])( + 'should 404 properly for fallback: false non-prerendered %s', + async (pathname) => { + const res = await next.fetch(pathname) + expect(res.status).toBe(404) + } + ) + + it.each([ + { urlPath: '/first', page: '/[first]' }, + { urlPath: '/first/second', page: '/[first]/[second]' }, + { urlPath: '/first/second/third', page: '/[first]/[second]/[third]' }, + { + urlPath: '/first/second/third/fourth', + page: '/[first]/[second]/[third]/[fourth]', + }, + ])( + 'should render properly for fallback: false prerendered $urlPath', + async ({ urlPath, page }) => { + const res = await next.fetch(urlPath) + expect(res.status).toBe(200) + expect(await res.text()).toContain(page) + } + ) + + it('should render properly for fallback: blocking', async () => { + const res = await next.fetch('/first/second/third/another') + expect(res.status).toBe(200) + expect(await res.text()).toContain('/[first]/[second]/[third]/[fourth]') + }) +}) diff --git a/test/e2e/i18n-fallback-collision/next.config.ts b/test/e2e/i18n-fallback-collision/next.config.ts new file mode 100644 index 000000000000..d322e5a79fe5 --- /dev/null +++ b/test/e2e/i18n-fallback-collision/next.config.ts @@ -0,0 +1,13 @@ +import type { NextConfig } from 'next' + +const nextConfig: NextConfig = { + /* config options here */ + reactStrictMode: true, + + i18n: { + defaultLocale: 'en', + locales: ['en', 'es'], + }, +} + +export default nextConfig diff --git a/test/e2e/i18n-fallback-collision/pages/[first]/[second]/[third]/[fourth]/index.js b/test/e2e/i18n-fallback-collision/pages/[first]/[second]/[third]/[fourth]/index.js new file mode 100644 index 000000000000..6d4904e9b466 --- /dev/null +++ b/test/e2e/i18n-fallback-collision/pages/[first]/[second]/[third]/[fourth]/index.js @@ -0,0 +1,32 @@ +export default function Page() { + return ( + <> +
page: /[first]/[second]/[third]/[fourth]
+ > + ) +} + +export function getStaticProps({ params }) { + return { + props: { + params, + now: Date.now(), + }, + } +} + +export function getStaticPaths() { + return { + paths: [ + { + params: { + first: 'first', + second: 'second', + third: 'third', + fourth: 'fourth', + }, + }, + ], + fallback: 'blocking', + } +} diff --git a/test/e2e/i18n-fallback-collision/pages/[first]/[second]/[third]/index.js b/test/e2e/i18n-fallback-collision/pages/[first]/[second]/[third]/index.js new file mode 100644 index 000000000000..f32ce55db64b --- /dev/null +++ b/test/e2e/i18n-fallback-collision/pages/[first]/[second]/[third]/index.js @@ -0,0 +1,27 @@ +export default function Page() { + return ( + <> +page: /[first]/[second]/[third]
+ > + ) +} + +export function getStaticProps({ params }) { + return { + props: { + params, + now: Date.now(), + }, + } +} + +export function getStaticPaths() { + return { + paths: [ + { + params: { first: 'first', second: 'second', third: 'third' }, + }, + ], + fallback: false, + } +} diff --git a/test/e2e/i18n-fallback-collision/pages/[first]/[second]/index.js b/test/e2e/i18n-fallback-collision/pages/[first]/[second]/index.js new file mode 100644 index 000000000000..c8e4e5befa96 --- /dev/null +++ b/test/e2e/i18n-fallback-collision/pages/[first]/[second]/index.js @@ -0,0 +1,27 @@ +export default function Page() { + return ( + <> +page: /[first]/[second]
+ > + ) +} + +export function getStaticProps({ params }) { + return { + props: { + params, + now: Date.now(), + }, + } +} + +export function getStaticPaths() { + return { + paths: [ + { + params: { first: 'first', second: 'second' }, + }, + ], + fallback: false, + } +} diff --git a/test/e2e/i18n-fallback-collision/pages/[first]/index.js b/test/e2e/i18n-fallback-collision/pages/[first]/index.js new file mode 100644 index 000000000000..3e5fd7e387b8 --- /dev/null +++ b/test/e2e/i18n-fallback-collision/pages/[first]/index.js @@ -0,0 +1,27 @@ +export default function Page() { + return ( + <> +page: /[first]
+ > + ) +} + +export function getStaticProps({ params }) { + return { + props: { + params, + now: Date.now(), + }, + } +} + +export function getStaticPaths() { + return { + paths: [ + { + params: { first: 'first' }, + }, + ], + fallback: false, + } +} diff --git a/test/e2e/i18n-fallback-collision/pages/_app.tsx b/test/e2e/i18n-fallback-collision/pages/_app.tsx new file mode 100644 index 000000000000..93928c3da70e --- /dev/null +++ b/test/e2e/i18n-fallback-collision/pages/_app.tsx @@ -0,0 +1,5 @@ +import type { AppProps } from 'next/app' + +export default function App({ Component, pageProps }: AppProps) { + returnindex page
+ > + ) +}