Skip to content

Commit 0825f08

Browse files
authored
feat: Re-introduce locale argument for getRequestConfig to be used for overriding the locale (#1625)
**tldr;** — Do you use i18n routing and have you already switched to [`await requestLocale`](https://next-intl.dev/blog/next-intl-3-22#await-request-locale) in `getRequestConfig`? If yes, you can skip this. --- ### Deprecation of `locale` in favor of `await requestLocale` In [next-intl 3.22](https://next-intl.dev/blog/next-intl-3-22), the `locale` argument that was passed to `getRequestConfig` was deprecated in favor of [`await requestLocale`](https://next-intl.dev/blog/next-intl-3-22#await-request-locale): ```diff // i18n/request.ts export default function getRequestConfig(async ({ - locale + requestLocale }) => { + const locale = await requestLocale; // ... })); ``` This change was done in preparation for Next.js 15 where reading from headers [became async](https://nextjs.org/blog/next-15#async-request-apis-breaking-change). If you're using i18n routing, please upgrade to `requestLocale` now. ### Preview: `rootParams` are coming to Next.js Now, with [`rootParams`](vercel/next.js#72837) being on the horizon, this API will allow you to read a locale without receiving any param passed to `getRequestConfig`: ```tsx // i18n/request.ts import {unstable_rootParams as rootParams} from 'next/server'; import {getRequestConfig} from 'next-intl/server'; import {hasLocale} from 'next-intl'; import {routing} from './routing'; export default getRequestConfig(async () => { const params = await rootParams(); const locale = hasLocale(routing.locales, params.locale) ? params.locale : routing.defaultLocale; // ... }); ``` Among other simplifications, this allows to remove manual overrides like this that were merely done for enabling static rendering: ```diff - type Props = { - params: Promise<{locale: string}>; - }; export async function generateMetadata( - {params}: Props ) { - const {locale} = await params; - const t = await getTranslations({locale, namespace: 'HomePage'}); + const t = await getTranslations('HomePage'); // ... } ``` However, in some rare cases, you might want to render messages from multiple locales on the same page: ```tsx // Use messages from 'en', regardless of what the current user locale is const t = getTranslations({locale: 'en'}); ``` If you're using this pattern, you'll be able to accept the overridden locale in `getRequestConfig` as follows: ```tsx // i18n/request.ts import {unstable_rootParams as rootParams} from 'next/server'; import {getRequestConfig} from 'next-intl/server'; import {hasLocale} from 'next-intl'; import {routing} from './routing'; export default getRequestConfig(async ({locale}) => { // Use a locale based on these priorities: // 1. An override passed to the function // 2. A locale from the `[locale]` segment // 3. A default locale if (!locale) { const params = await rootParams(); locale = hasLocale(routing.locales, params.locale) ? params.locale : routing.defaultLocale; } // ... }); ``` This is quite an edge case, but this use case will remain supported via the re-introduced `locale` argument. Note that `await requestLocale` considers a potential locale override, therefore the `locale` argument will only be relevant once `rootParams` are a thing. I hope to have more to share on this in the future!
1 parent c224d1c commit 0825f08

File tree

4 files changed

+21
-3
lines changed

4 files changed

+21
-3
lines changed

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

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ See also: https://next-intl.dev/docs/usage/configuration#i18n-request
3838
}
3939

4040
const params: GetRequestConfigParams = {
41+
locale: localeOverride,
42+
4143
// In case the consumer doesn't read `params.locale` and instead provides the
4244
// `locale` (either in a single-language workflow or because the locale is
4345
// read from the user settings), don't attempt to read the request locale.

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

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type {IntlConfig} from 'use-intl/core';
1+
import type {IntlConfig, Locale} from 'use-intl/core';
22

33
export type RequestConfig = Omit<IntlConfig, 'locale'> & {
44
/**
@@ -8,6 +8,13 @@ export type RequestConfig = Omit<IntlConfig, 'locale'> & {
88
};
99

1010
export type GetRequestConfigParams = {
11+
/**
12+
* If you provide an explicit locale to an async server-side function like
13+
* `getTranslations({locale: 'en'})`, it will be passed via `locale` to
14+
* `getRequestConfig` so you can use it instead of the segment value.
15+
*/
16+
locale?: Locale;
17+
1118
/**
1219
* Typically corresponds to the `[locale]` segment that was matched by the middleware.
1320
*

packages/use-intl/src/core/hasLocale.test.tsx

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {it} from 'vitest';
1+
import {expect, it} from 'vitest';
22
import hasLocale from './hasLocale.tsx';
33

44
it('narrows down the type', () => {
@@ -24,3 +24,12 @@ it('can be called with a non-matching narrow candidate', () => {
2424
candidate satisfies never;
2525
}
2626
});
27+
28+
it('can be called with any candidate', () => {
29+
const locales = ['en-US', 'en-GB'] as const;
30+
expect(hasLocale(locales, 'unknown')).toBe(false);
31+
expect(hasLocale(locales, undefined)).toBe(false);
32+
33+
// Relevant since `ParamValue` in Next.js includes `string[]`
34+
expect(hasLocale(locales, ['de'])).toBe(false);
35+
});

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type {Locale} from './AppConfig.tsx';
77
*/
88
export default function hasLocale<LocaleType extends Locale>(
99
locales: ReadonlyArray<LocaleType>,
10-
candidate?: string | null
10+
candidate: unknown
1111
): candidate is LocaleType {
1212
return locales.includes(candidate as LocaleType);
1313
}

0 commit comments

Comments
 (0)