Skip to content

Commit 7b3a9a3

Browse files
authored
feat: Mark setRequestLocale as stable (#1437)
The previously `unstable_setRequestLocale` is now marked as stable as it's likely required for the foreseeable future.
1 parent e6d9d60 commit 7b3a9a3

File tree

19 files changed

+68
-47
lines changed

19 files changed

+68
-47
lines changed
File renamed without changes.

docs/pages/blog/next-intl-3-22.mdx

+17-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ While this release is fully backwards-compatible, it includes some modern altern
1919
2. [**`i18n/request.ts`**](#i18n-request): Streamlined file organization (introduced in `v3.19`)
2020
3. [**`await requestLocale`**](#await-request-locale): Preparation for Next.js 15 (introduced in `v3.22`)
2121
4. [**`createNavigation`**](#create-navigation): Streamlined navigation APIs (introduced in `v3.22`)
22-
5. [**`defaultTranslationValues`**](#default-translation-values): Deprecated in favor of a user-land pattern (introduced in `v3.22`)
22+
5. [**`setRequestLocale`**](#set-request-locale): Static rendering (marked as stable in `v3.22`)
23+
6. [**`defaultTranslationValues`**](#default-translation-values): Deprecated in favor of a user-land pattern (introduced in `v3.22`)
2324

2425
Let's take a look at these changes in more detail.
2526

@@ -63,7 +64,7 @@ export default createMiddleware(routing);
6364

6465
If you've used `defineRouting` previously to v3.22 and have provided middleware options as a second argument to `createMiddleware`, you can now pass these to `defineRouting` instead.
6566

66-
The docs have been consistently updated to reflect these changes and also suggest the creation of navigation APIs directly in `i18n/routing.ts`. If you prefer to keep your navigation APIs separately, that's of course fine as well.
67+
The docs have been consistently updated to reflect these changes and also suggest the creation of navigation APIs directly in `i18n/routing.ts` for simplicity. If you prefer to keep your navigation APIs separately, that's of course fine as well.
6768

6869
## `i18n/request.ts` [#i18n-request]
6970

@@ -187,7 +188,20 @@ const locale = await getLocale();
187188
+ getPathname(/* ... */);
188189
```
189190

190-
5. If you're using a combination of `localePrefix: 'as-needed'` and `domains` and you're using `getPathname`, you now need to provide a `domain` argument (see [Special case: Using `domains` with `localePrefix: 'as-needed'`](/docs/routing#domains-localeprefix-asneeded))
191+
5. If you're using a combination of `localePrefix: 'as-needed'` & `domains` and you're using `getPathname`, you now need to provide a `domain` argument (see [Special case: Using `domains` with `localePrefix: 'as-needed'`](/docs/routing#domains-localeprefix-asneeded))
192+
193+
## `setRequestLocale` marked as stable [#set-request-locale]
194+
195+
In case you rely on [static rendering](/docs/getting-started/app-router/with-i18n-routing#static-rendering), you might have used the `unstable_setRequestLocale` API before. This function has now been marked as stable since it will likely remain required for the foreseeable future.
196+
197+
```diff
198+
- import {unstable_setRequestLocale} from 'next-intl/server';
199+
+ import {setRequestLocale} from 'next-intl/server';
200+
```
201+
202+
Close to a year ago, I opened [discussion #58862](https://github.com/vercel/next.js/discussions/58862) in the Next.js repository. This was an attempt at starting a conversation about how Next.js could provide a way to access a user locale in Server Components without a tradeoff in ergonomics or rendering implications. While the issue has gained in popularity and currently [ranks as #2](https://github.com/vercel/next.js/discussions?discussions_q=is%3Aopen+sort%3Atop+created%3A%3E%3D2023-10-22) of the top upvoted discussions of the past year, I've unfortunately not been able to get a response from the Next.js team on this topic so far. Based on my understanding, it's certainly not an easy problem to solve, but I'd be more than happy to collaborate on this if I can.
203+
204+
While I'm still optimistic that we can make the `setRequestLocale` API obsolete at some point in the future, the "unstable" prefix doesn't seem to be justified anymore—especially since the API has been known to work reliably since its introduction.
191205

192206
## `defaultTranslationValues` (deprecated) [#default-translation-values]
193207

docs/pages/docs/getting-started/app-router/with-i18n-routing.mdx

+20-23
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ In case you ran into an issue, have a look at [the App Router example](/examples
268268

269269
## Static rendering
270270

271-
When using the setup with i18n routing, `next-intl`will currently opt into dynamic rendering when APIs like `useTranslations` are used in Server Components. This is a limitation that we aim to remove in the future, but as a stopgap solution, `next-intl` provides a temporary API that can be used to enable static rendering.
271+
When using the setup with i18n routing, `next-intl` will currently opt into dynamic rendering when APIs like `useTranslations` are used in Server Components. This is a limitation that we aim to remove in the future, but as a stopgap solution, `next-intl` provides a temporary API that can be used to enable static rendering.
272272

273273
<Steps>
274274

@@ -291,15 +291,23 @@ export function generateStaticParams() {
291291
}
292292
```
293293

294-
### Add `unstable_setRequestLocale` to all layouts and pages
294+
### Add `setRequestLocale` to all relevant layouts and pages
295295

296-
`next-intl` provides a temporary API that can be used to distribute the locale that is received via `params` in layouts and pages for usage in all Server Components that are rendered as part of the request.
296+
`next-intl` provides an API that can be used to distribute the locale that is received via `params` in layouts and pages for usage in all Server Components that are rendered as part of the request.
297297

298298
```tsx filename="app/[locale]/layout.tsx"
299-
import {unstable_setRequestLocale} from 'next-intl/server';
299+
import {setRequestLocale} from 'next-intl/server';
300+
import {notFound} from 'next/navigation';
301+
import {routing} from '@/i18n/routing';
300302

301303
export default async function LocaleLayout({children, params: {locale}}) {
302-
unstable_setRequestLocale(locale);
304+
// Ensure that the incoming `locale` is valid
305+
if (!routing.locales.includes(locale as any)) {
306+
notFound();
307+
}
308+
309+
// Enable static rendering
310+
setRequestLocale(locale);
303311

304312
return (
305313
// ...
@@ -308,10 +316,11 @@ export default async function LocaleLayout({children, params: {locale}}) {
308316
```
309317

310318
```tsx filename="app/[locale]/page.tsx"
311-
import {unstable_setRequestLocale} from 'next-intl/server';
319+
import {setRequestLocale} from 'next-intl/server';
312320

313321
export default function IndexPage({params: {locale}}) {
314-
unstable_setRequestLocale(locale);
322+
// Enable static rendering
323+
setRequestLocale(locale);
315324

316325
// Once the request locale is set, you
317326
// can call hooks from `next-intl`
@@ -325,25 +334,13 @@ export default function IndexPage({params: {locale}}) {
325334

326335
**Keep in mind that:**
327336

328-
1. The locale that you pass to `unstable_setRequestLocale` should be validated (e.g. in [`i18n/request.ts`](/docs/usage/configuration#i18n-request)).
329-
337+
1. The locale that you pass to `setRequestLocale` should be validated (e.g. in your [root layout](#layout)).
330338
2. You need to call this function in every page and every layout that you intend to enable static rendering for since Next.js can render layouts and pages independently.
331339

332-
E.g. when you navigate from `/settings/profile` to `/settings/privacy`, the `/settings` segment might not re-render as part of the request. Due to this, it's important that `unstable_setRequestLocale` is called not only in the parent `settings/layout.tsx`, but also in the individual pages `profile/page.tsx` and `privacy/page.tsx`.
333-
334-
<Details id="setrequestlocale-unstable">
335-
<summary>What does "unstable" mean?</summary>
336-
337-
`unstable_setRequestLocale` is meant to be used as a stopgap solution and we aim to remove it in the future [in case Next.js adds an API to access parts of the URL](https://github.com/vercel/next.js/discussions/58862). If that's the case, you'll get a deprecation notice in a minor version and the API will be removed as part of a major version.
338-
339-
That being said, the API is expected to work reliably if you're cautious to apply it in all relevant places.
340-
341-
</Details>
342-
343340
<Details id="setrequestlocale-implementation">
344-
<summary>How does unstable_setRequestLocale work?</summary>
341+
<summary>How does setRequestLocale work?</summary>
345342

346-
`next-intl` uses [`cache()`](https://react.dev/reference/react/cache) to create a mutable store that holds the current locale. By calling `unstable_setRequestLocale`, the current locale will be written to the store, making it available to all APIs that require the locale.
343+
`next-intl` uses [`cache()`](https://react.dev/reference/react/cache) to create a mutable store that holds the current locale. By calling `setRequestLocale`, the current locale will be written to the store, making it available to all APIs that require the locale.
347344

348345
Note that the store is scoped to a request and therefore doesn't affect other requests that might be handled in parallel while a given request resolves asynchronously.
349346

@@ -358,7 +355,7 @@ Due to this, `next-intl` uses its middleware to attach an `x-next-intl-locale` h
358355

359356
However, the usage of `headers` opts the route into dynamic rendering.
360357

361-
By using `unstable_setRequestLocale`, you can provide the locale that is received in layouts and pages via `params` to `next-intl`. All APIs from `next-intl` can now read from this value instead of the header, enabling static rendering.
358+
By using `setRequestLocale`, you can provide the locale that is received in layouts and pages via `params` to `next-intl`. All APIs from `next-intl` can now read from this value instead of the header, enabling static rendering.
362359

363360
</Details>
364361

docs/pages/docs/usage/configuration.mdx

+1-1
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ Depending on if you're using [i18n routing](/docs/getting-started/app-router), t
699699
700700
The returned value is resolved based on these priorities:
701701
702-
1. **Server Components**: If you're using [i18n routing](/docs/getting-started/app-router), the returned locale is the one that you've either provided via [`unstable_setRequestLocale`](/docs/getting-started/app-router/with-i18n-routing#static-rendering) or alternatively the one in the `[locale]` segment that was matched by the middleware. If you're not using i18n routing, the returned locale is the one that you've provided via `getRequestConfig`.
702+
1. **Server Components**: If you're using [i18n routing](/docs/getting-started/app-router), the returned locale is the one that you've either provided via [`setRequestLocale`](/docs/getting-started/app-router/with-i18n-routing#static-rendering) or alternatively the one in the `[locale]` segment that was matched by the middleware. If you're not using i18n routing, the returned locale is the one that you've provided via `getRequestConfig`.
703703
2. **Client Components**: In this case, the locale is received from `NextIntlClientProvider` or alternatively `useParams().locale`. Note that `NextIntlClientProvider` automatically inherits the locale if the component is rendered by a Server Component. For all other cases, you can specify the value
704704
explicitly.
705705

docs/theme.config.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export default {
108108
title: isDefault ? config.description : pageConfig.title,
109109
subtitle: pageConfig.frontMatter.subtitle
110110
};
111-
const ogImageUrl = new URL('/api/og', config.baseUrl);
111+
const ogImageUrl = new URL('/api/og-image', config.baseUrl);
112112
ogImageUrl.search = new URLSearchParams({
113113
params: JSON.stringify(ogPayload)
114114
}).toString();

examples/example-app-router-mixed-routing/src/app/(public)/[locale]/about/page.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {useTranslations} from 'next-intl';
2-
import {unstable_setRequestLocale} from 'next-intl/server';
2+
import {setRequestLocale} from 'next-intl/server';
33
import PageTitle from '@/components/PageTitle';
44

55
type Props = {
@@ -8,7 +8,7 @@ type Props = {
88

99
export default function About({params: {locale}}: Props) {
1010
// Enable static rendering
11-
unstable_setRequestLocale(locale);
11+
setRequestLocale(locale);
1212

1313
const t = useTranslations('About');
1414
return <PageTitle>{t('title')}</PageTitle>;

examples/example-app-router-mixed-routing/src/app/(public)/[locale]/layout.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {Metadata} from 'next';
22
import {notFound} from 'next/navigation';
33
import {NextIntlClientProvider} from 'next-intl';
4-
import {getMessages, unstable_setRequestLocale} from 'next-intl/server';
4+
import {getMessages, setRequestLocale} from 'next-intl/server';
55
import {ReactNode} from 'react';
66
import Document from '@/components/Document';
77
import {locales} from '@/config';
@@ -26,7 +26,7 @@ export default async function LocaleLayout({
2626
params: {locale}
2727
}: Props) {
2828
// Enable static rendering
29-
unstable_setRequestLocale(locale);
29+
setRequestLocale(locale);
3030

3131
// Ensure that the incoming locale is valid
3232
if (!locales.includes(locale as any)) {

examples/example-app-router-mixed-routing/src/app/(public)/[locale]/page.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {useTranslations} from 'next-intl';
2-
import {unstable_setRequestLocale} from 'next-intl/server';
2+
import {setRequestLocale} from 'next-intl/server';
33
import PageTitle from '@/components/PageTitle';
44

55
type Props = {
@@ -8,7 +8,7 @@ type Props = {
88

99
export default function Index({params: {locale}}: Props) {
1010
// Enable static rendering
11-
unstable_setRequestLocale(locale);
11+
setRequestLocale(locale);
1212

1313
const t = useTranslations('Index');
1414
return <PageTitle>{t('title')}</PageTitle>;

examples/example-app-router/src/app/[locale]/layout.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {notFound} from 'next/navigation';
2-
import {getTranslations, unstable_setRequestLocale} from 'next-intl/server';
2+
import {getTranslations, setRequestLocale} from 'next-intl/server';
33
import {ReactNode} from 'react';
44
import BaseLayout from '@/components/BaseLayout';
55
import {routing} from '@/i18n/routing';
@@ -33,7 +33,7 @@ export default async function LocaleLayout({
3333
}
3434

3535
// Enable static rendering
36-
unstable_setRequestLocale(locale);
36+
setRequestLocale(locale);
3737

3838
return <BaseLayout locale={locale}>{children}</BaseLayout>;
3939
}

examples/example-app-router/src/app/[locale]/page.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {useTranslations} from 'next-intl';
2-
import {unstable_setRequestLocale} from 'next-intl/server';
2+
import {setRequestLocale} from 'next-intl/server';
33
import PageLayout from '@/components/PageLayout';
44

55
type Props = {
@@ -8,7 +8,7 @@ type Props = {
88

99
export default function IndexPage({params: {locale}}: Props) {
1010
// Enable static rendering
11-
unstable_setRequestLocale(locale);
11+
setRequestLocale(locale);
1212

1313
const t = useTranslations('IndexPage');
1414

examples/example-app-router/src/app/[locale]/pathnames/page.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {useTranslations} from 'next-intl';
2-
import {unstable_setRequestLocale} from 'next-intl/server';
2+
import {setRequestLocale} from 'next-intl/server';
33
import PageLayout from '@/components/PageLayout';
44

55
type Props = {
@@ -8,7 +8,7 @@ type Props = {
88

99
export default function PathnamesPage({params: {locale}}: Props) {
1010
// Enable static rendering
11-
unstable_setRequestLocale(locale);
11+
setRequestLocale(locale);
1212

1313
const t = useTranslations('PathnamesPage');
1414

packages/next-intl/src/navigation/react-client/createLocalizedPathnamesNavigation.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import useBasePathname from './useBasePathname';
2525
import useBaseRouter from './useBaseRouter';
2626

2727
/**
28-
* @deprecated Consider switching to `createNavigation` (see https://github.com/amannn/next-intl/pull/1316)
28+
* @deprecated Consider switching to `createNavigation` (see https://next-intl-docs.vercel.app/blog/next-intl-3-22#create-navigation)
2929
**/
3030
export default function createLocalizedPathnamesNavigation<
3131
AppLocales extends Locales,

packages/next-intl/src/navigation/react-client/createSharedPathnamesNavigation.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import useBasePathname from './useBasePathname';
1212
import useBaseRouter from './useBaseRouter';
1313

1414
/**
15-
* @deprecated Consider switching to `createNavigation` (see https://github.com/amannn/next-intl/pull/1316)
15+
* @deprecated Consider switching to `createNavigation` (see https://next-intl-docs.vercel.app/blog/next-intl-3-22#create-navigation)
1616
**/
1717
export default function createSharedPathnamesNavigation<
1818
AppLocales extends Locales,

packages/next-intl/src/server/react-client/index.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {
55
getNow as getNow_type,
66
getRequestConfig as getRequestConfig_type,
77
getTimeZone as getTimeZone_type,
8+
setRequestLocale as setRequestLocale_type,
89
unstable_setRequestLocale as unstable_setRequestLocale_type
910
} from '../react-server';
1011

@@ -48,3 +49,7 @@ export const getTranslations = notSupported('getTranslations');
4849
export const unstable_setRequestLocale = notSupported(
4950
'unstable_setRequestLocale'
5051
) as typeof unstable_setRequestLocale_type;
52+
53+
export const setRequestLocale = notSupported(
54+
'setRequestLocale'
55+
) as typeof setRequestLocale_type;

packages/next-intl/src/server/react-server/RequestLocale.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ async function getLocaleFromHeaderImpl(): Promise<string | undefined> {
2424
(error as any).digest === 'DYNAMIC_SERVER_USAGE'
2525
) {
2626
const wrappedError = new Error(
27-
'Usage of next-intl APIs in Server Components currently opts into dynamic rendering. This limitation will eventually be lifted, but as a stopgap solution, you can use the `unstable_setRequestLocale` API to enable static rendering, see https://next-intl-docs.vercel.app/docs/getting-started/app-router/with-i18n-routing#static-rendering',
27+
'Usage of next-intl APIs in Server Components currently opts into dynamic rendering. This limitation will eventually be lifted, but as a stopgap solution, you can use the `setRequestLocale` API to enable static rendering, see https://next-intl-docs.vercel.app/docs/getting-started/app-router/with-i18n-routing#static-rendering',
2828
{cause: error}
2929
);
3030
(wrappedError as any).digest = (error as any).digest;

packages/next-intl/src/server/react-server/RequestLocaleLegacy.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ function getLocaleFromHeaderImpl() {
2222
(error as any).digest === 'DYNAMIC_SERVER_USAGE'
2323
) {
2424
throw new Error(
25-
'Usage of next-intl APIs in Server Components currently opts into dynamic rendering. This limitation will eventually be lifted, but as a stopgap solution, you can use the `unstable_setRequestLocale` API to enable static rendering, see https://next-intl-docs.vercel.app/docs/getting-started/app-router/with-i18n-routing#static-rendering',
25+
'Usage of next-intl APIs in Server Components currently opts into dynamic rendering. This limitation will eventually be lifted, but as a stopgap solution, you can use the `setRequestLocale` API to enable static rendering, see https://next-intl-docs.vercel.app/docs/getting-started/app-router/with-i18n-routing#static-rendering',
2626
{cause: error}
2727
);
2828
} else {

packages/next-intl/src/server/react-server/getRequestConfig.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export type RequestConfig = Omit<IntlConfig, 'locale'> & {
1616

1717
export type GetRequestConfigParams = {
1818
/**
19-
* Deprecated in favor of `requestLocale` (see https://github.com/amannn/next-intl/pull/1383).
19+
* Deprecated in favor of `requestLocale` (see https://next-intl-docs.vercel.app/blog/next-intl-3-22#await-request-locale).
2020
*
2121
* The locale that was matched by the `[locale]` path segment. Note however
2222
* that this can be overridden in async APIs when the `locale` is explicitly

packages/next-intl/src/server/react-server/index.tsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,9 @@ export {default as getTranslations} from './getTranslations';
1010
export {default as getMessages} from './getMessages';
1111
export {default as getLocale} from './getLocale';
1212

13-
export {setCachedRequestLocale as unstable_setRequestLocale} from './RequestLocaleCache';
13+
export {setCachedRequestLocale as setRequestLocale} from './RequestLocaleCache';
14+
15+
export {
16+
/** @deprecated Deprecated in favor of `setRequestLocale`. */
17+
setCachedRequestLocale as unstable_setRequestLocale
18+
} from './RequestLocaleCache';

packages/use-intl/src/core/IntlConfig.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ type IntlConfig<Messages = AbstractIntlMessages> = {
4646
* Defaults will be overidden by locally provided values.
4747
*
4848
* @deprecated See https://next-intl-docs.vercel.app/docs/usage/messages#rich-text-reuse-tags
49-
* */
49+
**/
5050
defaultTranslationValues?: RichTranslationValues;
5151
};
5252

0 commit comments

Comments
 (0)