Skip to content

Commit ba6b1ad

Browse files
authored
feat: Support ./i18n/request.ts in addition to i18n.ts (#1303)
1 parent 99a3545 commit ba6b1ad

File tree

53 files changed

+170
-149
lines changed

Some content is hidden

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

53 files changed

+170
-149
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ The latter two have already been added in minor versions, but 3.0 cleans up the
3131

3232
`next-intl` now requires two additional setup steps when you're using the App Router:
3333

34-
1. [The `i18n.ts` module](/docs/getting-started/app-router#i18nts) provides configuration for Server Components
34+
1. [The `i18n.ts` module](/docs/getting-started/app-router#i18n-request) provides configuration for Server Components
3535
2. [`next-intl/plugin`](/docs/getting-started/app-router#nextconfigjs) needs to be added to link your `i18n.ts` module to `next-intl`
3636

3737
### New navigation APIs for the App Router

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ If you're using the [`pathnames`](/docs/routing#pathnames) setting, you can gene
205205
```tsx
206206
import {MetadataRoute} from 'next';
207207
import {locales, defaultLocale} from '@/config';
208-
import {getPathname} from '@/routing';
208+
import {getPathname} from '@/i18n/routing';
209209

210210
// Adapt this as necessary
211211
const host = 'https://acme.com';

docs/pages/docs/environments/core-library.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ While `next-intl` is primarily intended to be used in Next.js apps, the core is
88

99
1. [Routing APIs](/docs/routing)
1010
2. [Awaitable APIs](/docs/environments/actions-metadata-route-handlers) for Server Actions, Metadata and Route Handlers
11-
3. [Server Components integration](/docs/environments/server-client-components) along with `i18n.ts`
11+
3. [Server Components integration](/docs/environments/server-client-components) along with `i18n/request.ts`
1212

1313
In case Server Components establish themselves in React apps outside of Next.js, the support for Server Components might be moved to the core library in the future.
1414

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

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,18 +77,16 @@ export default function RootLayout({children}) {
7777
}
7878
```
7979

80-
For the 404 page to render, we need to call the `notFound` function in [`i18n.ts`](/docs/usage/configuration#i18nts) when we detect an incoming `locale` param that isn't a valid locale.
80+
For the 404 page to render, we need to call the `notFound` function in [`i18n/request.ts`](/docs/usage/configuration#i18n-request) when we detect an incoming `locale` param that isn't a valid locale.
8181

82-
```tsx filename="i18n.ts"
82+
```tsx filename="i18n/request.ts"
8383
import {notFound} from 'next/navigation';
8484
import {getRequestConfig} from 'next-intl/server';
85-
86-
// Can be imported from a shared config
87-
const locales = ['en', 'de'];
85+
import {routing} from '@/i18n/routing';
8886

8987
export default getRequestConfig(async ({locale}) => {
9088
// Validate that the incoming `locale` parameter is valid
91-
if (!locales.includes(locale as any)) notFound();
89+
if (!routing.locales.includes(locale as any)) notFound();
9290

9391
return {
9492
// ...

docs/pages/docs/environments/server-client-components.mdx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ If you import `useTranslations`, `useFormatter`, `useLocale`, `useNow` and `useT
9191
<Details id="rsc-background">
9292
<summary>How does the Server Components integration work?</summary>
9393

94-
`next-intl` uses [`react-server` conditional exports](https://github.com/reactjs/rfcs/blob/main/text/0227-server-module-conventions.md#react-server-conditional-exports) to load code that is optimized for the usage in Server or Client Components. While configuration for hooks like `useTranslations` is read via `useContext` on the client side, on the server side it is loaded via [`i18n.ts`](/docs/usage/configuration#i18nts).
94+
`next-intl` uses [`react-server` conditional exports](https://github.com/reactjs/rfcs/blob/main/text/0227-server-module-conventions.md#react-server-conditional-exports) to load code that is optimized for the usage in Server or Client Components. While configuration for hooks like `useTranslations` is read via `useContext` on the client side, on the server side it is loaded via [`i18n/request.ts`](/docs/usage/configuration#i18n-request).
9595

9696
Hooks are currently primarly known for being used in Client Components since they are typically stateful or don't apply to a server environment. However, hooks like [`useId`](https://react.dev/reference/react/useId) can be used in Server Components too. Similarly, `next-intl` provides a hooks-based API that looks identical, regardless of if it's used in a Server or Client Component.
9797

@@ -106,13 +106,13 @@ If you implement components that qualify as shared components, it can be benefic
106106

107107
However, there's no need to dogmatically use non-async functions exclusively for handling internationalization—use what fits your app best.
108108

109-
In regard to performance, async functions and hooks can be used very much interchangeably. The configuration from [`i18n.ts`](/docs/usage/configuration#i18nts) is only loaded once upon first usage and both implementations use request-based caching internally where relevant. The only minor difference is that async functions have the benefit that rendering can be resumed right after an async function has been invoked. In contrast, in case a hook call triggers the initialization in `i18n.ts`, the component will suspend until the config is resolved and will re-render subsequently, possibly re-executing component logic prior to the hook call. However, once config has been resolved as part of a request, hooks will execute synchronously without suspending, resulting in less overhead in comparison to async functions since rendering can be resumed without having to wait for the microtask queue to flush (see [resuming a suspended component by replaying its execution](https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md#resuming-a-suspended-component-by-replaying-its-execution) in the corresponding React RFC).
109+
Regarding performance, async functions and hooks can be used interchangeably. The configuration from [`i18n/request.ts`](/docs/usage/configuration#i18n-request) is only loaded once upon first usage and both implementations use request-based caching internally where relevant. The only minor difference is that async functions have the benefit that rendering can be resumed right after an async function has been invoked. In contrast, in case a hook call triggers the initialization in `i18n/request.ts`, the component will suspend until the config is resolved and will re-render subsequently, possibly re-executing component logic prior to the hook call. However, once config has been resolved as part of a request, hooks will execute synchronously without suspending, resulting in less overhead in comparison to async functions since rendering can be resumed without having to wait for the microtask queue to flush (see [resuming a suspended component by replaying its execution](https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md#resuming-a-suspended-component-by-replaying-its-execution) in the corresponding React RFC).
110110

111111
</Details>
112112

113113
## Using internationalization in Client Components
114114

115-
Depending on your situation, you may need to handle internationalization in Client Components as well. While providing all messages to the client side is typically the easiest way to [get started](/docs/getting-started/app-router#layout) and a reasonable approach for many apps, you can be more selective about which messages are passed to the client side if you're interested in optimizing the performance of your app.
115+
Depending on your situation, you may need to handle internationalization in Client Components. While providing all messages to the client side is typically the easiest way to [get started](/docs/getting-started/app-router#layout) and a reasonable approach for many apps, you can be more selective about which messages are passed to the client side if you're interested in optimizing the performance of your app.
116116

117117
<Details id="client-messages-performance">
118118
<summary>How does loading messages on the client side relate to performance?</summary>
@@ -298,7 +298,7 @@ import {NextIntlClientProvider, useMessages} from 'next-intl';
298298
import ClientCounter from './ClientCounter';
299299

300300
export default function Counter() {
301-
// Receive messages provided in `i18n.ts` …
301+
// Receive messages provided in `i18n/request.ts` …
302302
const messages = useMessages();
303303

304304
return (
@@ -332,7 +332,7 @@ import {NextIntlClientProvider} from 'next-intl';
332332
import {getMessages} from 'next-intl/server';
333333

334334
export default async function RootLayout(/* ... */) {
335-
// Receive messages provided in `i18n.ts`
335+
// Receive messages provided in `i18n/request.ts`
336336
const messages = await getMessages();
337337

338338
return (

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

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ Now, we're going to create the following file structure:
2828
│ └── ...
2929
├── next.config.mjs (2)
3030
└── src
31-
├── routing.ts (3)
31+
├── i18n
32+
│ ├── routing.ts (3)
33+
│ └── request.ts (5)
3234
├── middleware.ts (4)
33-
├── i18n.ts (5)
3435
└── app
3536
└── [locale]
3637
├── layout.tsx (6)
@@ -93,7 +94,7 @@ module.exports = withNextIntl(nextConfig);
9394
</Tab>
9495
</Tabs>
9596

96-
### `src/routing.ts` [#i18n-routing]
97+
### `src/i18n/routing.ts` [#i18n-routing]
9798

9899
We'll integrate with Next.js' routing in two places:
99100

@@ -104,7 +105,7 @@ This enables you to work with pathnames like `/about`, while i18n aspects like l
104105

105106
To share the configuration between these two places, we'll set up `routing.ts`:
106107

107-
```ts filename="src/routing.ts"
108+
```ts filename="src/i18n/routing.ts"
108109
import {defineRouting} from 'next-intl/routing';
109110
import {createSharedPathnamesNavigation} from 'next-intl/navigation';
110111

@@ -130,7 +131,7 @@ Once we have our routing configuration in place, we can use it to set up the mid
130131

131132
```tsx filename="src/middleware.ts"
132133
import createMiddleware from 'next-intl/middleware';
133-
import {routing} from './routing';
134+
import {routing} from './i18n/routing';
134135

135136
export default createMiddleware(routing);
136137

@@ -140,11 +141,11 @@ export const config = {
140141
};
141142
```
142143

143-
### `src/i18n.ts` [#i18n-request]
144+
### `src/i18n/request.ts` [#i18n-request]
144145

145146
`next-intl` creates a request-scoped configuration object, which you can use to provide messages and other options based on the user's locale to Server Components.
146147

147-
```tsx filename="src/i18n.ts"
148+
```tsx filename="src/i18n/request.ts"
148149
import {notFound} from 'next/navigation';
149150
import {getRequestConfig} from 'next-intl/server';
150151
import {routing} from './routing';
@@ -154,30 +155,30 @@ export default getRequestConfig(async ({locale}) => {
154155
if (!routing.locales.includes(locale as any)) notFound();
155156

156157
return {
157-
messages: (await import(`../messages/${locale}.json`)).default
158+
messages: (await import(`../../messages/${locale}.json`)).default
158159
};
159160
});
160161
```
161162

162-
<Details id="move-i18n-request">
163+
<Details id="i18n-request-path">
163164
<summary>Can I move this file somewhere else?</summary>
164165

165-
This file is supported out-of-the-box as `./i18n.ts` both in the `src` folder as well as in the project root with the extensions `.ts`, `.tsx`, `.js` and `.jsx`.
166+
This file is supported out-of-the-box as `./i18n/request.ts` both in the `src` folder as well as in the project root with the extensions `.ts`, `.tsx`, `.js` and `.jsx`.
166167

167168
If you prefer to move this file somewhere else, you can optionally provide a path to the plugin:
168169

169170
```js filename="next.config.mjs"
170171
const withNextIntl = createNextIntlPlugin(
171172
// Specify a custom path here
172-
'./somewhere/else/i18n.ts'
173+
'./somewhere/else/request.ts'
173174
);
174175
```
175176

176177
</Details>
177178

178179
### `src/app/[locale]/layout.tsx` [#layout]
179180

180-
The `locale` that was matched by the middleware is available via the `locale` param and can be used to configure the document language. Additionally, we can use this place to pass configuration from `i18n.ts` to Client Components via `NextIntlClientProvider`.
181+
The `locale` that was matched by the middleware is available via the `locale` param and can be used to configure the document language. Additionally, we can use this place to pass configuration from `i18n/request.ts` to Client Components via `NextIntlClientProvider`.
181182

182183
```tsx filename="app/[locale]/layout.tsx"
183184
import {NextIntlClientProvider} from 'next-intl';
@@ -206,7 +207,7 @@ export default async function LocaleLayout({
206207
}
207208
```
208209

209-
Note that `NextIntlClientProvider` automatically inherits configuration from `i18n.ts` here, but `messages` need to be passed explicitly.
210+
Note that `NextIntlClientProvider` automatically inherits configuration from `i18n/request.ts` here, but `messages` need to be passed explicitly.
210211

211212
### `src/app/[locale]/page.tsx` [#page]
212213

@@ -216,7 +217,7 @@ Now you can use translations and other functionality from `next-intl` in your co
216217

217218
```tsx filename="app/[locale]/page.tsx"
218219
import {useTranslations} from 'next-intl';
219-
import {Link} from '@/routing';
220+
import {Link} from '@/i18n/routing';
220221

221222
export default function HomePage() {
222223
const t = useTranslations('HomePage');
@@ -264,11 +265,10 @@ When using the setup with i18n routing, `next-intl`will currently opt into dynam
264265
Since we are using a dynamic route segment for the `[locale]` param, we need to pass all possible values to Next.js via [`generateStaticParams`](https://nextjs.org/docs/app/api-reference/functions/generate-static-params) so that the routes can be rendered at build time.
265266

266267
```tsx filename="app/[locale]/layout.tsx"
267-
// Can be imported from a shared config
268-
const locales = ['en', 'de'];
268+
import {routing} from '@/i18n/routing';
269269

270270
export function generateStaticParams() {
271-
return locales.map((locale) => ({locale}));
271+
return routing.locales.map((locale) => ({locale}));
272272
}
273273
```
274274

@@ -306,7 +306,7 @@ export default function IndexPage({params: {locale}}) {
306306

307307
**Keep in mind that:**
308308

309-
1. The locale that you pass to `unstable_setRequestLocale` should be validated (e.g. in [`i18n.ts`](/docs/usage/configuration#i18nts)).
309+
1. The locale that you pass to `unstable_setRequestLocale` should be validated (e.g. in [`i18n/request.ts`](/docs/usage/configuration#i18n-request)).
310310

311311
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.
312312

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

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ Now, we're going to create the following file structure:
2828
│ └── ...
2929
├── next.config.mjs (2)
3030
└── src
31-
├── i18n.ts (3)
31+
├── i18n
32+
│ └── request.tsx (3)
3233
└── app
3334
├── layout.tsx (4)
3435
└── page.tsx (5)
@@ -87,11 +88,11 @@ module.exports = withNextIntl(nextConfig);
8788
</Tab>
8889
</Tabs>
8990

90-
### `i18n.ts` [#i18n-request]
91+
### `i18n/request.ts` [#i18n-request]
9192

9293
`next-intl` creates a request-scoped configuration object, which you can use to provide messages and other options based on the user's locale to Server Components.
9394

94-
```tsx filename="src/i18n.ts"
95+
```tsx filename="src/i18n/request.ts"
9596
import {getRequestConfig} from 'next-intl/server';
9697

9798
export default getRequestConfig(async () => {
@@ -101,15 +102,15 @@ export default getRequestConfig(async () => {
101102

102103
return {
103104
locale,
104-
messages: (await import(`../messages/${locale}.json`)).default
105+
messages: (await import(`../../messages/${locale}.json`)).default
105106
};
106107
});
107108
```
108109

109-
<Details id="move-i18n-request">
110+
<Details id="i18n-request-path">
110111
<summary>Can I move this file somewhere else?</summary>
111112

112-
This file is supported out-of-the-box as `./i18n.ts` both in the `src` folder as well as in the project root with the extensions `.ts`, `.tsx`, `.js` and `.jsx`.
113+
This file is supported out-of-the-box as `./i18n/request.ts` both in the `src` folder as well as in the project root with the extensions `.ts`, `.tsx`, `.js` and `.jsx`.
113114

114115
If you prefer to move this file somewhere else, you can optionally provide a path to the plugin:
115116

@@ -124,7 +125,7 @@ const withNextIntl = createNextIntlPlugin(
124125

125126
### `app/layout.tsx` [#layout]
126127

127-
The `locale` that was provided in `i18n.ts` is available via `getLocale` and can be used to configure the document language. Additionally, we can use this place to pass configuration from `i18n.ts` to Client Components via `NextIntlClientProvider`.
128+
The `locale` that was provided in `i18n/request.ts` is available via `getLocale` and can be used to configure the document language. Additionally, we can use this place to pass configuration from `i18n/request.ts` to Client Components via `NextIntlClientProvider`.
128129

129130
```tsx filename="app/layout.tsx"
130131
import {NextIntlClientProvider} from 'next-intl';
@@ -153,7 +154,7 @@ export default async function RootLayout({
153154
}
154155
```
155156

156-
Note that `NextIntlClientProvider` automatically inherits configuration from `i18n.ts` here, but `messages` need to be passed explicitly.
157+
Note that `NextIntlClientProvider` automatically inherits configuration from `i18n/request.ts` here, but `messages` need to be passed explicitly.
157158

158159
### `app/page.tsx` [#page]
159160

docs/pages/docs/routing.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ This enables you to express your app in terms of APIs like `<Link href="/about">
2222

2323
The routing configuration that is shared between the [middleware](/docs/routing/middleware) and [the navigation APIs](/docs/routing/navigation) can be defined with the `defineRouting` function.
2424

25-
```tsx filename="src/routing.ts"
25+
```tsx filename="src/i18n/routing.ts"
2626
import {defineRouting} from 'next-intl/routing';
2727

2828
export const routing = defineRouting({

0 commit comments

Comments
 (0)