Skip to content

Commit

Permalink
docs: Improve docs for passing non-serializable config to `NextIntlCl…
Browse files Browse the repository at this point in the history
…ientProvider` and further clarify inheritance of config options
  • Loading branch information
amannn committed Aug 14, 2024
1 parent aecb75c commit 88dfb10
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 65 deletions.
33 changes: 2 additions & 31 deletions docs/pages/docs/environments/server-client-components.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -368,35 +368,6 @@ The component accepts the following props that are not serializable:
2. [`getMessageFallback`](/docs/usage/configuration#error-handling)
3. Rich text elements for [`defaultTranslationValues`](/docs/usage/configuration#default-translation-values)

To configure these, you can wrap `NextIntlClientProvider` with another component that is marked with `'use client'` and defines the relevant props:
To configure these, you can wrap `NextIntlClientProvider` with another component that is marked with `'use client'` and defines the relevant props.

```tsx filename="MyCustomNextIntlClientProvider.tsx"
'use client';

import {NextIntlClientProvider} from 'next-intl';

export default function MyCustomNextIntlClientProvider({
locale,
timeZone,
now,
...rest
}) {
return (
<NextIntlClientProvider
// Define non-serializable props here
defaultTranslationValues={{
i: (text) => <i>{text}</i>
}}
// Make sure to forward these props to avoid markup mismatches
locale={locale}
timeZone={timeZone}
now={now}
{...props}
/>
);
}
```

By doing this, your custom provider will already be part of the client-side bundle and can therefore define and pass functions as props.

**Important:** Be sure to pass explicit `locale`, `formats`, `timeZone` and `now` props to `NextIntlClientProvider` in this case, since the props aren't automatically inherited from a Server Component when you import `NextIntlClientProvider` from a Client Component.
See: [How can I provide non-serializable props like `onError` to `NextIntlClientProvider`?](/docs/usage/configuration#nextintlclientprovider-non-serializable-props)
121 changes: 87 additions & 34 deletions docs/pages/docs/usage/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,80 @@ In contrast, these props can be provided as necessary:
3. `defaultTranslationValues`
4. `onError` and `getMessageFallback`

<Details id="nextintlclientprovider-non-serializable-props">
<summary>How can I provide non-serializable props like `onError` to `NextIntlClientProvider`?</summary>

React limits the types of props that can be passed to Client Components to the ones that are [serializable](https://react.dev/reference/rsc/use-client#serializable-types). Since `onError`, `getMessageFallback` and `defaultTranslationValues` can receive functions, these configuration options can't be automatically inherited by the client side.

In order to define these values, you can wrap `NextIntlClientProvider` with another component that is marked with `'use client'` and defines the relevant props:

```tsx filename="IntlProvider.tsx"
'use client';

import {NextIntlClientProvider} from 'next-intl';

export default function IntlProvider({
locale,
now,
timeZone,
messages,
formats
}) {
return (
<NextIntlClientProvider
// Define non-serializable props here
defaultTranslationValues={{
i: (text) => <i>{text}</i>
}}
onError={(error) => console.error(error)}
getMessageFallback={({namespace, key}) => `${namespace}.${key}`}
// Make sure to forward these props to avoid markup mismatches
locale={locale}
now={now}
timeZone={timeZone}
// Provide as necessary
messages={messages}
formats={formats}
/>
);
}
```

Once you have defined your client-side provider component, you can use it in a Server Component:

```tsx filename="layout.tsx"
import IntlProvider from './IntlProvider';
import {getLocale, getNow, getTimeZone, getMessages} from 'next-intl/server';

export default async function RootLayout({children}) {
const locale = await getLocale();
const now = await getNow();
const timeZone = await getTimeZone();
const messages = await getMessages();

return (
<html lang={locale}>
<body>
<NextIntlClientProvider
locale={locale}
now={now}
timeZone={timeZone}
messages={messages}
>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}
```

By doing this, your provider component will already be part of the client-side bundle and can therefore define and pass functions as props.

**Important:** Be sure to pass explicit `locale`, `timeZone` and `now` props to `NextIntlClientProvider` in this case, since these aren't automatically inherited from a Server Component when you import `NextIntlClientProvider` from a Client Component.

</Details>

## Messages

The most crucial aspect of internationalization is providing labels based on the user's language. The recommended workflow is to store your messages in your repository along with the code.
Expand Down Expand Up @@ -130,7 +204,7 @@ export default getRequestConfig(async ({locale}) => {
});
```

After messages are configured, they can be used via `useTranslations`.
After messages are configured, they can be used via [`useTranslations`](/docs/usage/messages#rendering-messages-with-usetranslations).

In case you require access to messages in a component, you can read them via `useMessages()` or `getMessages()` from your configuration:

Expand Down Expand Up @@ -363,6 +437,8 @@ export default getRequestConfig(async ({locale}) => {
});
```

Note that `formats` are not automatically inherited by Client Components. If you want to make this available in Client Components, you should provide the same configuration to [`NextIntlClientProvider`](#nextintlclientprovider).

</Tab>
<Tab>

Expand Down Expand Up @@ -396,7 +472,7 @@ export default getRequestConfig(async ({locale}) => {
</Tab>
</Tabs>

Usage in components:
Once you have `formats` set up, you can use them in your components via `useFormatter`:

```tsx
import {useFormatter} from 'next-intl';
Expand All @@ -410,7 +486,7 @@ function Component() {
}
```

Global formats for numbers, dates and times can be referenced in messages too.
Global formats for numbers, dates and times can be referenced in messages too:

```json filename="en.json"
{
Expand All @@ -432,7 +508,7 @@ function Component() {

## Default translation values

To achieve consistent usage of translation values and reduce redundancy, you can define a set of global default values. This configuration can also be used to apply consistent styling of commonly used rich text elements. In case you provide values at a specific call site of `t`, these will potentially override global defaults.
To achieve consistent usage of translation values and reduce redundancy, you can define a set of global default values. This configuration can also be used to apply consistent styling of commonly used rich text elements.

<Tabs items={['i18n.ts', 'Provider']}>
<Tab>
Expand All @@ -452,6 +528,8 @@ export default getRequestConfig(async ({locale}) => {
});
```

Note that `defaultTranslationValues` are not automatically inherited by Client Components. If you want to make this available in Client Components, you should provide the same configuration to [`NextIntlClientProvider`](#nextintlclientprovider).

</Tab>
<Tab>

Expand All @@ -466,39 +544,12 @@ export default getRequestConfig(async ({locale}) => {
</NextIntlClientProvider>
```

Note that `NextIntlClientProvider` is a Client Component, therefore if you render it from a Server Component, the props need to be [serializable](https://react.dev/reference/react/use-client#serializable-types) across the server/client boundary.

If you provide rich text element functions, this means that you should render the provider along with the configuration for `defaultTranslationValues` from a component that is marked with `'use client'`:

```tsx filename="Provider.tsx"
// Making this a Client Component allows us to provide
// non-serializable props to `NextIntlClientProvider`
'use client';

import {NextIntlClientProvider} from 'next-intl';

export default function Provider({messages, children}) {
return (
<NextIntlClientProvider
// Passing functions as props is fine here!
defaultTranslationValues={{
important: (chunks) => <b>{chunks}</b>
}}
// Forward messages from the server side
messages={messages}
>
{children}
</NextIntlClientProvider>
);
}
```

In this case, `messages` should be provided from a Server Component, potentially as returned from the [`useMessages`](/docs/usage/configuration#messages) hook.
Note that `NextIntlClientProvider` is a Client Component, therefore if you render it from a Server Component, the props need to be serializable across the server/client boundary (see: [How can I provide non-serializable props to `NextIntlClientProvider`](#nextintlclientprovider-non-serializable-props)).

</Tab>
</Tabs>

## Error handling
## Error handling (`onError` & `getMessageFallback`) [#error-handling]

By default, when a message fails to resolve or when the formatting failed, an error will be printed on the console. In this case `${namespace}.${key}` will be rendered instead to keep your app running.

Expand Down Expand Up @@ -537,6 +588,8 @@ export default getRequestConfig(async ({locale}) => {
});
```

Note that `onError` and `getMessageFallback` are not automatically inherited by Client Components. If you want to make this functionality available in Client Components, you should provide the same configuration to [`NextIntlClientProvider`](#nextintlclientprovider).

</Tab>
<Tab>

Expand Down Expand Up @@ -612,7 +665,7 @@ The returned value is resolved based on these priorities:
</Details>
<Details id="locale-pages-router">
<summary>I'm using the Pages Router, how can I provide the locale?</summary>
<summary>I'm using the Pages Router, how can I access the locale?</summary>
If you use [internationalized routing with the Pages Router](https://nextjs.org/docs/pages/building-your-application/routing/internationalization), you can receive the locale from the router in order to pass it to `NextIntlClientProvider`:
Expand Down

0 comments on commit 88dfb10

Please sign in to comment.