next.js icon indicating copy to clipboard operation
next.js copied to clipboard

Server side locale always default in dynamic routes when a middleware is present

Open XavierLasierra opened this issue 1 year ago • 70 comments

Verify canary release

  • [X] I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 22.5.0: Thu Jun  8 22:22:20 PDT 2023; root:xnu-8796.121.3~7/RELEASE_ARM64_T6000
Binaries:
  Node: 18.8.0
  npm: 8.18.0
  Yarn: 1.22.19
  pnpm: N/A
Relevant Packages:
  next: 13.4.18-canary.0
  eslint-config-next: N/A
  react: 18.2.0
  react-dom: 18.2.0
  typescript: N/A
Next.js Config:
  output: N/A

Which area(s) of Next.js are affected? (leave empty if unsure)

Middleware / Edge (API routes, runtime), Routing (next/router, next/navigation, next/link)

Link to the code that reproduces this issue or a replay of the bug

https://github.com/XavierLasierra/next-dynamic-routes-bug

To Reproduce

  1. Clone repository, install dependencies and start the server
  2. Go to the main page with a locale other than the default (ex /en)
  3. Navigate to a dynamic route using next/link
  4. The locale on the server side will be always the default one only when doing client navigation

Describe the Bug

When doing client side navigation to a dynamic route having a middleware.js file, the locale will always be the default one.

export const getServerSideProps = async ({ locale }) => {
  return {
    props: {
      locale, // Always default with client side navigation
    },
  };
};
  • This is not happening for static routes.
  • This is not happening for dynamic routes with no middleware.js file.

Expected Behavior

The locale will be consistent in dynamic routes.

Which browser are you using? (if relevant)

Chrome 115.0.5790.170

How are you deploying your application? (if relevant)

No response

XavierLasierra avatar Aug 18 '23 11:08 XavierLasierra

We are facing the same issue using a global middleware file on the project. The locale passed to the getServerSideProps is the default locale.

We are using the locale for a subsequent request inside the getServerSideProps function. As a temporary workaround, I parsed the locale from the invoke path header so we can use it for our internal request. I thought it might be handy for someone.

const isValidLanguage = (value: string | null): value is Language => {
  if (!value) return false;

  return languages.includes(value as Language);
};


export const parseLocaleWithHeaderFallback = ({
  req,
  locale,
}: {
  req: IncomingMessage;
  locale: string;
}): Language => {
  if (locale !== 'default' && isValidLanguage(locale)) return locale;

  const headerValue = req.headers['x-invoke-path'];
  const [xInvokePath] = Array.isArray(headerValue)
    ? headerValue
    : [headerValue];
  const [parsedLocale] = xInvokePath
    ? xInvokePath.substring(1).split('/')
    : [null];

  if (isValidLanguage(parsedLocale)) return parsedLocale;

  Sentry.captureException(new Error('Could not derive locale from request header'), {
    extra: { req },
  });
  return 'de';
};

next.config.js

...
  i18n: {
    locales: ["default", "en", "de"],
    defaultLocale: "default",
  },
  ...

lkappeler avatar Aug 23 '23 12:08 lkappeler

Having the same issue when middleware file present. Problem is since v13.4.13.
We have multi locale setup. Navigating pages with next/link getting 404 page not found. The page is in en locale. In middleware req.nextUrl.locale is en, but in getStaticProps the context.locale is the default locale - e.g.de.

avitslv avatar Aug 25 '23 11:08 avitslv

We are facing similar issue - https://github.com/aralroca/next-translate/issues/1131

michalstrzelecki avatar Aug 30 '23 12:08 michalstrzelecki

git bisect reveals commit 1398de9977b89c9c4717d26e213b52bc63ccbe7e to be the culprit. Not sure how helpful it is in pinpointing the actual cause since it's such a big merge commit, but it would seem that middleware URL normalization is leaking downstream and breaks locale handling for internal _next requests.

(edit: removed suggested workaround which did not avail the issue)

lauripiispanen avatar Aug 30 '23 18:08 lauripiispanen

looks like I have the same issue. I use middleware for default locale prefix and my locale inside getStaticProps is 'default' on client-side navigation. If I open a page by direct link locale is correct. I found that this issue starts from 13.4.13 version.

oleggrishechkin avatar Sep 04 '23 13:09 oleggrishechkin

Same issue here. We'll revert to 13.4.12 for the time being, let us know if we can provide any more info to help troubleshooting.

emilioschepis avatar Sep 04 '23 14:09 emilioschepis

We also ran into a similar problem when updating Next.js which broke our internationalization strategy. We´ll also keep using 13.4.12 for the moment.

lx-maxhaslehner avatar Sep 05 '23 10:09 lx-maxhaslehner

Yup, after updating from 13.4.12 to 13.4.13+, the internationalization strategy breaks. getServerSideProps gets "default" as the passed locale.

Hopefully this gets looked into soon.

SanderRuusmaa avatar Sep 07 '23 11:09 SanderRuusmaa

Wow, this bug was annoying me today. Downgrading to 13.14.12 fixed it. I use two languages, 'is' and 'en', where 'en' is the default one. If I open /is/ then the page opens in Icelandic. If I open it dynamically with Link, then it uses the default locale. Refreshing the page makes it work. I wonder if this is also a bug on prod?

raRaRa avatar Sep 09 '23 12:09 raRaRa

In my case I have a middleware with a matcher of specific URLs. The middleware is not even run for the pages where this issue happens. But removing the middleware fixes it.

raRaRa avatar Sep 09 '23 12:09 raRaRa

I wonder if this PR fixes it: https://github.com/vercel/next.js/pull/54357

raRaRa avatar Sep 09 '23 12:09 raRaRa

Any updates on this issue? We're finding this issue on all of our pages that use getServerSideProps.

According to nextjs docs, "default" should be added for prefixing locale. Now "default" is what is being passed to all of our link components that do no explicitly state what the locale is.

This resolved it for us: https://github.com/vercel/next.js/issues/53988#issuecomment-1680201847

vinnycodes avatar Sep 15 '23 16:09 vinnycodes

Still broken in 13.5.1

raRaRa avatar Sep 19 '23 20:09 raRaRa

Upgrading to 13.5.1 breaks the internationalization in a new way. The app gets stuck in a redirect loop when trying to navigate to the app's root ("/" or "/en" or "/et").

Seems to me that the automatic locale prefixing is still broken and 13.4.12 is still the last working version for me. Either some logic was changed and the Docs is not updated or it's just bugging out still.

//next.config.js (by the book, according to docs)
const nextConfig = {
  reactStrictMode: true,

  i18n: {
    locales: ['default', 'en', 'et'],
    defaultLocale: 'default',
  },

}

module.exports = nextConfig
//middleware.ts (by the book, according to docs)
import { NextRequest, NextResponse } from 'next/server'

const PUBLIC_FILE = /\.(.*)$/

export async function middleware(req: NextRequest) {
	if (
		req.nextUrl.pathname.startsWith('/_next') ||
		req.nextUrl.pathname.includes('/api/') ||
		PUBLIC_FILE.test(req.nextUrl.pathname)
	) {
		return
	}
        
        // automatic locale prefixing
	if (req.nextUrl.locale === 'default') {
		const locale = req.cookies.get('NEXT_LOCALE')?.value || 'en'

		return NextResponse.redirect(
			new URL(`/${locale}${req.nextUrl.pathname}${req.nextUrl.search}`, req.url)
		)
	}
}

network tab when routing to "/": image

SanderRuusmaa avatar Sep 20 '23 10:09 SanderRuusmaa

Facing the same issue. Upgraded to 13.5.2 from 13.3.1. Server always has the default locale when routing via a NextLink. Reloading the page does give the correct locale.

gijsbotje avatar Sep 21 '23 11:09 gijsbotje

It's really worrying that this has been an issue for such a long time without Vercel taking any action. This breaks so many websites that support multiple locales + have middleware file present (very common as well).

raRaRa avatar Sep 21 '23 13:09 raRaRa

We have the same issue as well. Downgrading to 13.3.1 for now.

danielkoller avatar Sep 21 '23 14:09 danielkoller

same issue here...

jeremymarc avatar Sep 22 '23 16:09 jeremymarc

Same issue here, upgrading > 13.4.19 breaks the app with the 307 redirect loop.

Zerebokep avatar Sep 26 '23 15:09 Zerebokep

Same issue. I couldn't fathom this to be a bug, since it seems to basic and so easily (regression-)testable at Vercel's end.

So I started this discussion. No responses yet, at the time of writing, unfortunately.

I spent another 2 hours stepping through internal Next.js code, and I couldn't find any place where the locale definitely gets reset to default. Even knowing the middleware mechanism is the culprit, it still didn't help to figure out how, where, and why this is happening.

I'm also very interested to how this could've slipped through QA at Vercel. Since i18n seems like a pretty important feature, I would assume it goes through some sort of testing. Maybe not good enough then.

thany avatar Sep 28 '23 10:09 thany

@thany I would also assume such a feature has the proper tests in place. It might be because the app router doesn't provide out of the box i18n routing that this slipped through. I believe the app router is the main focus, which is quite clear as no one from vercel or the next team has replied about this issue yet.

gijsbotje avatar Sep 28 '23 11:09 gijsbotje

Still an issue in 13.5.4 - reverting to 13.4.12 works.

raRaRa avatar Oct 05 '23 12:10 raRaRa

Having the same issue when middleware file present. Problem is since v13.4.13. We have multi locale setup. Navigating pages with next/link getting 404 page not found. The page is in en locale. In middleware req.nextUrl.locale is en, but in getStaticProps the context.locale is the default locale - e.g.de.

We updated project to use next v13.5.4, and now both in middleware and page getStaticPaths getting the correct locale values, not always the default locale.

avitslv avatar Oct 10 '23 12:10 avitslv

The same issue of infinite redirects occurred, so I downgraded from version v13.5.4-> v13.4.12.

Is there a solution?

rhkdgns95 avatar Oct 11 '23 09:10 rhkdgns95

The same issue of infinite redirects occurred, so I downgraded from version v13.5.4-> v13.4.12.

Is there a solution?

Unfortunately not, unless you remove the middleware file.

raRaRa avatar Oct 12 '23 09:10 raRaRa

v13.5.4 works for here as expected... Make sure you updated properly (check your lock file for the correct next version presence only), remove .next directory before running again, update your middleware file to contain the new config bit with a matcher.

odincov avatar Oct 16 '23 08:10 odincov

v13.5.4 works for here as expected... Make sure you updated properly (check your lock file for the correct next version presence only), remove .next directory before running again, update your middleware file to contain the new config bit with a matcher.

Doesn't work for me. Do you have a middleware present in your pages folder and are you using the pages folder? All client-side routing returns the default locale for me in 13.5.4. I've deleted ./.next & ./node_modules, etc. Still no luck.

raRaRa avatar Oct 16 '23 09:10 raRaRa

@raRaRa middleware has to be in the same directory as pages, yes i am using pages and the middleware file is present...

Check your paths, make sure locale is outside of params { params: { slug }, locale } etc... Good luck.

odincov avatar Oct 16 '23 10:10 odincov

That's precisely how I do it.

export async function getStaticProps({
  params,
  locale,
  previewData,
}: GetStaticPropsContext) {

Locale defaults to en.

  i18n: {
    locales: ["en", "is"],
    localeDetection: false,
    defaultLocale: "en",
  },

If I navigate to some URL with Nextjs <Link> on client-side, such as /is/test, locale becomes en in my getStaticProps, but if I refresh that page I get it as is (correct). So client-side navigation doesn't work on a locale different than the defaultLocale, but it works if I refresh the page (server-side navigation).

The middleware doesn't even run for those paths, it just needs to be present for this bug to appear. If I remove the middleware file it works. I tried console logging inside my middleware and it doesn't run for the path I tried. It only runs for certain paths which the matcher is configured to.

Please note that this only seems to happen on the development server (next dev), not production.

raRaRa avatar Oct 16 '23 10:10 raRaRa

The same issue of infinite redirects occurred, so I downgraded from version v13.5.4-> v13.4.12.

Is there a solution?

Try with localeDetection: false. In our case 13.5.5 seems to work correctly but only with that option set. Otherwise we get a redirect loop.

mkataja avatar Oct 16 '23 17:10 mkataja