next-international
next-international copied to clipboard
Locale goes back when clicking on nextjs Link component
Describe the bug
Maybe related to #138 & #140, when I click nextjs <Link>
component, it forces to go back to the default locale.
To Reproduce
Steps to reproduce the behavior:
- Go to CodesandBox
- Change locale from
en
->fr
- Click
Link component
- Always route to default locale
en
even we pass the locale prop in the <Link />
Expected behavior Should keep the locale
About (please complete the following information):
- next-international: "^1.1.4",
- Next.js version: "^14.0.3"
Do you reproduce the issue when running locally? AFAIK this is a bug with CodeSandbox, they don't preserve cookies correctly.
@QuiiBz Thanks for the response! Yes it also does the same behavior in my local environment. But I also noticed that this problem is related to the cookies.
The thing is my current middleware.ts
has some rewrite condition like below
import { NextRequest } from "next/server"
import { createI18nMiddleware } from "next-international/middleware"
export const I18nMiddleware = createI18nMiddleware({
locales: ["en", "jp"],
defaultLocale: "en",
urlMappingStrategy: "rewriteDefault",
})
export default function middleware(request: NextRequest) {
const url = request.nextUrl.clone()
const pathname = url.pathname
// Check if the URL does not contain any of the specified locales
// It basically works but did not change the nextjs cookie locale info so when we use `Link` component, it behaves strange.
// if (!pathname.startsWith("/en/") && !pathname.startsWith("/jp/")) {
// url.pathname = `/en${pathname}`
// console.log("Redirecting to: ", url.toString())
// return NextResponse.rewrite(url)
// }
return I18nMiddleware(request)
}
export const config = {
matcher: ["/((?!api|static|.*\\..*|_next|favicon.ico|robots.txt).*)"],
}
The reason why I added if (!pathname.startsWith("/en/") && !pathname.startsWith("/jp/"))
is when we try to dynamically access the path without specifying the default locale (in this case, dynamically typing & changing the URL path from localhost:3000/jp/admin/
to localhost:3000/admin/
), nextjs stays the locale as is since the cookie is still the same so I can't basically change the locale from the URL bar.
So basically I want to know if there is a way to handle user can type and dynamically change the locale & route to the correct path from URL bar without above if statement in the middleware.ts?
Ok so finally I could manage the cookies correctly via middlware.ts
like so.
import { NextRequest, NextResponse } from "next/server"
import { createI18nMiddleware } from "next-international/middleware"
export const I18nMiddleware = createI18nMiddleware({
locales: ["en", "jp"],
defaultLocale: "en",
urlMappingStrategy: "rewriteDefault",
})
export default function middleware(request: NextRequest) {
const url = request.nextUrl.clone()
const pathname = url.pathname
const currentLocale = pathname.startsWith("/jp/") ? "jp" : "en"
const cookieLocale = request.cookies.get("Next-Locale")?.value || "en"
// Check if the current locale does not match the cookie locale, then update the cookie and rewrite the URL
if (currentLocale !== cookieLocale && cookieLocale !== undefined) {
// Update the cookie to match the current locale
const response = NextResponse.next()
response.cookies.set("Next-Locale", currentLocale, { path: "/" })
// Rewrite to include the default locale if not already present
if (!pathname.startsWith(`/${currentLocale}/`)) {
url.pathname = `/${currentLocale}${pathname}`
const response = NextResponse.rewrite(url)
response.cookies.set("Next-Locale", currentLocale, { path: "/" })
return response
}
// Special case: If URL explicitly starts with '/en/' (bascially call from /jp/ route to /en/),
// remove it and call I18nMiddleware
if (pathname.startsWith("/en/")) {
url.pathname = pathname.replace("/en", "")
NextResponse.rewrite(url)
return I18nMiddleware(request)
}
return response
}
return I18nMiddleware(request)
}
export const config = {
matcher: ["/((?!api|static|.*\\..*|_next|favicon.ico|robots.txt).*)"],
}
With this, the cookies should be always align with the current locale. But I found that I still have to explicitly tell nextjs Link component to include locale prefix in the href, otherwise it always navigates me to the pure href link even we include the locale props like below.
For example we are in: http://localhost:3000/jp/admin/settings/
export function PricingTable() {
const locale = useCurrentLocale()
console.log("locale : " + locale) < the out put is "locale: jp"
.....some code.....
<Link href={"/admin/settings/billing/"} locale={locale}>Click me</Link>
After click the Link component, it navigates me to the admin/settings/billing/
and changed the locale to en
...
And of course if we specify the locale prefix like, it works correctly and keeps the cookie fine as well.
<Link href={`/${locale}` + "/admin/settings/billing/} locale={locale}>
Am I missing something?
I'm not able to reproduce the issue using the example in the repo: https://github.com/QuiiBz/next-international/tree/main/examples/next-app
The steps I did:
- Clear cookies
- Navigate to
/
, automatically redirected to/en
, cookie set toen
- Click on
fr
, redirected to/fr
, cookie updated tofr
- Navigate to other pages, the route segment is still
fr
, cookie is stillfr
Does this work on your side, and if no could share the steps to reproduce in this example above? CodeSandbox has issues with cookies so that's why I'd like to reproduce outside of it.
Hello @QuiiBz
I experiment an issue which could be related to this one.
To reproduce:
- Open a new private navigation window
- Go to https://dashboard-rtm.vercel.app/
- Click on the language switcher, then switch to English/French (depending of your current locale on the website first-visit)
- Click on the "Login" button of the navbar
- Login via Discord (I don't keep track of anything)
- The website locale is out-of-sync
Source code: https://github.com/Tirraa/dashboard_rtm
Maybe this is due to my middlewares chain?
I think the onClick={() => signIn('discord', { callbackUrl: ROUTES_ROOTS.DASHBOARD })}
on the Login button, is redirecting to Discord OAuth "Before" something in next-international which saves the user locale choice...
Btw: I also cheat one rewrite in my middleware, without having any issue about it.