Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [email protected] #1391

Merged
merged 42 commits into from
Oct 21, 2024
Merged

feat: [email protected] #1391

merged 42 commits into from
Oct 21, 2024

Conversation

amannn
Copy link
Owner

@amannn amannn commented Oct 1, 2024

Update:


[email protected] is around the corner and already available as a prerelease:

npm install next-intl@canary

I'd be grateful if some users of next-intl could help to test out this version. I've got an extensive test suite, but it'd be great to get feedback on whether it works in all real-world cases. Please leave a note here if you've decided to try out the prerelease and let me know if everything works as expected!

As this is a minor release, there are no adaptions necessary to your app in order to continue working as before. However, two new APIs have been added that will supersede previously available ones that are now deprecated—see the linked PRs below for details.

Improvements

  1. feat: createNavigation #1316
  2. feat: Add async requestLocale param to getRequestConfig for Next.js 15 support #1383
  3. feat: Add localeCookie option for middleware #1414
  4. fix: When using domains, handle unknown domains more gracefully #1389
  5. feat: Deprecate defaultTranslationValues  #1411
  6. feat: Mark setRequestLocale as stable #1437

Next.js 15 compatibility

Adopting createNavigation and await requestLocale will get your app in shape for Next.js 15 to work without any warnings (see #1375).

Related issues

Fixes #444
Fixes #1335
Resolves #454
Resolves #1268
Resolves #1055
Addresses #785

amannn and others added 7 commits August 30, 2024 14:36
…ike `localhost:3000` more gracefully in middleware (#1389)

When a request from an unknown host like `localhost:3000` is made—as
it's often the case during development—, don't try to pick a better
domain when responding with redirects in the middleware. Instead, the
host is now only changed for redirects if the requested host is a known
one that is specified in `domains`.

Additionally, the port is now no longer removed automatically to
determine a domain. This allows e.g. to pick different ports locally to
test different locales.
This PR provides a new **`createNavigation`** function that supersedes
the previously available APIs:
1. `createSharedPathnamesNavigation`
2. `createLocalizedPathnamesNavigation`

The new function unifies the API for both use cases and also fixes a few
quirks in the previous APIs.

**Usage**

```tsx
import {createNavigation} from 'next-intl/navigation';
import {defineRouting} from 'next-intl/routing';
 
export const routing = defineRouting(/* ... */);
 
export const {Link, redirect, usePathname, useRouter} =
  createNavigation(routing);
```

(see the [updated navigation
docs](https://next-intl-docs-git-feat-create-navigation-next-intl.vercel.app/docs/routing/navigation))

**Improvements**
1. A single API can be used both for shared as well as localized
pathnames. This reduces the API surface and simplifies the corresponding
docs.
2. `Link` can now be composed seamlessly into another component with its
`href` prop without having to add a generic type argument.
3. `getPathname` is now available for both shared as well as localized
pathnames (fixes #785)
4. `router.push` and `redirect` now accept search params consistently
via the object form (e.g. `router.push({pathname: '/users', query:
{sortBy: 'name'})`)—regardless of if you're using shared or localized
pathnames.
5. When using `localePrefix: 'as-necessary'`, the initial render of
`Link` now uses the correct pathname immediately during SSR (fixes
[#444](#444)). Previously, a
prefix for the default locale was added during SSR and removed during
hydration. Also `redirect` now gets the final pathname right without
having to add a superfluous prefix (fixes
[#1335](#1335)). The only
exception is when you use `localePrefix: 'as-necessary'` in combination
with `domains` (see [Special case: Using `domains` with `localePrefix:
'as-needed'`](https://next-intl-docs-git-feat-create-navigation-next-intl.vercel.app/docs/routing#domains-localeprefix-asneeded))
6. `Link` is now compatible with the `asChild` prop of Radix Primitives
when rendered in RSC (see
[#1322](#1322))

**Migrating to `createNavigation`**

`createNavigation` is generally considered a drop-in replacement, but a
few changes might be necessary:
1. `createNavigation` is expected to receive your complete routing
configuration. Ideally, you define this via the
[`defineRouting`](https://next-intl-docs.vercel.app/docs/routing#define-routing)
function and pass the result to `createNavigation`.
2. If you've used `createLocalizedPathnamesNavigation` and have
[composed the `Link` with its `href`
prop](https://next-intl-docs.vercel.app/docs/routing/navigation#link-composition),
you should no longer provide the generic `Pathname` type argument (see
[updated
docs](https://next-intl-docs-git-feat-create-navigation-next-intl.vercel.app/docs/routing/navigation#link-composition)).
```diff
- ComponentProps<typeof Link<Pathname>>
+ ComponentProps<typeof Link>
```
3. If you've used
[`redirect`](https://next-intl-docs.vercel.app/docs/routing/navigation#redirect),
you now have to provide an explicit locale (even if it's just [the
current
locale](https://next-intl-docs.vercel.app/docs/usage/configuration#locale)).
This change was necessary for an upcoming change in Next.js 15 where
`headers()` turns into a promise (see
[#1375](#1375) for details).
```diff
- redirect('/about')
+ redirect({pathname: '/about', locale: 'en'})
```
4. If you've used
[`getPathname`](https://next-intl-docs.vercel.app/docs/routing/navigation#getpathname)
and have previously manually prepended a locale prefix, you should no
longer do so—`getPathname` now takes care of this depending on your
routing strategy.
```diff
- '/'+ locale + getPathname(/* ... */)
+ getPathname(/* ... */);
```
5. If you're using a combination of `localePrefix: 'as-necessary'` and
`domains` and you're using `getPathname`, you now need to provide a
`domain` argument (see [Special case: Using `domains` with
`localePrefix:
'as-needed'`](https://next-intl-docs-git-feat-create-navigation-next-intl.vercel.app/docs/routing#domains-localeprefix-asneeded))
Copy link

vercel bot commented Oct 1, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
next-intl-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 21, 2024 2:17pm
next-intl-example-app-router ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 21, 2024 2:17pm
next-intl-example-app-router-without-i18n-routing ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 21, 2024 2:17pm

…js 15 support (#1383)

Since [Next.js is switching `headers()` to be
`async`](vercel/next.js#68812), the `locale`
that is passed to `getRequestConfig` needs to be replaced by an
awaitable alternative. Note that this is only relevant for your app in
case you're using i18n routing.

## tldr;

Switch to the new API and call it a day:

```diff
export default getRequestConfig(async ({
-  locale
+  requestLocale
}) => {
+  const locale = await requestLocale;

  // ...
});
```

If your app worked well before, then this is a 1:1 switch and will get
your app in shape for Next.js 15.

## Details

The new `requestLocale` parameter also offered a chance to get in some
enhancements for edge cases that were previously harder to support.
Therefore, the following migration is generally recommended:

**Before:**

```tsx
import {notFound} from 'next/navigation';
import {getRequestConfig} from 'next-intl/server';
import {routing} from './routing';
 
export default getRequestConfig(async ({locale}) => {
  // Validate that the incoming `locale` parameter is valid
  if (!routing.locales.includes(locale as any)) notFound();
 
  return {
    // ...
  };
});
```

**After:**

```tsx filename="src/i18n/request.ts"
import {getRequestConfig} from 'next-intl/server';
import {routing} from './routing';

export default getRequestConfig(async ({requestLocale}) => {
  // This typically corresponds to the `[locale]` segment
  let locale = await requestLocale;

  // Ensure that the incoming locale is valid
  if (!locale || !routing.locales.includes(locale as any)) {
    locale = routing.defaultLocale;
  }

  return {
    locale,
    // ...
  };
});
```

The differences are:
1. `requestLocale` is a promise that needs to be awaited
2. The resolved value can be `undefined`—therefore a default should be
supplied. The default assignment allows handling cases where an error
would be thrown previously, e.g. when using APIs like `useTranslations`
on a global language selection page at `app/page.tsx`.
3. The `locale` should be returned (since you can now adjust it in the
function body).
4. We now recommend calling `notFound()` in response to an invalid
`[locale]` param in
[`app/[locale]/layout.tsx`](https://next-intl-docs-git-feat-async-request-locale-next-intl.vercel.app/docs/getting-started/app-router/with-i18n-routing#layout)
instead of in `i18n/request.ts`. This unlocks another use case, where
APIs like `useTranslations` can now be used on a global
`app/not-found.tsx` page.

See also the [updated getting started
docs](https://next-intl-docs-git-feat-async-request-locale-next-intl.vercel.app/docs/getting-started/app-router/with-i18n-routing#i18n-request).

Note that this change is non-breaking, but the synchronously available
`locale` is now considered deprecated and will be removed in a future
major version.

Contributes to #1375
Addresses #1355
The previously `unstable_setRequestLocale` is now marked as stable as
it's likely required for the foreseeable future.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment