From 88dfb10ee3d20745bd61426c9cc1427b1d0da406 Mon Sep 17 00:00:00 2001 From: Jan Amann Date: Wed, 14 Aug 2024 16:27:20 +0200 Subject: [PATCH] docs: Improve docs for passing non-serializable config to `NextIntlClientProvider` and further clarify inheritance of config options --- .../environments/server-client-components.mdx | 33 +---- docs/pages/docs/usage/configuration.mdx | 121 +++++++++++++----- 2 files changed, 89 insertions(+), 65 deletions(-) diff --git a/docs/pages/docs/environments/server-client-components.mdx b/docs/pages/docs/environments/server-client-components.mdx index 196dba788..24f29beb8 100644 --- a/docs/pages/docs/environments/server-client-components.mdx +++ b/docs/pages/docs/environments/server-client-components.mdx @@ -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 ( - {text} - }} - // 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) diff --git a/docs/pages/docs/usage/configuration.mdx b/docs/pages/docs/usage/configuration.mdx index 5d3aed051..5e93245a5 100644 --- a/docs/pages/docs/usage/configuration.mdx +++ b/docs/pages/docs/usage/configuration.mdx @@ -99,6 +99,80 @@ In contrast, these props can be provided as necessary: 3. `defaultTranslationValues` 4. `onError` and `getMessageFallback` +
+How can I provide non-serializable props like `onError` to `NextIntlClientProvider`? + +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 ( + {text} + }} + 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 ( + + + + {children} + + + + ); +} +``` + +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. + +
+ ## 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. @@ -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: @@ -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). + @@ -396,7 +472,7 @@ export default getRequestConfig(async ({locale}) => { -Usage in components: +Once you have `formats` set up, you can use them in your components via `useFormatter`: ```tsx import {useFormatter} from 'next-intl'; @@ -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" { @@ -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. @@ -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). + @@ -466,39 +544,12 @@ export default getRequestConfig(async ({locale}) => {
``` -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 ( - {chunks} - }} - // Forward messages from the server side - messages={messages} - > - {children} - - ); -} -``` - -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)). -## 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. @@ -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). + @@ -612,7 +665,7 @@ The returned value is resolved based on these priorities:
-I'm using the Pages Router, how can I provide the locale? +I'm using the Pages Router, how can I access the locale? 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`: