-
-
Notifications
You must be signed in to change notification settings - Fork 185
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: Make sure cookie value stays up to date when the Next.js Router …
…Cache is used (#790) Fixes #786 If you frequently change the locale, Next.js will at some point use the Router Cache to return a previous response immediately, not making a request to the server. This has the side effect that no `set-cookie` response header is returned that will update the locale cookie. To address this, `next-intl` navigation APIs will now manually keep the cookie in sync. Note that a related issue described in #786 was addressed in Next.js 14.1, so upgrading both `next` and `next-intl` is recommended.
- Loading branch information
Showing
16 changed files
with
216 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
examples/example-app-router-playground/tests/locale-prefix-never.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import {test as it, expect} from '@playwright/test'; | ||
|
||
it.describe.configure({mode: 'parallel'}); | ||
|
||
it('clears the router cache when changing the locale', async ({page}) => { | ||
await page.goto('/'); | ||
|
||
async function expectDocumentLang(lang: string) { | ||
await expect(page.locator(`html[lang="${lang}"]`)).toBeAttached(); | ||
} | ||
|
||
await expectDocumentLang('en'); | ||
|
||
await page.getByRole('link', {name: 'Client page'}).click(); | ||
await expectDocumentLang('en'); | ||
await expect(page).toHaveURL('/client'); | ||
await expect( | ||
page.getByText('This page hydrates on the client side.') | ||
).toBeAttached(); | ||
|
||
await page.getByRole('link', {name: 'Go to home'}).click(); | ||
await expectDocumentLang('en'); | ||
await expect(page).toHaveURL('/'); | ||
|
||
await page.getByRole('link', {name: 'Switch to German'}).click(); | ||
|
||
await expectDocumentLang('de'); | ||
|
||
await page.getByRole('link', {name: 'Client-Seite'}).click(); | ||
await expectDocumentLang('de'); | ||
await expect(page).toHaveURL('/client'); | ||
await expect( | ||
page.getByText('Dise Seite wird auf der Client-Seite initialisiert.') | ||
).toBeAttached(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
packages/next-intl/src/navigation/shared/syncLocaleCookie.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { | ||
COOKIE_LOCALE_NAME, | ||
COOKIE_MAX_AGE, | ||
COOKIE_SAME_SITE | ||
} from '../../shared/constants'; | ||
|
||
/** | ||
* We have to keep the cookie value in sync as Next.js might | ||
* skip a request to the server due to its router cache. | ||
* See https://github.com/amannn/next-intl/issues/786. | ||
*/ | ||
export default function syncLocaleCookie( | ||
pathname: string | null, | ||
locale: string, | ||
nextLocale?: string | ||
) { | ||
const isSwitchingLocale = nextLocale !== locale; | ||
|
||
if ( | ||
!isSwitchingLocale || | ||
// Theoretical case, we always have a pathname in a real app, | ||
// only not when running e.g. in a simulated test environment | ||
!pathname | ||
) { | ||
return; | ||
} | ||
|
||
const basePath = window.location.pathname.replace(pathname, ''); | ||
const hasBasePath = basePath !== ''; | ||
const path = hasBasePath ? basePath : '/'; | ||
|
||
// Note that writing to `document.cookie` doesn't overwrite all | ||
// cookies, but only the ones referenced via the name here. | ||
document.cookie = `${COOKIE_LOCALE_NAME}=${nextLocale}; path=${path}; max-age=${COOKIE_MAX_AGE}; sameSite=${COOKIE_SAME_SITE}`; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
2 comments
on commit 977b973
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
next-intl-example-app-router – ./examples/example-app-router
next-intl-example-next-13.vercel.app
next-intl-example-app-router.vercel.app
next-intl-example-app-router-git-main-next-intl.vercel.app
next-intl-example-app-router-next-intl.vercel.app
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
next-intl-docs – ./docs
next-intl-docs-git-main-next-intl.vercel.app
next-intl-docs-next-intl.vercel.app
next-intl-docs.vercel.app
I believe this addition of
pathname
to these dependencies has potential to cause some issues.Using a router method such as
router.replace
inside of a useEffect causes a change inpathname
which in turn causesrouter.replace
reference to change. Withrouter.replace
as a dependency in theuseEffect
this has potential to create an infinite loop.Trivial example:
Will create an issue as well but thought I would add this comment where the change was introduced 👍