amannn / next-intl

🌐 Internationalization (i18n) for Next.js
https://next-intl-docs.vercel.app
MIT License
2.58k stars 236 forks source link

Prepare for async `await headers()` in Next.js 15 #1375

Closed amannn closed 3 weeks ago

amannn commented 1 month ago

Is your feature request related to a problem? Please describe.

Ref.: https://github.com/vercel/next.js/pull/68812

next-intl currently relies on reading a request header to detect the current locale when relevant APIs are called in a Server Component. Note that this by itself is a workaround, ideally the locale could be retrieved from a param (see https://github.com/vercel/next.js/discussions/58862).

The APIs next-intl offers can be put in three categories:

  1. Formatting APIs (useTranslations & useFormatter)
  2. APIs for retrieving global configuration (e.g. useLocale)
  3. Navigation APIs (e.g. Link—these are lightweight wrappers for navigation APIs from Next.js and automatically incorporate localization of pathnames like locale prefixes)

The first two categories use APIs that have signatures that are either hooks or awaitable async functions—so those should be fine to update.

In the third category, the APIs are a bit more diverse. However, most APIs can be adapted as necessary:

  1. The Link component can be turned async to use await getLocale() internally
  2. useRouter & usePathname can consume a promise via use
  3. getPathname requires an explicit locale anyway

Only one API stands out as problematic: redirect

This is a function that can be called in the following environments:

  1. During the RSC render in an async component
  2. During the RSC render in a non-async component
  3. During SSR in a Client Component
  4. During CSR in a Client Component
  5. (Calling outside of render, e.g. in an event handler, will not have any effect)

Since use(…) doesn't work in async components, we'd have to use an awaitable API for (1). However, for the other call sites, a non-awaitable API would be more ergonomic (otherwise the user would have to wrap in use)

Describe the solution you'd like

I can think of the following solutions to address this:

  1. Require a locale to be passed to redirect: redirect({locale: 'en', href: '/about'}). To retrieve the locale, it'd be the job of the user to either call async getLocale() or useLocale() depending on the calling component.
    • Pros: Flexible, also adding the capability to redirect to other locales
    • Cons: More verbose to what we had before (i.e. redirect('/about')
  2. Make redirect awaitable
    • Pros: Can keep the ergonomics and not require a locale (mostly)
    • Cons: Divergence from what the user knows from Next.js (prone to errors), can not be used in non-async components

Another implication is likely that the user can't read the locale that's passed to i18n/request.ts synchronously. We might have to replace this with something promise-based (await locale or await getLocale()).

Moving forward

As matters stand currently, it seems like (1) will be more future-proof. Also, in case use() becomes available in async Server Components in the future, we can put back a default for the current locale in a later release.

Note that the navigation APIs are currently being reworked in https://github.com/amannn/next-intl/pull/1316, so now would be a good point to adjust for the future.

amannn commented 1 month ago

The following PRs will resolve this:

  1. https://github.com/amannn/next-intl/pull/1316
  2. https://github.com/amannn/next-intl/pull/1383

More details: https://github.com/amannn/next-intl/pull/1391

tonyabracadabra commented 1 month ago

Is this resolved with the canary version of next-intl? I am still getting

 Server   In route /[locale] headers were iterated over. `headers()` should be awaited before using its value. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis
amannn commented 1 month ago

Please see here: https://github.com/amannn/next-intl/pull/1391

tonyabracadabra commented 1 month ago

Please see here: #1391

Hey thanks for the reply! I am unfortunately still having that error after making the changes

Adopting createNavigation and await requestLocale will get your app in shape for Next.js 15 to work without any warnings (see https://github.com/amannn/next-intl/issues/1375).

Will the new canary release on that PR solve the problem?

Also I am still really confused by the approach of adding unstable_setRequestLocale(locale); everywhere in server layout and pages, will this still be the case in the next js 15 compatible version of next-intl? Thank you!

amannn commented 1 month ago

@tonyabracadabra Did you upgrade to the new APIs in the canary version? (see also https://github.com/amannn/next-intl/pull/1391#issuecomment-2411017222)

I tried recently with a Next.js 15 canary version and was able to get rid of all warnings. Can you try to diagnose where exactly you're seeing a warning and what causes it?

Also I am still really confused by the approach of adding unstable_setRequestLocale(locale); everywhere in server layout and pages, will this still be the case in the next js 15 compatible version of next-intl? Thank you!

That's unfortunately a different story, see https://github.com/amannn/next-intl/issues/663

imranbarbhuiya commented 1 month ago

@amannn Have you seen https://github.com/vercel/next.js/discussions/58862#discussioncomment-10843594?

amannn commented 1 month ago

@imranbarbhuiya Yep, we had a brief exchange on X: https://x.com/jamannnnnn/status/1842179418657116244. It's essentially the same approach as unstable_setRequestLocale, but in the case of nuqs, parsing is applied on top.

@danielkoller I can't really see an issue in that code sample you've provided. Maybe the error is triggered by another component? I'll soon have the examples in this repo updated for Next.js 15, maybe that can help for reference.

amannn commented 3 weeks ago

Next.js 15 is supported now without warnings:

  1. next-intl 3.22: Adopt await requestLocale and createNavigation
  2. next-intl 3.23: Next.js 15 has been added to peer dependencies

However, please consider: https://github.com/amannn/next-intl/issues/1442. Due to this, I'm waiting for now with updating examples and the official bug repro.