diff --git a/docs/pages/blog/translations-outside-of-react-components.mdx b/docs/pages/blog/translations-outside-of-react-components.mdx index 608337aee..3280699c8 100644 --- a/docs/pages/blog/translations-outside-of-react-components.mdx +++ b/docs/pages/blog/translations-outside-of-react-components.mdx @@ -122,7 +122,7 @@ import {getTranslations} from 'next-intl/server'; const locale = params.locale; // This creates the same function that is returned by `useTranslations`. -const t = await getTranslations(locale); +const t = await getTranslations({locale}); // Result: "Hello world!" t('hello', {name: 'world'}); diff --git a/docs/pages/docs/environments/error-files.mdx b/docs/pages/docs/environments/error-files.mdx index c98b576fb..52a0d2828 100644 --- a/docs/pages/docs/environments/error-files.mdx +++ b/docs/pages/docs/environments/error-files.mdx @@ -41,7 +41,7 @@ export default function CatchAllPage() { } ``` -After this change, all requests that are matched within the `[locale]` segment will render the `not-found` page when an unknown route is encountered. +After this change, all requests that are matched within the `[locale]` segment will render the `not-found` page when an unknown route is encountered (e.g. `/en/unknown`). ### Catching non-localized requests @@ -54,6 +54,7 @@ You can add a root `not-found` page to handle these cases too. import {redirect, usePathname} from 'next/navigation'; +// Can be imported from a shared config const defaultLocale = 'en'; export default function NotFound() { diff --git a/docs/pages/docs/environments/metadata-route-handlers.mdx b/docs/pages/docs/environments/metadata-route-handlers.mdx index fa8bc0c8f..cc18a4c52 100644 --- a/docs/pages/docs/environments/metadata-route-handlers.mdx +++ b/docs/pages/docs/environments/metadata-route-handlers.mdx @@ -27,6 +27,12 @@ export async function generateMetadata({params: {locale}}) { } ``` + + By passing an explicit `locale` to the awaitable functions from `next-intl`, + you can make the metadata handler eligable for [static + rendering](/docs/getting-started/app-router#static-rendering). + + ### Metadata files If you need to internationalize content within [metadata files](https://nextjs.org/docs/app/api-reference/file-conventions/metadata), such as an Open Graph image, you can call APIs from `next-intl` in the exported function. diff --git a/docs/pages/docs/environments/server-client-components.mdx b/docs/pages/docs/environments/server-client-components.mdx index 7135c1d24..f2ef00cff 100644 --- a/docs/pages/docs/environments/server-client-components.mdx +++ b/docs/pages/docs/environments/server-client-components.mdx @@ -113,7 +113,9 @@ The one restriction that currently comes with this pattern is that hooks can not
Should I use async or non-async functions for my components? -If you implement components that qualify as shared components, it can be beneficial to implement them as non-async functions. This allows to use these components either in a server or client environment, making them really flexible. Even if you don't intend to to ever run a particular component on the client side, this compatibility can still be helpful, e.g. for simplified testing. However, there's no need to dogmatically use non-async functions exclusively for handling internationalization—use what fits your app best. +If you implement components that qualify as shared components, it can be beneficial to implement them as non-async functions. This allows to use these components either in a server or client environment, making them really flexible. Even if you don't intend to to ever run a particular component on the client side, this compatibility can still be helpful, e.g. for simplified testing. + +However, there's no need to dogmatically use non-async functions exclusively for handling internationalization—use what fits your app best.
diff --git a/docs/pages/docs/getting-started/app-router.mdx b/docs/pages/docs/getting-started/app-router.mdx index 7687f6404..00d531226 100644 --- a/docs/pages/docs/getting-started/app-router.mdx +++ b/docs/pages/docs/getting-started/app-router.mdx @@ -7,9 +7,13 @@ The Next.js App Router introduces support for [React Server Components](https:// ## Getting started -If you haven't done so already, [create a Next.js app that uses the App Router](https://nextjs.org/docs/getting-started/installation). All pages should be moved within a `[locale]` folder so that we can use this segment to provide content in different languages (e.g. `/en`, `/en/about`, etc.). +If you haven't done so already, [create a Next.js app that uses the App Router](https://nextjs.org/docs/getting-started/installation). -**Start by running `npm install next-intl` and create the following file structure:** +`next-intl` integrates with the App Router, by using a `[locale]` [dynamic segment](https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes) so that we can use this segment to provide content in different languages (e.g. `/en`, `/en/about`, etc.). + +Let's get started! + +**Run `npm install next-intl` and create the following file structure:** ``` ├── messages (1) @@ -45,7 +49,7 @@ The simplest option is to add JSON files in your project based on locales, e.g. ### `next.config.js` -Now, set up the plugin which provides i18n configuration for Server Components. +Now, set up the plugin which creates an alias to import your i18n configuration (specified in the next step) into Server Components. ```js filename="next.config.js" const withNextIntl = require('next-intl/plugin')(); @@ -89,7 +93,7 @@ module.exports = withNextIntl({ ### `middleware.ts` -[The middleware](/docs/routing/middleware) matches a locale for the request and handles redirects and rewrites accordingly. +The middleware matches a locale for the request and handles redirects and rewrites accordingly. ```tsx filename="middleware.ts" import createMiddleware from 'next-intl/middleware'; @@ -115,6 +119,7 @@ The `locale` that was matched by the middleware is available via the `locale` pa ```tsx filename="app/[locale]/layout.tsx" import {notFound} from 'next/navigation'; +// Can be imported from a shared config const locales = ['en', 'de']; export default function LocaleLayout({children, params: {locale}}) { @@ -166,12 +171,8 @@ That's all it takes! navigation docs](/docs/routing/navigation).
  • - Wondering how to link between internationalized pages? Have a look at [the - navigation docs](/docs/routing/navigation). -
  • -
  • - Are you transitioning from the Pages Router to the App Router? Check out the - migration example. + Are you transitioning from the Pages to the App Router? Check out [the + migration example](/examples/app-router-migration).
  • @@ -240,13 +241,13 @@ export default function IndexPage({ **Important:** `unstable_setRequestLocale` needs to be called after the `locale` is validated, but before you call any hooks from `next-intl`. Otherwise, you'll get an error when trying to prerender the page. +Note that Next.js can render layouts and pages indepently. This means that e.g. when you navigate from `/settings/profile` to `/settings/privacy`, the `/settings` segment might not re-render as part of the request. Due to this, it's important that `unstable_setRequestLocale` is called not only in the parent `settings/layout.tsx`, but also in the individual pages `profile/page.tsx` and `privacy/page.tsx`. +
    What does "unstable" mean? `unstable_setRequestLocale` is meant to be used as a stopgap solution and we aim to remove it in the future [in case Next.js adds an API to access parts of the URL](https://github.com/facebook/react/pull/27424#issuecomment-1739464985). If that's the case, you'll get a deprecation notice in a minor version and the API will be removed as part of a major version. -Note that Next.js can render layouts and pages indepently. This means that e.g. when you navigate from `/settings/profile` to `/settings/privacy`, the `/settings` segment might not re-render as part of the request. Due to this, it's important that `unstable_setRequestLocale` is called not only in the parent `settings/layout.tsx`, but also in the individual pages `profile/page.tsx` and `privacy/page.tsx`. - That being said, the API is expected to work reliably if you're cautious to apply it in all relevant places.
    diff --git a/docs/pages/docs/usage/messages.mdx b/docs/pages/docs/usage/messages.mdx index 83f725eeb..836fdb72d 100644 --- a/docs/pages/docs/usage/messages.mdx +++ b/docs/pages/docs/usage/messages.mdx @@ -7,12 +7,12 @@ The main part of handling internationalization (typically referred to as _i18n_) ## Terminology -- **Locale**: We use this term to describe an identifier that contains the language and formatting preferences of users. Apart from the language, this includes optional regional information (e.g. `en-US`). -- **Messages**: These are collections of namespace-label pairs that provide grouping by locale (e.g. `en-US.json`). +- **Locale**: We use this term to describe an identifier that contains the language and formatting preferences of users. Apart from the language, a locale can include optional regional information (e.g. `en-US`). Locales are specified as [IETF BCP 47 language tags](https://en.wikipedia.org/wiki/IETF_language_tag). +- **Messages**: These are collections of namespace-label pairs that are grouped by locale (e.g. `en-US.json`). ## Structuring messages -To group your messages within a locale, it's recommended to use component names as namespaces and embrace them as the primary unit of code organization in your app. +To group your messages within a locale, it's recommended to use component names as namespaces and embrace them as the primary unit of code organization in your app. You can of course also use a different structure, depending on what suits your app best. ```json filename="en.json" { @@ -22,9 +22,9 @@ To group your messages within a locale, it's recommended to use component names } ``` -Now, you can render messages from within a React component via the `useTranslations` hook: +You can render messages from within a React component via the `useTranslations` hook: -```js filename="About.tsx" +```tsx filename="About.tsx" import {useTranslations} from 'next-intl'; function About() { @@ -60,7 +60,7 @@ Optionally, you can structure your messages as nested objects. } ``` -```js filename="SignUp.tsx" +```tsx filename="SignUp.tsx" import {useTranslations} from 'next-intl'; function SignUp() { @@ -117,9 +117,7 @@ export default function useLocaleLabel() {
    How can I use translations outside of components? -`next-intl` is heavily based on the `useTranslations` API which is intended to consume translations from within React components. - -This may seem like an unnecessary limitation, but this is intentional and aims to encourage the use of proven patterns that avoid potential issues—especially if they are easy to overlook. +`next-intl` is heavily based on the `useTranslations` API which is intended to consume translations from within React components. This may seem like a limitation, but this is intentional and aims to encourage the use of proven patterns that avoid potential issues—especially if they are easy to overlook. If you're interested to dive deeper into this topic, there's a blog post that discusses the background of this design decision: [How (not) to use translations outside of React components](/blog/translations-outside-of-react-components). @@ -203,7 +201,7 @@ To match a specific number, `next-intl` additionally supports the special `=valu To apply pluralization based on an order of items, the `selectordinal` argument can be used: -```js filename="en.json" +```tsx filename="en.json" "message": "It's your {year, selectordinal, one {#st} two {#nd} few {#rd} other {#th}} birthday!" ``` @@ -235,7 +233,7 @@ To match a specific number, `next-intl` additionally supports the special `=valu To map identifiers to human readable labels, you can use the `select` argument: -```js filename="en.json" +```tsx filename="en.json" "message": "{gender, select, female {She} male {He} other {They}} is online." ``` @@ -296,7 +294,7 @@ Messages can use tags without any chunks as children, but syntax-wise, a closing ```js t.rich('message', { - br: (chunks) =>
    + br: () =>
    }); ``` @@ -385,56 +383,43 @@ If you need to render a list of messages, the recommended approach is to map an ```json filename="en.json" { - "Benefits": { - "zero-config": "Works with zero config", - "customizable": "Easy to customize", - "fast": "Blazingly fast" + "CompanyStats": { + "yearsOfService": { + "title": "Years of service", + "value": "34" + }, + "happyClients": { + "title": "Happy clients", + "value": "1.000+" + }, + "partners": { + "title": "Products", + "value": "5.000+" + } } } ``` -```js filename="Benefits.tsx" +```tsx filename="CompanyStats.tsx" import {useTranslations} from 'next-intl'; -function Benefits() { - const t = useTranslations('Benefits'); - const keys = ['zero-config', 'customizable', 'fast'] as const; +function CompanyStats() { + const t = useTranslations('CompanyStats'); + const keys = ['yearsOfService', 'happyClients', 'partners'] as const; return ( ); } ``` -If the number of items varies between locales, you can solve this by using [rich text](#rich-text). - -```json filename="en.json" -{ - "Benefits": { - "items": "Works with zero configEasy to customizeBlazingly fast" - } -} -``` - -```js filename="Benefits.tsx" -import {useTranslations} from 'next-intl'; - -function Benefits() { - const t = useTranslations('Benefits'); - return ( - - ); -} -``` -
    Why can't I just use arrays in my messages? @@ -450,7 +435,7 @@ The advantage of this approach over supporting arrays in messages is that this w } ``` -```js filename="Benefits.tsx" +```tsx filename="Benefits.tsx" import {useTranslations} from 'next-intl'; function Benefits() { diff --git a/docs/pages/examples/_meta.json b/docs/pages/examples/_meta.json index 405167b90..4a83ca6f4 100644 --- a/docs/pages/examples/_meta.json +++ b/docs/pages/examples/_meta.json @@ -7,8 +7,8 @@ "title": "Pages Router", "theme": {"layout": "full"} }, - "pages-router-advanced": { - "title": "Pages Router (advanced)", + "app-router-migration": { + "title": "App Router migration", "theme": {"layout": "full"} } } diff --git a/docs/pages/examples/pages-router-advanced.mdx b/docs/pages/examples/pages-router-advanced.mdx deleted file mode 100644 index 8fc9e6fea..000000000 --- a/docs/pages/examples/pages-router-advanced.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: Pages Router (advanced) -full: true ---- - -import CodeSandbox from 'components/CodeSandbox'; - -