Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into feat/next-13-rsc
Browse files Browse the repository at this point in the history
# Conflicts:
#	examples/example-next-13/src/components/LocaleSwitcher.tsx
#	packages/next-intl/package.json
  • Loading branch information
amannn committed Jul 17, 2023
2 parents b423baa + 023d23a commit add3e94
Show file tree
Hide file tree
Showing 24 changed files with 382 additions and 43 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

# 2.18.0 (2023-07-17)


### Features

* Accept `locale` with `useRouter` APIs ([#409](https://github.com/amannn/next-intl/issues/409)) ([0fbb3c7](https://github.com/amannn/next-intl/commit/0fbb3c7cc9945eff40fe84ef433da172b909a8e6)), closes [#408](https://github.com/amannn/next-intl/issues/408) [#407](https://github.com/amannn/next-intl/issues/407) [#320](https://github.com/amannn/next-intl/issues/320)





## 2.17.5 (2023-07-07)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ const t = createTranslator({locale: 'en', messages});
t('hello', {name: 'world'});
```

There's currently a proposal to further simplify this use case, by offering a set of [new APIs that integrate with Server Components](/docs/getting-started/app-router-server-components#using-internationalization-outside-of-components) (currently in beta).
There's currently a proposal to further simplify this use case, by offering a set of [new APIs that integrate with Server Components](/docs/environments/metadata-route-handlers) (currently in beta).

## This seems familiar

Expand Down
2 changes: 1 addition & 1 deletion docs/pages/docs/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Configuration properties that you use across your Next.js app can be set globall

## Client- and Server Components [#client-server-components]

Depending on if you handle internationalization in [Client- or Server Components](/docs/environments/server-client-components#server-components-benefits), the configuration from `NextIntlClientProvider` or `i18n.ts` will be applied respectively.
Depending on if you handle [internationalization in Client- or Server Components](/docs/environments/server-client-components), the configuration from `NextIntlClientProvider` or `i18n.ts` will be applied respectively.

<VersionTabs defaultLabel="Provider" rscLabel="i18n.ts">
<Tab>
Expand Down
1 change: 1 addition & 0 deletions docs/pages/docs/environments/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"index": "Overview",
"server-client-components": "Server & Client Components",
"metadata-route-handlers": "Metadata & Route Handlers",
"error-files": "Error files (e.g. not-found)",
"core-library": "Core library"
}
146 changes: 146 additions & 0 deletions docs/pages/docs/environments/error-files.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import VersionTabs from 'components/VersionTabs';
import Callout from 'components/Callout';

# Internationalization in Next.js error files

The Next.js App Router's file convention provides two files that can be used for error handling:

1. [`not-found.js`](https://nextjs.org/docs/app/api-reference/file-conventions/not-found)
2. [`error.js`](https://nextjs.org/docs/app/api-reference/file-conventions/error)

This page provides practical guides for these cases.

<Callout>
Have a look at [the App Router example](/examples/app-router) to explore a
working app with error handling.
</Callout>

## `not-found.js`

Next.js renders the closest `not-found` page when a route segment calls the [`notFound` function](https://nextjs.org/docs/app/api-reference/functions/not-found). Since the `[locale]` segment is used to localize your app, the `not-found` page needs to be located within this route segment too.

```tsx filename="app/[locale]/not-found.tsx"
import {useTranslations} from 'next-intl';

export default function NotFoundPage() {
const t = useTranslations('NotFoundPage');
return <h1>{t('title')}</h1>;
}
```

Note however, that Next.js will only render this page when the `notFound` function is called from within a route, not for all unknown routes in general.

### Catching unknown routes

To catch unknown routes too, you can define a catch-all route that explicitly calls the `notFound` function.

```tsx filename="app/[locale]/[...rest]/page.tsx"
import {notFound} from 'next/navigation';

export default function CatchAllPage() {
notFound();
}
```

After this change, all requests that are matched within the `[locale]` segment will render the `not-found` page when an unknown route is encountered.

### Catching non-localized requests

When the user requests a route that is not matched by [the `next-intl` middleware](/docs/routing/middleware) (depending on your `matcher` config), there's no locale associated with the request (e.g. `/unknown.txt`). You can add a root `not-found` page to handle these cases too.

```tsx filename="app/not-found.tsx"
'use client';

import Error from 'next/error';

// Render the default Next.js 404 page when a route
// is requested that doesn't match the middleware and
// therefore doesn't have a locale associated with it.

export default function NotFound() {
return (
<html lang="en">
<body>
<Error statusCode={404} />
</body>
</html>
);
}
```

Note that a root layout file is required in this case, even if [it's just passing `children` through](https://github.com/amannn/next-intl/blob/main/examples/example-next-13/src/app/layout.tsx).

## `error.js`

When an `error` file is defined, Next.js creates [an error boundary within your layout](https://nextjs.org/docs/app/building-your-application/routing/error-handling#how-errorjs-works) that wraps pages accordingly to catch runtime errors:

<figure>
<div className="w-full lg:w-[500px]">
```tsx
<LocaleLayout>
<ErrorBoundary fallback={<Error />}>
<Page />
</ErrorBoundary>
</LocaleLayout>
```
</div>

<figcaption>
Schematic component hierarchy that Next.js creates internally.
</figcaption>

</figure>

Since the `error` file must be defined as a Client Component, you have to use [`NextIntlClientProvider`](/docs/configuration#client-server-components) to provide messages in case the `error` file renders.

If you've [set up `next-intl` to be used in Client Components](/docs/getting-started/app-router-client-components), this is already the case and there's no additional setup needed. If you're using [the Server Components beta](/docs/getting-started/app-router-server-components) though, you have to provide the relevant messages in the wrapping layout.

<figure>

```tsx filename="app/[locale]/layout.tsx"
import pick from 'lodash/pick';

export default async function LocaleLayout({children}) {
// ...
const messages = useMessages();

return (
<html lang={locale}>
<body>
<NextIntlClientProvider
locale={locale}
messages={pick(messages, 'Error')}
>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}
```

<figcaption>
Providing messages for the `error` file is only necessary when using [the
Server Components beta](/docs/getting-started/app-router-server-components).
</figcaption>

</figure>

Once `NextIntlClientProvider` is in place, you can use functionality from `next-intl` in the `error` file:

```tsx filename="app/[locale]/error.tsx"
'use client';

import {useTranslations} from 'next-intl';

export default function Error({error, reset}) {
const t = useTranslations('Error');

return (
<div>
<h1>{t('title')}</h1>
<button onClick={reset}>{t('retry')}</button>
</div>
);
}
```
7 changes: 7 additions & 0 deletions docs/pages/docs/environments/index.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
RectangleGroupIcon,
DocumentTextIcon,
BoltIcon,
CodeBracketIcon
} from '@heroicons/react/24/outline';
import {Card} from 'nextra-theme-docs';
Expand All @@ -22,6 +23,12 @@ The `next-intl` APIs are available in the following environments:
title="Metadata API & Route Handlers"
href="/docs/environments/metadata-route-handlers"
/>
<Card
arrow
icon={<BoltIcon />}
title="Error files (e.g. not-found)"
href="/docs/environments/error-files"
/>
<Card
arrow
icon={<CodeBracketIcon />}
Expand Down
33 changes: 22 additions & 11 deletions docs/pages/docs/environments/server-client-components.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,20 @@ This page contains background information about the advantages of moving interna

With the introduction of the App Router in Next.js 13, [React Server Components](https://nextjs.org/docs/getting-started/react-essentials) became publicly available. This new paradigm allows components that don’t require React’s interactive features, such as `useState` and `useEffect`, to remain server-side only.

```tsx filename="app/[locale]/page.tsx"
import {useTranslations} from 'next-intl';

export default function Index() {
const t = useTranslations('Index');
return <h1>{t('title')}</h1>;
}
```

## Benefits of handling i18n in Server Components [#server-components-benefits]

Moving internationalization to the server side unlocks new levels of performance, leaving the client side for interactive features.

<Callout emoji="" title="Benefits of server-side internationalization">
<Callout emoji="" title="Benefits of server-side internationalization:">
<ol className="ml-4 list-decimal">
<li>
Your messages never leave the server and don't need to be serialized for
Expand All @@ -33,11 +42,13 @@ Moving internationalization to the server side unlocks new levels of performance
</ol>
</Callout>

However, depending on your situation, you may need to handle internationalization in Client Components as well. This page discusses several strategies for handling this situation, in order of recommendation.
## Using internationalization in Client Components

Depending on your situation, you may need to handle internationalization in Client Components as well. There are several options for using translations or other functionality from `next-intl` in Client Components, listed here in order of recommendation.

## Option 1: Passing translations to Client Components
### Option 1: Passing translations to Client Components

If you need to use translations or other functionality from `next-intl` in Client Components, the best approach is to pass the processed labels as props or `children` from a Server Component.
The preferred approach is to pass the processed labels as props or `children` from a Server Component.

```tsx filename="[locale]/faq/page.tsx" {7-9}
import {useTranslations} from 'next-intl';
Expand Down Expand Up @@ -74,9 +85,9 @@ function Expandable({title, children}) {
}
```

As you can see, we can use interactive features from React like `useState` on translated content, even though the translation only runs on the server side.
As you see, we can use interactive features from React like `useState` on translated content, even though the translation only runs on the server side.

## Option 2: Moving state to the server side
### Option 2: Moving state to the server side

You might run into cases where you have dynamic state, such as pagination, that should be reflected in translated messages.

Expand All @@ -98,12 +109,12 @@ In particular, page and search params are often a great option because they offe
<Callout>
There's [an article on Smashing Magazine about using `next-intl` in Server
Components](https://www.smashingmagazine.com/2023/03/internationalization-nextjs-13-react-server-components)
which explores the usage of search params (specifically [the section about
adding
which explores the usage of search params through a real-world example
(specifically [the section about adding
interactivity](https://www.smashingmagazine.com/2023/03/internationalization-nextjs-13-react-server-components/#adding-interactivity-dynamic-ordering-of-photos)).
</Callout>

## Option 3: Providing individual messages
### Option 3: Providing individual messages

If you need to internalize dynamic state that can not be moved to the server side, you can wrap the respective components with `NextIntlClientProvider`.

Expand Down Expand Up @@ -138,11 +149,11 @@ export default function Counter() {
`NextIntlClientProvider` doesn't automatically inherit configuration from
`i18n.ts`, therefore make sure to provide all relevant props on the component.
If you're configuring non-serializable values like functions, you have to mark
the component that renders `NextIntlClientProvider` with `'use client';`
the component that renders `NextIntlClientProvider` with `'use client'`
([example](https://codesandbox.io/p/sandbox/next-intl-non-serializable-props-on-nextintlclientprovider-r0h2hi?file=%2Fsrc%2Fapp%2F%5Blocale%5D%2FNextIntlProvider.tsx)).
</Callout>

## Option 4: Providing all messages
### Option 4: Providing all messages

If you're building a highly dynamic app where most components use React's interactive features, you may prefer to make all messages available to Client Components.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Next.js 13 introduces support for [React Server Components](https://nextjs.org/d
## Current beta version

```
npm install [email protected].7
npm install [email protected].8
```

This beta version was tested with `[email protected]`.
Expand Down Expand Up @@ -208,7 +208,7 @@ export default function Index() {
}
```

2. The [APIs for using internationalization outside of components](#using-internationalization-outside-of-components) are integrated with static rendering. As a temporary solution, you can use these APIs in components too, but note that you have to "drill-down" the `locale` that is received via `params` to all components.
2. The [APIs for using internationalization outside of components](/docs/environments/metadata-route-handlers) are integrated with static rendering. As a temporary solution, you can use these APIs in components too, but note that you have to "drill-down" the `locale` that is received via `params` to all components. Furthermore, the returned `t` function is based on [the `createTranslator` API](/docs/environments/core-library), meaning that `t.rich` uses functions that accept and return strings.

```tsx filename="app/[locale]/page.tsx"
import {getTranslator} from 'next-intl/server';
Expand Down
35 changes: 28 additions & 7 deletions docs/pages/docs/routing/navigation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import Callout from 'components/Callout';

`next-intl` provides drop-in replacements for common Next.js navigation APIs that automatically handle the user locale behind the scenes.

### `Link`
## `Link`

This component wraps `next/link` and automatically prefixes the `href` with the current locale as necessary. If the default locale is matched, the `href` remains unchanged and no prefix is added.
This component wraps [`next/link`](https://nextjs.org/docs/app/api-reference/components/link) and automatically prefixes the `href` with the current locale as necessary. If the default locale is matched, the `href` remains unchanged and no prefix is added.

```tsx
import Link from 'next-intl/link';
Expand All @@ -18,9 +18,9 @@ import Link from 'next-intl/link';
<Link href="/" locale="de">Switch to German</Link>
```

### `useRouter`
## `useRouter`

If you need to navigate programmatically, e.g. in response to a form submission, `next-intl` provides a convience API that wraps `useRouter` from Next.js and automatically applies the locale of the user.
If you need to navigate programmatically, e.g. in an event handler, `next-intl` provides a convience API that wraps [`useRouter` from Next.js](https://nextjs.org/docs/app/api-reference/functions/use-router) and automatically applies the locale of the user.

```tsx
'use client';
Expand All @@ -31,9 +31,30 @@ const router = useRouter();

// When the user is on `/en`, the router will navigate to `/en/about`
router.push('/about');

// You can override the `locale` to switch to another language
router.push('/about', {locale: 'de'});
```

<details>
<summary>How can I change the locale for the current page?</summary>

By combining [`usePathname`](#usepathname) with [`useRouter`](#userouter), you can change the locale for the current page programmatically.

```tsx
'use client';

import {usePathname, useRouter} from 'next-intl/client';

const pathname = usePathname();
const router = useRouter();

router.replace(pathname, {locale: 'de'});
```

### `usePathname`
</details>

## `usePathname`

To retrieve the pathname without a potential locale prefix, you can call `usePathname`.

Expand All @@ -46,14 +67,14 @@ import {usePathname} from 'next-intl/client';
const pathname = usePathname();
```

### `redirect`
## `redirect`

<Callout type="warning">
This API is only available in [the Server Components
beta](/docs/getting-started/app-router-server-components).
</Callout>

If you want to interrupt the render of a Server Component and redirect to another page, you can invoke the `redirect` function from `next-intl/server`. This wraps [the `redirect` function from Next.js](https://nextjs.org/docs/app/api-reference/functions/redirect) and automatically applies the current locale.
If you want to interrupt the render and redirect to another page, you can invoke the `redirect` function from `next-intl/server`. This wraps [the `redirect` function from Next.js](https://nextjs.org/docs/app/api-reference/functions/redirect) and automatically applies the current locale.

```tsx {1, 8}
import {redirect} from 'next-intl/server';
Expand Down
7 changes: 7 additions & 0 deletions docs/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ figure {
@apply my-8 flex flex-col items-center;
}

figure .nextra-code-block {
@apply w-full;
}
figure .nextra-code-block > pre {
@apply mb-0;
}

figcaption {
@apply mt-4 text-center text-sm text-slate-500;
}
Expand Down
Loading

0 comments on commit add3e94

Please sign in to comment.