Skip to content

Commit f4bb734

Browse files
committed
Merge remote-tracking branch 'origin/main' into v4
# Conflicts: # docs/src/pages/docs/environments/error-files.mdx # docs/src/pages/docs/getting-started/app-router/with-i18n-routing.mdx # docs/src/pages/docs/routing.mdx # examples/example-app-router-migration/src/app/[locale]/layout.tsx # examples/example-app-router-mixed-routing/src/app/(public)/[locale]/PublicNavigationLocaleSwitcher.tsx # examples/example-app-router-mixed-routing/src/app/(public)/[locale]/about/page.tsx # examples/example-app-router-mixed-routing/src/app/(public)/[locale]/layout.tsx # examples/example-app-router-mixed-routing/src/app/(public)/[locale]/page.tsx # examples/example-app-router-mixed-routing/src/db.ts # examples/example-app-router-next-auth/src/app/[locale]/layout.tsx # examples/example-app-router-playground/eslint.config.mjs # examples/example-app-router-playground/next.config.mjs # examples/example-app-router-playground/package.json # examples/example-app-router-playground/src/app/[locale]/about/page.tsx # examples/example-app-router-playground/src/app/[locale]/api/route.ts # examples/example-app-router-playground/src/app/[locale]/client/ClientContent.tsx # examples/example-app-router-playground/src/app/[locale]/layout.tsx # examples/example-app-router-playground/src/app/[locale]/news/[articleId]/page.tsx # examples/example-app-router-playground/src/i18n/routing.ts # examples/example-app-router/src/app/[locale]/layout.tsx # examples/example-app-router/src/app/[locale]/page.tsx # examples/example-app-router/src/app/[locale]/pathnames/page.tsx # examples/example-app-router/src/app/sitemap.ts # examples/example-app-router/src/components/BaseLayout.tsx # examples/example-app-router/src/components/LocaleSwitcherSelect.tsx # examples/example-app-router/src/i18n/routing.ts # packages/next-intl/.size-limit.ts # pnpm-lock.yaml
2 parents dea867b + 7487e5e commit f4bb734

File tree

106 files changed

+2555
-1367
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+2555
-1367
lines changed

docs/src/components/CodeSnippets.tsx

+5-5
Original file line numberDiff line numberDiff line change
@@ -293,23 +293,23 @@ function buildOutput() {
293293
<span className="line">
294294
<span data-token="text">┌ ● /</span>
295295
<span data-token="text"> </span>
296-
<span data-token="text">{' '}2.1 kB</span>
296+
<span data-token="text">{' '}2.5 kB</span>
297297
<span data-token="text">{' '}</span>
298-
<span data-token="string">97.1 kB</span>
298+
<span data-token="string">115 kB</span>
299299
</span>
300300
<span className="line">
301301
<span data-token="text">├ ● /about</span>
302302
<span data-token="text">{' '}</span>
303-
<span data-token="text">2.5 kB</span>
303+
<span data-token="text">2.6 kB</span>
304304
<span data-token="text">{' '}</span>
305-
<span data-token="string">97.6 kB</span>
305+
<span data-token="string">116 kB</span>
306306
</span>
307307
<span className="line">
308308
<span data-token="text">└ λ /[username]</span>
309309
<span data-token="text">{' '}</span>
310310
<span data-token="text">3.2 kB</span>
311311
<span data-token="text">{' '}</span>
312-
<span data-token="string">98.3 kB</span>
312+
<span data-token="string">117 kB</span>
313313
</span>
314314
<span className="line"> </span>
315315
<span className="line">

docs/src/components/HeroCode.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ const files = [
164164
<span data-token="text">:</span>
165165
<span data-token="text"> </span>
166166
<span data-token="string">
167-
"{'{'}firstname{'}'}'s profile"
167+
"{'{'}firstName{'}'}'s profile"
168168
</span>
169169
<span data-token="text">,</span>
170170
</span>

docs/src/pages/blog/next-intl-4-0.mdx

+72-16
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ Here's what's new in `next-intl@4.0`:
1919
4. [**GDPR compliance**](#gdpr-compliance)
2020
5. [**Modernized build output**](#modernized-build-output)
2121
6. [**Improved inheritance in `NextIntlClientProvider`**](#nextintlclientprovider-inheritance)
22-
7. [**Preparation for upcoming Next.js features**](#nextjs-future)
22+
7. [**Stricter config for `domains`**](#domains-config)
23+
8. [**Preparation for upcoming Next.js features**](#nextjs-future)
2324

24-
Please also have a look at the [other breaking changes](#other-breaking-changes) listed below before you upgrade.
25+
Please also have a look at the [other changes](#other-changes) listed below before you upgrade.
2526

2627
## Revamped augmented types
2728

@@ -118,7 +119,7 @@ t('message', {});
118119
t('message', {});
119120
// ^? {page: number, total: number}
120121

121-
// "You have {count, plural, =0 {no followers yet} =1 {one follower} other {# followers}}."
122+
// "You have {count, plural, =0 {no followers yet} one {one follower} other {# followers}}."
122123
t('message', {});
123124
// ^? {count: number}
124125

@@ -127,8 +128,8 @@ t('message', {});
127128
// ^? {country: 'US' | 'CA' | (string & {})}
128129

129130
// "Please refer to the <link>guidelines</link>."
130-
t('message', {});
131-
// ^? {link: (chunks: ReactNode) => ReactNode}
131+
t.rich('message', {});
132+
// ^? {link: (chunks: ReactNode) => ReactNode}
132133
```
133134

134135
With this type inference in place, you can now use autocompletion in your IDE to get suggestions for the available arguments of a given ICU message and catch potential errors early.
@@ -153,7 +154,7 @@ Due to a current limitation in TypeScript, this feature is opt-in for now. Pleas
153154

154155
In order to comply with the current GDPR regulations, the following changes have been made and are relevant to you if you're using the `next-intl` middleware for i18n routing:
155156

156-
1. The locale cookie has been changed to a session cookie that expires when a browser is closed.
157+
1. The locale cookie now defaults to a session cookie that expires when a browser is closed.
157158
2. The locale cookie is now only set when a user switches to a locale that doesn't match the `accept-language` header.
158159

159160
If you want to increase the cookie expiration, e.g. because you're informing users about the usage of cookies or if GDPR doesn't apply to your app, you can use the `maxAge` attribute to do so:
@@ -187,13 +188,13 @@ The build output of `next-intl` has been modernized and now leverages the follow
187188
2. **Modern JSX transform:** The peer dependency for React has been bumped to v17 in order to use the more efficient, modern JSX transform.
188189
3. **Modern syntax:** Syntax is now compiled down to the Browserslist `defaults` query, which is a shortcut for ">0.5%, last 2 versions, Firefox ESR, not dead"—a baseline that is considered a reasonable target for modern apps.
189190

190-
With these changes, the bundle size of `next-intl` has been reduced by ~7% ([all details](https://github.com/amannn/next-intl/pull/1470)).
191+
With these changes, the bundle size of `next-intl` has been reduced by ~7% ([PR #1470](https://github.com/amannn/next-intl/pull/1470)).
191192

192193
## Improved inheritance of `NextIntlClientProvider` [#nextintlclientprovider-inheritance]
193194

194195
Previously, [`NextIntlClientProvider`](/docs/usage/configuration#nextintlclientprovider) would conservatively inherit only a subset from `i18n/request.ts`.
195196

196-
To improve the getting started experience, the provider now also inherits:
197+
To improve the getting started experience, the provider by default now also inherits:
197198

198199
- `messages` ([PR #1682](https://github.com/amannn/next-intl/pull/1682))
199200
- `formats` ([PR #1191](https://github.com/amannn/next-intl/pull/1191))
@@ -209,10 +210,64 @@ Due to this, you can now remove these props from `NextIntlClientProvider` if you
209210
</NextIntlClientProvider>
210211
```
211212

213+
If you don't want to inherit these props, you can either opt-out via `messages={null}` and `formats={null}`, or by passing a specific value for these props.
214+
212215
With this, `NextIntlClientProvider` now inherits all of your configuration, with the minor exception of [error handling functions](/docs/usage/configuration#error-handling). Since functions are not serializable, they cannot be passed across the server/client boundary. However, [an alternative](https://github.com/amannn/next-intl/issues/1285) for this is also on the horizon.
213216

214217
To make it easier to work with error handling functions on the client side, `NextIntlClientProvider` can now also be used in a nested fashion and will inherit the configuration from a parent provider ([PR #1413](https://github.com/amannn/next-intl/pull/1413)).
215218

219+
## Stricter config for `domains` [#domains-config]
220+
221+
So far, when using [`domains`](/docs/routing#domains) in combination with [`localePrefix: 'as-needed'`](/docs/routing#locale-prefix-as-needed), `next-intl` had to make some [tradeoffs](https://next-intl-docs-6wwcmwb9a-next-intl.vercel.app/docs/routing#domains-localeprefix-asneeded) to avoid reading the current host of the incoming request in components.
222+
223+
Now, by introducing two new constraints, `next-intl` can avoid these tradeoffs altogether:
224+
225+
1. A locale can now only be used for a single domain
226+
2. Each domain now must specify its `locales`
227+
228+
The result is a simplified, more intuitive model that works as expected for this popular use case.
229+
230+
If you previously used locales across multiple domains, you now have to be more specific—typically by introducing a regional variant for a base language. You can additionally customize the prefixes if desired.
231+
232+
**Example:**
233+
234+
```tsx
235+
import {defineRouting} from 'next-intl/routing';
236+
237+
export const routing = defineRouting({
238+
locales: ['sv-SE', 'en-SE', 'no-NO', 'en-NO'],
239+
defaultLocale: 'en-SE',
240+
localePrefix: {
241+
mode: 'as-needed',
242+
prefixes: {
243+
'en-SE': '/en',
244+
'en-NO': '/en'
245+
}
246+
},
247+
domains: [
248+
{
249+
domain: 'example.se',
250+
defaultLocale: 'sv-SE',
251+
locales: ['sv-SE', 'en-SE']
252+
},
253+
{
254+
domain: 'example.no',
255+
defaultLocale: 'no-NO',
256+
locales: ['no-NO', 'en-NO']
257+
}
258+
]
259+
});
260+
```
261+
262+
This will create the following structure:
263+
264+
- `example.se`: `sv-SE`
265+
- `example.se/en`: `en-SE`
266+
- `example.no`: `no-NO`
267+
- `example.no/en`: `en-NO`
268+
269+
Learn more in the updated docs for [`domains`](https://v4.next-intl.dev/docs/routing#domains).
270+
216271
## Preparation for upcoming Next.js features [#nextjs-future]
217272

218273
To ensure that the sails of `next-intl` are set for a steady course in the upcoming future, I've investigated the implications of upcoming Next.js features like [`ppr`](https://nextjs.org/docs/app/api-reference/next-config-js/ppr), [`dynamicIO`](https://nextjs.org/docs/canary/app/api-reference/config/next-config-js/dynamicIO) and [`rootParams`](https://github.com/vercel/next.js/pull/72837) for `next-intl`.
@@ -221,21 +276,22 @@ This led to three minor changes:
221276

222277
1. If you don't already have a `NextIntlClientProvider` in your app that wraps all Client Components that use `next-intl`, you now have to add one (see [PR #1541](https://github.com/amannn/next-intl/pull/1541) for details).
223278
2. If you're using `format.relativeTime` in Client Components, you may need to provide the `now` argument explicitly now (see [PR #1536](https://github.com/amannn/next-intl/pull/1536) for details).
224-
3. If you're using i18n routing, make sure you've updated to [`await requestLocale`](https://next-intl.dev/blog/next-intl-3-22#await-request-locale) that was introduced in `next-intl@3.22`. The previously deprecated `locale` argument will serve an edge case in the future once `rootParams` is a thing (see [PR #1625](https://github.com/amannn/next-intl/pull/1625/) for details).
279+
3. If you're using i18n routing, make sure you've updated to [`await requestLocale`](/blog/next-intl-3-22#await-request-locale) that was introduced in `next-intl@3.22`. The previously deprecated `locale` argument will serve an edge case in the future once `rootParams` is a thing (see [PR #1625](https://github.com/amannn/next-intl/pull/1625/) for details).
225280

226281
While the mentioned Next.js features are still under development and may change, these changes seem reasonable to me in any case—and ideally will be all that's necessary to adapt for `next-intl` to get the most out of these upcoming capabilities.
227282

228283
I'm particularly excited about the announcement of `rootParams`, as it seems like this will finally fill in the [missing piece](https://github.com/vercel/next.js/discussions/58862) that enables apps with i18n routing to support static rendering without workarounds like `setRequestLocale`. I hope to have more to share on this soon!
229284

230-
## Other breaking changes
285+
## Other changes
231286

232287
1. Return type-safe messages from `useMessages` and `getMessages` (see [PR #1489](https://github.com/amannn/next-intl/pull/1489))
233288
2. Require locale to be returned from `getRequestConfig` (see [PR #1486](https://github.com/amannn/next-intl/pull/1486))
234-
3. Disallow passing `null`, `undefined` or `boolean` as an ICU argument (see [PR #1561](https://github.com/amannn/next-intl/pull/1561))
235-
4. Bump minimum required TypeScript version to 5 for projects using TypeScript (see [PR #1481](https://github.com/amannn/next-intl/pull/1481))
236-
5. Return `x-default` alternate link also for sub pages when using `localePrefix: 'always'` and update middleware matcher suggestion in docs (see [PR #1720](https://github.com/amannn/next-intl/pull/1720))
237-
6. Remove deprecated APIs (see [PR #1479](https://github.com/amannn/next-intl/pull/1479))
238-
7. Remove deprecated APIs pt. 2 (see [PR #1482](https://github.com/amannn/next-intl/pull/1482))
289+
3. Allow to declare `pathnames` partially for convenience (see [PR #1743](https://github.com/amannn/next-intl/pull/1743))
290+
4. Disallow passing `null`, `undefined` or `boolean` as an ICU argument (see [PR #1561](https://github.com/amannn/next-intl/pull/1561))
291+
5. Bump minimum required TypeScript version to 5 for projects using TypeScript (see [PR #1481](https://github.com/amannn/next-intl/pull/1481))
292+
6. Return `x-default` alternate link also for sub pages when using `localePrefix: 'always'` and update middleware matcher suggestion to `/((?!api|_next|_vercel|.*\\..*).*)` (see [PR #1720](https://github.com/amannn/next-intl/pull/1720))
293+
7. Remove deprecated APIs (see [PR #1479](https://github.com/amannn/next-intl/pull/1479))
294+
8. Remove deprecated APIs pt. 2 (see [PR #1482](https://github.com/amannn/next-intl/pull/1482))
239295

240296
## Upgrade now
241297

@@ -255,7 +311,7 @@ I'd love to hear about your experiences with `next-intl@4.0`! Join the conversat
255311

256312
I want to sincerely thank everyone who has helped to make `next-intl` what it is today.
257313

258-
A special thank you goes to <PartnerContentLink href="https://crowdin.com/">Crowdin</PartnerContentLink>, the primary sponsor of `next-intl`, enabling me to regularly work on this project and provide it as a free and open-source library for everyone.
314+
A special thank you goes to <PartnerContentLink href="https://crowdin.com/">Crowdin</PartnerContentLink>, the sponsor partner of `next-intl`, enabling me to regularly work on this project and provide it as a free and open-source library for everyone.
259315

260316
—Jan
261317

docs/src/pages/docs/environments/actions-metadata-route-handlers.mdx

+6-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ To internationalize metadata like the page title, you can use functionality from
2121
```tsx filename="layout.tsx"
2222
import {getTranslations} from 'next-intl/server';
2323

24-
export async function generateMetadata({params: {locale}}) {
24+
export async function generateMetadata({params}) {
25+
const {locale} = await params;
2526
const t = await getTranslations({locale, namespace: 'Metadata'});
2627

2728
return {
@@ -109,7 +110,8 @@ If you're programmatically generating [Open Graph images](https://nextjs.org/doc
109110
import {ImageResponse} from 'next/og';
110111
import {getTranslations} from 'next-intl/server';
111112

112-
export default async function OpenGraphImage({params: {locale}}) {
113+
export default async function OpenGraphImage({params}) {
114+
const {locale} = await params;
113115
const t = await getTranslations({locale, namespace: 'OpenGraphImage'});
114116
return new ImageResponse(<div style={{fontSize: 128}}>{t('title')}</div>);
115117
}
@@ -231,12 +233,13 @@ You can use `next-intl` in [Route Handlers](https://nextjs.org/docs/app/building
231233
import {NextResponse} from 'next/server';
232234
import {hasLocale} from 'next-intl';
233235
import {getTranslations} from 'next-intl/server';
236+
import {routing} from '@/i18n/routing';
234237

235238
export async function GET(request) {
236239
// Example: Receive the `locale` via a search param
237240
const {searchParams} = new URL(request.url);
238241
const locale = searchParams.get('locale');
239-
if (!hasLocale(locales, locale)) {
242+
if (!hasLocale(routing.locales, locale)) {
240243
return NextResponse.json({error: 'Invalid locale'}, {status: 400});
241244
}
242245

docs/src/pages/docs/environments/error-files.mdx

+2-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ import {hasLocale} from 'next-intl';
8484
import {notFound} from 'next/navigation';
8585
import {routing} from '@/i18n/routing';
8686

87-
export default function LocaleLayout({children, params: {locale}}) {
87+
export default function LocaleLayout({children, params}) {
88+
const {locale} = await params;
8889
if (!hasLocale(routing.locales, locale)) {
8990
notFound();
9091
}

docs/src/pages/docs/environments/mdx.mdx

+3-1
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ Now, in `page.tsx`, you can import the MDX content based on the user's locale:
2424
import {notFound} from 'next/navigation';
2525

2626
export default async function HomePage({params}) {
27+
const {locale} = await params;
28+
2729
try {
28-
const Content = (await import(`./${params.locale}.mdx`)).default;
30+
const Content = (await import(`./${locale}.mdx`)).default;
2931
return <Content />;
3032
} catch (error) {
3133
notFound();

0 commit comments

Comments
 (0)