vercel/next.js

[Middleware] Locale in NextResponse.rewrite(...) path ignored for dynamic routes when basepath is set and skipMiddlewareUrlNormalize is true

joslarson opened this issue · 0 comments

Link to the code that reproduces this issue

https://codesandbox.io/p/devbox/localized-slugs-y6fgq8

To Reproduce

Set the following in next.config.js to add basepath, enable path based localization, and to set skipMiddlewareUrlNormalize to true.

const nextConfig = {
  basePath: "/basepath",
  i18n: {
    defaultLocale: "en-US",
    locales: ["en-US", "es", "fr"],
  },
  skipMiddlewareUrlNormalize: true,
};

module.exports = nextConfig;

Next create middleware with a rewrite to a different locale (I've setup the middleware here to read a query param to override the locale):

const { NextResponse } = require("next/server");

const SKIP = /^\/(basepath\/)?(_next).*/;

export function middleware(request) {
  if (SKIP.test(request.nextUrl.pathname)) return NextResponse.next();

  const locale = request.nextUrl.searchParams.get("lang");
  if (!locale) return NextResponse.next();

  const url = request.nextUrl.clone();
  if (!url.pathname.startsWith(`/${locale.toLowerCase()}`)) {
    url.pathname = `/${locale.toLowerCase()}${url.pathname}`;
  }

  console.log("rewrite to:", url.href);

  return NextResponse.rewrite(url);
}

Next create a dynamic page such as pages/test/[slug].tsx, and print out the locale:

import Link from "next/link";
import { useRouter } from "next/router";

const Page = () => {
  const router = useRouter();
  return (
    <>
      {router.pathname}: {router.locale}
    </>
  );
};

export default Page;

Go to that page with the lang param set to a different locale: /basepath/test/dynamic?lang=fr, you'll see that the redirect url logs correctly as /basepath/fr/test/dynamic, but the page loads with the router reporting en-US as the local.

Try these same steps for a non dynamic route, and you'll see that it does work in that case.

Pinning to ~13.3.0 fixes the issue, but no newer version resolves the issue.

Current vs. Expected behavior

I expect dynamic routes to respect rewrites the same as non-dynamic routes, where currently in the above scenario, that is not the case.

Provide environment information

Operating System:
  Platform: linux
  Arch: x64
  Version: #1 SMP PREEMPT_DYNAMIC Sun Aug  6 20:05:33 UTC 2023
  Available memory (MB): 8198
  Available CPU cores: 4
Binaries:
  Node: 20.11.1
  npm: 10.2.4
  Yarn: 1.22.19
  pnpm: 8.15.4
Relevant Packages:
  next: 14.2.3 // Latest available version is detected (14.2.3).
  eslint-config-next: N/A
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.3.3
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Middleware, Pages Router

Which stage(s) are affected? (Select all that apply)

next dev (local), next build (local), next start (local)

Additional context

No response