Skip to content

Commit

Permalink
feat: Invoke notFound() when no locale was attached to the request …
Browse files Browse the repository at this point in the history
…and update docs to suggest validating the locale in `i18n.ts` (amannn#742)

Users are currently struggling with errors that are caused by these two
scenarios:
1. An invalid locale was passed to `next-intl` APIs (e.g.
[amannn#736](amannn#736))
2. No locale is available during rendering (e.g.
[amannn#716](amannn#716))

**tldr:**
1. We now suggest to validate the incoming `locale` in
[`i18n.ts`](https://next-intl-docs.vercel.app/docs/usage/configuration#i18nts).
This change is recommended to all users.
2. `next-intl` will call the `notFound()` function when the middleware
didn't run on a localized request and `next-intl` APIs are used during
rendering. Previously this would throw an error, therefore this is only
relevant for you in case you've encountered [the corresponding
error](https://next-intl-docs.vercel.app/docs/routing/middleware#unable-to-find-locale).
Make sure you provide a relevant [`not-found`
page](https://next-intl-docs.vercel.app/docs/environments/error-files#not-foundjs)
that can be rendered in this case.

---

**Handling invalid locales**

Users run into this error because the locale wasn't sufficiently
validated. This is in practice quite hard because we currently educate
in the docs that this should happen in [the root
layout](https://next-intl-docs.vercel.app/docs/getting-started/app-router#applocalelayouttsx),
but due to the parallel rendering capabilities of Next.js, potentially a
page or `generateMetadata` runs first.

Therefore moving this validation to a more central place seems
necessary. Due to this, the docs will now suggest validating the locale
in `i18n.ts`. By doing this, we can catch erroneous locale arguments in
a single place before e.g. importing JSON files.

The only edge case is if an app uses no APIs of `next-intl` in Server
Components at all and therefore `i18n.ts` doesn't run. This should be a
very rare case though as even `NextIntlClientProvider` will call
`i18n.ts`. The only case to run into this is if you're using
`NextIntlClientProvider` in a Client Component and delegate all i18n
handling to Client Components too. If you have such an app, `i18n.ts`
will not be invoked and you should validate the `locale` before passing
it to `NextIntlClientProvider`.

**Handling missing locales**

This warning is probably one of the most annoying errors that users
currently run into:

```
Unable to find next-intl locale because the middleware didn't run on this request.
```

The various causes of this error are outlined in [the
docs](https://next-intl-docs.vercel.app/docs/routing/middleware#unable-to-find-locale).

Some of these cases should simply be 404s (e.g. when your middleware
matcher doesn't match `/unknown.txt`), while others require a fix in the
matcher (e.g. considering `/users/jane.doe` when using `localePrefix:
'as-necessary'`).

My assumption is that many of these errors are false positives that are
caused by the `[locale]` segment acting as a catch-all. As a result, a
500 error is encountered instead of 404s. Due to this, this PR will
downgrade the previous error to a dev-only warning and will invoke the
`notFound()` function. This should help in the majority of cases. Note
that you should define [a `not-found`
file](https://next-intl-docs.vercel.app/docs/environments/error-files#not-foundjs)
to handle this case.

I think this change is a good idea because if you're using
`unstable_setRequestLocale` and you have a misconfigured middleware
matcher, you can provide any kind of string to `next-intl` (also
`unknown.txt`) and not run into this error. Therefore it only affects
users with dynamic rendering. Validating the locale in `i18n.ts` is the
solution to either case (see above). Also in case something like
[`routeParams`](amannn#663) gets
added to Next.js, the current warning will be gone entirely—therefore
tackling it from a different direction is likely a good idea.

The false negatives of this should hopefully be rather small as we
consistently point out that you need to adapt your middleware matcher
when switching the `localePrefix` to anything other than `always`.
Dev-only warnings should help to get further information for these
requests.

---

Closes amannn#736
Closes amannn#716
Closes amannn#446
  • Loading branch information
amannn authored Dec 21, 2023
1 parent f4161b6 commit c10e2d1
Showing 1 changed file with 7 additions and 3 deletions.
10 changes: 7 additions & 3 deletions src/server/react-server/RequestLocale.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {headers} from 'next/headers';
import {notFound} from 'next/navigation';
import {cache} from 'react';
import {HEADER_LOCALE_NAME} from '../../shared/constants';

Expand All @@ -22,9 +23,12 @@ function getLocaleFromHeaderImpl() {
}

if (!locale) {
throw new Error(
`Unable to find \`next-intl\` locale because the middleware didn't run on this request. See https://next-intl-docs.vercel.app/docs/routing/middleware#unable-to-find-locale`
);
if (process.env.NODE_ENV === 'development') {
console.error(
`Unable to find \`next-intl\` locale because the middleware didn't run on this request. See https://next-intl-docs.vercel.app/docs/routing/middleware#unable-to-find-locale. The \`notFound()\` function will be called as a result.`
);
}
notFound();
}

return locale;
Expand Down

0 comments on commit c10e2d1

Please sign in to comment.