i18nexus / next-i18n-router

Next.js App Router internationalized routing and locale detection.
MIT License
257 stars 19 forks source link

[Bug] Changing Locale Without router.refresh() Causes useCurrentLocale to Return Incorrect Value #80

Closed junseinagao closed 3 months ago

junseinagao commented 4 months ago

I found that useCurrentLocale returns the wrong value when changing routes without using router.refresh() in the client. For example, this happens when using the <Link> component.

I believe that useCurrentLocale is checking cookies before usePathname, which causes this behavior.

Steps to Reproduce

I created a codesandbox example.

  1. Open the CodeSandbox and open the preview in a new tab because CodeSandbox's preview in the editor doesn't use the browser's cookies.
  2. Click the locale change links.
  3. Observe that the values of usePathname() and useCurrentLocale() are different.

https://github.com/i18nexus/next-i18n-router/assets/54606780/71605cea-4aec-42bc-aadc-aba3e607e1cd

Expected Behavior

useCurrentLocale should return the correct locale string when the route is changed by rendering caused by the client.

Actual Behavior

useCurrentLocale returns a different value than the URL path after the route is changed by rendering caused by the client.

i18nexus commented 4 months ago

Hmm, good catch. Yes, useCurrentLocale is getting the locale from the cookie. But when it's a cached page, Next.js doesn't use the middleware, so the cookie is not updated.

We used to have it so that useCurrentLocale would simply get the locale by parsing the current pathname, but since we added the noPrefix option, we changed it to read the cookie instead.

The middleware not being called for cached pages is really out of our hands. If you're dependent on the server to set the cookie, using router.refresh on language change is required. That's why we included router.refresh in our LanguageChanger component in the example projects in this repo. We should certainly add this information to the README.

If you don't call router.refresh after language change, there are other side effects besides the issue with useCurrentLocale. For example, in your video above, if you were to now click on a link like /about-us, you would receive the Japanese "about-us" page instead of the German "about-us" page. This is because your cookie is currently set to ja.

We will add to the documentation that router.refresh is required if changing language via user-clicked buttons or links.

A simple, global way to do this would probably be to have a client component in your root layout that has a useEffect that calls router.refresh any time the language changes. You would need to include a conditional in the useEffect that the previous language was not null since we don't want to refresh on initial app load.

junseinagao commented 4 months ago

@i18nexus

We will add to the documentation that router.refresh is required if changing language via user-clicked buttons or links.

That's a great idea. This information will help developers who use next-i18n-router.


I also found that useCurrentLocale works well if application never use the NEXT_LANG cookie. The serverSetCookie: 'never' setting is the key. This is a example sandbox.

How about also adding this information to the README and the comment of the useCurrentLocale method? I guess some developers would like to use next-i18n-router's client hook without router.refresh() or without concerns about the behavior of NEXT_LANG cookie.

i18nexus commented 4 months ago

Using serverSetCookie: "never" will still have the same problems. The only difference is that useCurrentLocale will work correctly since the hook pulls the locale from the pathname when no cookie available. But you will still run into issues on cached pages as I described above.

i18nexus commented 3 months ago

Added to the documentation in the FAQ section