Skip to content
3.0 preview (see announcement)
Blog
next-intl 3.0

next-intl 3.0 release candidate

Sep 28, 2023 · by Jan Amann

Almost one year ago, on Oct 25, 2022, Next.js 13 was announced (opens in a new tab) with beta support for the App Router and Server Components. Ever since then, next-intl began an exploration on what it means to provide an ideal experience for implementing i18n with the newly added capabilities.

Today, after more than 300 commits and the involvement of over 50 community members (opens in a new tab), I'm absolutely thrilled to share the first release candidate of next-intl 3.0, which is now App Router-first.

If you're still happy with the Pages Router, rest assured that next-intl is dedicated to support this paradigm for as long as Next.js does. For those who have already migrated to the App Router, this means that next-intl now takes full advantage of the new capabilities.

New features

  1. Support for React Server Components: The APIs useTranslations, useFormatter, useLocale, useNow and useTimeZone can now be used in Server Components (proposed docs (opens in a new tab)).
  2. New async APIs to handle i18n outside of components: To handle i18n in the Metadata API and Route Handlers, the APIs getTranslations, getFormatter, getNow, and getTimeZone have been added (proposed docs (opens in a new tab)).
  3. Middleware for internationalized routing: While Next.js has built-in support for this with the Pages Router, the App Router doesn't include a built-in solution anymore. next-intl now provides a drop-in solution that has you covered (proposed docs (opens in a new tab)).
  4. Internationalized navigation APIs: Similar to the middleware, this provides a drop-in solution that adds internationalization support for Next.js' navigation APIs: Link, useRouter, usePathname and redirect. These APIs allow you to handle locale prefixes behind the scenes and also provide support for localizing pathnames (e.g. /en/about vs. /de/ueber-uns, see the proposed docs (opens in a new tab)).

The latter two have already been added in minor versions, but 3.0 cleans up the API and includes many improvements and bug fixes.

Breaking changes

If you've been part of the Server Components beta and have already tried out previous releases, first of all, thank you so much! Second: Some APIs saw iterations over the beta period, please carefully review the breaking changes below, even if you're already using some of the new APIs.

Updated setup

next-intl now requires two additional setup steps when you're using the App Router:

  1. The i18n.ts module (opens in a new tab) provides configuration for Server Components
  2. next-intl/plugin (opens in a new tab) needs to be added to link your i18n.ts module to next-intl

New navigation APIs for the App Router

With v2.14 (opens in a new tab), the navigation APIs useRouter, usePathname and Link were added to next-intl that enabled you to use the APIs you're used to from Next.js while automatically considering a locale behind the scenes.

With 3.0, we're cleaning up these APIs by moving them to a shared namespace as well as introducing type-safety for the locale prop that can be passed to these APIs.

- import Link from 'next-intl/link';
- import {useRouter, usePathname} from 'next-intl/client';
- import {redirect} from 'next-intl/server';
 
+ import {createSharedPathnamesNavigation} from 'next-intl/navigation';
+
+ const locales = ['en', 'de'] as const;
+ const {Link, useRouter, usePathname, redirect} = createSharedPathnamesNavigation({locales});

Typically, you'll want to call this factory function in a central place in your app where you can easily import from (see the proposed navigation docs (opens in a new tab)).

These changes bring the existing APIs in line with the new createLocalizedPathnamesNavigation API (opens in a new tab) that allows you to localize pathnames:

navigation.ts
import {createLocalizedPathnamesNavigation, Pathnames} from 'next-intl/navigation';
 
export const locales = ['en', 'de'] as const;
 
// The `pathnames` object holds pairs of internal
// and external paths, separated by locale.
export const pathnames = {
  // If all locales use the same pathname, a
  // single external path can be provided.
  '/': '/',
  '/blog': '/blog',
 
  // If locales use different paths, you can
  // specify each external path per locale.
  '/about': {
    en: '/about',
    de: '/ueber-uns'
  }
} satisfies Pathnames<typeof locales>;
 
export const {Link, redirect, usePathname, useRouter} =
  createLocalizedPathnamesNavigation({locales, pathnames});

By using a similar API, you can upgrade from shared pathnames to localized pathnames by replacing the factory function.

Switching the middleware default of localePrefix to always

Previously, the localePrefix of the middleware (opens in a new tab) defaulted to as-needed, meaning that a locale prefix was only added for non-default locales.

This default has now been changed to always since this has two advantages:

  1. We can recommend a safer default matcher (opens in a new tab) that needs no extra treatment for pathnames with dots (e.g. /users/jane.doe)
  2. It avoids an edge case of Link (opens in a new tab) where we include a prefix for the default locale on the server side but patch this on the client side by removing the prefix (certain SEO tools might report a hint that a link points to a redirect in this case).

If you want to stay on the as-needed strategy, you can configure this option (opens in a new tab) in the middleware.

Static rendering of Server Components

With the newly introduced Server Components support comes a temporary workaround for static rendering.

If you call APIs like useTranslations in a Server Component, by default, the hook will opt the page into dynamic rendering (opens in a new tab). This is a limitation that we aim to remove in the future, but as a stopgap solution, we've added the unstable_setRequestLocale (opens in a new tab) API so that you can keep your pages fully static.

Note that if you're using next-intl exclusively in Client Components, this doesn't apply to your app and static rendering will continue to work as it did before.

Other notable changes

  1. next-intl now uses exports in package.json (opens in a new tab) to clearly define which modules are exported. This should not affect you, unless you've previously imported undocumented internals.
  2. NextIntlProvider has been removed in favor of NextIntlClientProvider (opens in a new tab)
  3. NextIntlClientProvider now needs to be imported from next-intl instead of next-intl/client.
  4. The middleware (opens in a new tab) now needs to be imported from next-intl/middleware instead of next-intl/server (deprecated since v2.14).
  5. next@^13.4 is now required for the RSC APIs. Next.js 12 is still supported for the Pages Router integration.
  6. If you're using NextIntlClientProvider outside of the App Router (e.g. with the Pages Router), you need to define the locale prop explicitly.
  7. useMessages now has a non-nullable return type for easier consumption and will throw if no messages are configured.
  8. createTranslator(…).rich now returns a ReactNode. Previously, this was somewhat confusing, since t.rich accepted and returned either React elements or strings depending on if you retrieve the fuction via useTranslations or createTranslator. Now, an explicit t.markup function has been added to generate markup strings like '<b>Hello</b>' outside of React components.
  9. useIntl has been replaced with useFormatter (opens in a new tab) (deprecated since v2.11).
  10. createIntl has been replaced with createFormatter (opens in a new tab) (deprecated since v2.11).

Upgrade now

Along with the release candidate also comes a preview of the updated docs (opens in a new tab).

We're still looking for more feedback, please try out the release candidate and reach out (opens in a new tab)!

npm install next-intl@3.0.0-rc.10

This release was truly a team effort and couldn't be nearly where it is today without the involvement of the community. Thank you so much for being part of this!