Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into feat/esm-2
Browse files Browse the repository at this point in the history
# Conflicts:
#	package.json
#	packages/next-intl/package.json
#	packages/next-intl/src/navigation/createLocalizedPathnamesNavigation.test.tsx
#	packages/next-intl/src/navigation/createSharedPathnamesNavigation.test.tsx
#	packages/next-intl/src/navigation/react-client/ClientLink.test.tsx
#	packages/next-intl/src/navigation/react-client/useBasePathname.test.tsx
#	packages/next-intl/src/navigation/shared/redirects.tsx
#	packages/next-intl/src/navigation/shared/utils.tsx
#	packages/next-intl/src/react-client/useFormatter.test.tsx
#	packages/next-intl/src/react-client/useLocale.test.tsx
#	packages/next-intl/src/react-client/useNow.test.tsx
#	packages/next-intl/src/react-client/useTimeZone.test.tsx
#	packages/next-intl/src/react-client/useTranslations.test.tsx
#	packages/next-intl/src/runtimes/react-server/getTranslations.tsx
#	packages/next-intl/src/runtimes/react-server/getTranslator.tsx
#	packages/next-intl/src/server/react-client/index.test.tsx
#	packages/next-intl/src/shared/NextIntlClientProvider.test.tsx
#	packages/next-intl/src/shared/utils.test.tsx
#	packages/next-intl/src/shared/utils.tsx
#	packages/next-intl/test/navigation/shared/utils.test.tsx
#	packages/use-intl/package.json
#	pnpm-lock.yaml
  • Loading branch information
amannn committed Aug 21, 2024
2 parents ad0bdf1 + e47a693 commit 881d415
Show file tree
Hide file tree
Showing 140 changed files with 4,248 additions and 1,688 deletions.
54 changes: 54 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,60 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## 3.17.4 (2024-08-20)

### Bug Fixes

* Update `@formatjs/intl-localematcher` to latest version ([#1140](https://github.com/amannn/next-intl/issues/1140)) ([c217582](https://github.com/amannn/next-intl/commit/c217582cf47a3d0d65315e70eb9fd945efca7163)) – by @amannn

## 3.17.3 (2024-08-14)

### Bug Fixes

* Handle optional catch-all segments in navigation APIs if no value is provided and handle the case if a dynamic value appears multiple times in a pathname ([#1259](https://github.com/amannn/next-intl/issues/1259)) ([58ef482](https://github.com/amannn/next-intl/commit/58ef482eda383fc03a552a4f34b00c7b3136a4af)), closes [#1236](https://github.com/amannn/next-intl/issues/1236) – by @amannn

## 3.17.2 (2024-07-19)

### Bug Fixes

* Fix open redirect vulnerability for `localePrefix: 'as-necessary'` by sanitizing pathname in the middleware ([#1208](https://github.com/amannn/next-intl/issues/1208)) ([f42ac01](https://github.com/amannn/next-intl/commit/f42ac014c8a01124ab4eba46652a5224c5d7698e)), closes [#1207](https://github.com/amannn/next-intl/issues/1207) – by @hblee12294

## 3.17.1 (2024-07-15)

### Bug Fixes

* Apply `useMemo` for `useRouter` returned from `createLocalizedPathnamesNavigation` to keep a stable reference when possible ([#1201](https://github.com/amannn/next-intl/issues/1201)) ([a1b9a36](https://github.com/amannn/next-intl/commit/a1b9a3680b2a0d7f5b77f8571787ea8d66043852)), closes [#1198](https://github.com/amannn/next-intl/issues/1198) – by @amannn

## 3.17.0 (2024-07-12)

### Features

* Cache `Intl.*` constructors ([#1193](https://github.com/amannn/next-intl/issues/1193)) ([52c4f2c](https://github.com/amannn/next-intl/commit/52c4f2cede844c0ff3d2f73890dcfd75210bc1f2)), closes [#215](https://github.com/amannn/next-intl/issues/215) – by @amannn

## 3.16.0 (2024-07-11)

### Features

* Support `trailingSlash: true` in Next.js config ([#1190](https://github.com/amannn/next-intl/issues/1190)) ([cfbdee9](https://github.com/amannn/next-intl/commit/cfbdee990b367a968eca6258d5c2fcfe8ef2ff2d)) – by @amannn

## 3.15.5 (2024-07-09)

### Bug Fixes

* Support relative pathnames in `redirect` ([#1178](https://github.com/amannn/next-intl/issues/1178)) ([3b698d7](https://github.com/amannn/next-intl/commit/3b698d7abdb8859a43448381ba2361dee4b5e669)), closes [#1177](https://github.com/amannn/next-intl/issues/1177) – by @amannn

## 3.15.4 (2024-07-08)

### Bug Fixes

* Export `DomainsConfig` ([#1175](https://github.com/amannn/next-intl/issues/1175)) ([c4d1bb0](https://github.com/amannn/next-intl/commit/c4d1bb08e23cc8a726c3a52e6cee7b1c63cb4c8a)) – by @amannn

## 3.15.3 (2024-06-26)

### Bug Fixes

* Prefer more specific routes in `usePathname` when detecting the currently active pathname for localized pathnames ([#1152](https://github.com/amannn/next-intl/issues/1152)) ([936839e](https://github.com/amannn/next-intl/commit/936839e9508e447f3e60cc1f606258fd00e5227e)), closes [#1151](https://github.com/amannn/next-intl/issues/1151) – by @amannn

## 3.15.2 (2024-06-19)

**Note:** Version bump only for package root
Expand Down
4 changes: 2 additions & 2 deletions docs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"name": "docs",
"version": "2.14.3",
"private": true,
"scripts": {
"dev": "next dev",
Expand All @@ -11,6 +10,7 @@
"start": "next start"
},
"dependencies": {
"@docsearch/css": "^3.6.0",
"@docsearch/react": "^3.6.0",
"@heroicons/react": "^2.1.4",
"@vercel/analytics": "1.3.1",
Expand All @@ -32,7 +32,7 @@
"eslint-config-molindo": "^7.0.0",
"eslint-config-next": "^14.2.4",
"next-sitemap": "^4.2.3",
"typescript": "^5.4.5"
"typescript": "^5.5.3"
},
"prettier": "../.prettierrc.json"
}
12 changes: 6 additions & 6 deletions docs/pages/docs/design-principles.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Once internationalization is set up, adding a new language is in the simplest ca

## Standards-based

With the introduction of [the ECMAScript Internationalization API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl), JavaScript has gotten very capable in the recent years in regard to formatting of dates, times, numbers, lists and pluralization. `next-intl` builds on top of this API and provides an ergonomic interface to work with these features, while considering your app-specic configuration & needs.
With the introduction of [the ECMAScript Internationalization API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl), JavaScript has gotten very capable in the recent years in regard to formatting of dates, times, numbers, lists and pluralization. `next-intl` builds on top of this API and provides an ergonomic interface to work with these features, while considering your app-specific configuration & needs.

For text formatting, `next-intl` is based on [International Components for Unicode (ICU)](https://unicode-org.github.io/icu/userguide/format_parse/). ICU is a mature and widely used standard for internationalization that is supported by many programming languages and frameworks. `next-intl` uses the ICU message syntax for defining text labels, which allows to express complex formatting requirements like interpolating variables and pluralization in a concise and readable way—also for non-developers like translators.

Expand Down Expand Up @@ -94,18 +94,18 @@ Next.js has a rich ecosystem of libraries that can be used alongside `next-intl`

To achieve this, `next-intl` primarly relies on these techniques currently:

1. **Splitting of messages**: By splitting messages by locale, and optionally also by server, client and component, we can reduce the amount of messages that are sent to the client. This is especially important for apps that support many languages and have a large amount of messages.
2. **Shortcuts**: By detecting plain messages, these messages can be returned immediately without having to parse them first.
3. **Caching**: Parsing and formatting of messages is cached across your app, therefore reducing the amount of necessary computation.
4. **RSC-first**: By integrating deeply with React Server Components, we can offload work to a build step or a capable server, therefore reducing the runtime footprint of your app on the client side.
1. **RSC-first**: By integrating deeply with React Server Components, we can offload work to a build step or a capable server, therefore reducing the runtime footprint of your app on the client side.
2. **Splitting of messages**: By splitting messages by locale, and optionally also by server, client and component, we can reduce the amount of messages that are sent to the client. This is especially important for apps that support many languages and have a large amount of messages.
3. **Caching**: Parsing of messages, as well as the instantiation of [`Intl`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) constructors is cached across your app, therefore reducing the amount of necessary computation.
4. **Shortcuts**: By detecting plain messages, these messages can be returned immediately without having to parse them first.

[Planned enhancements](https://github.com/amannn/next-intl/labels/area%3A%20performance)

## Next.js-first

Next.js comes with a lot of bells and whistles, but at the same time there are a number of aspects that need to be handled carefully to enable a reliable internationalization integration. `next-intl`, as the name implies, is primarly designed to work well with Next.js. Rather than trying to be a one-size-fits-all solution, `next-intl` integrates with Next.js as deeply as necessary and makes it a priority to stay on top of the latest developments in the Next.js ecosystem.

That being said, `next-intl` has a Next.js-agnostic core that can be used in any React app, or even in plain JavaScript: [`use-intl`](/docs/environments/core-library). This core library contains most features of `next-intl`, but lacks Next.js-specific integrations like routing APIs. The goal of this library is to make it possibly to use familiar APIs in other parts of your stack (e.g. React Native) and to provide a straightforward way to [migrate away from Next.js](#migration-friendly), in case you ever decide to do so.
That being said, `next-intl` has a Next.js-agnostic core that can be used in any React app, or even in plain JavaScript: [`use-intl`](/docs/environments/core-library). This core library contains most features of `next-intl`, but lacks Next.js-specific integrations like routing APIs. The goal of this library is to make it possibly to use familiar APIs in other parts of your stack (e.g. React Native) and, in case you ever feel like Next.js is not the right fit for your project anymore, to provide a straightforward [migration path](#migration-friendly).

## Migration-friendly

Expand Down
1 change: 1 addition & 0 deletions docs/pages/docs/environments.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ The `next-intl` APIs are available in the following environments:
title="Error files (e.g. not-found)"
href="/docs/environments/error-files"
/>
<Card title="Markdown (MDX)" href="/docs/environments/mdx" />
<Card
title="Core library (agnostic)"
href="/docs/environments/core-library"
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,6 +2,7 @@
"server-client-components": "Server & Client Components",
"actions-metadata-route-handlers": "Server Actions, Metadata & Route Handlers",
"error-files": "Error files (e.g. not-found)",
"mdx": "Markdown (MDX)",
"core-library": "Core library",
"runtime-requirements": "Runtime requirements"
}
69 changes: 44 additions & 25 deletions docs/pages/docs/environments/actions-metadata-route-handlers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,7 @@ Next.js supports providing alternate URLs per language via the [`alternates` ent
<Tabs items={['Shared pathnames', 'Localized pathnames']}>
<Tab>

If you're use the same pathnames for all locales (i.e. you're not using the [`pathnames`](/docs/routing#pathnames) setting), you can iterate over an array of pathnames that your app supports and generate a sitemap entry for each pathname.

**Example:**
If you're use the same pathnames for all locales (i.e. you're not using the [`pathnames`](/docs/routing#pathnames) setting), you can assemble the sitemap based on the pathnames that your app supports.

```tsx
import {MetadataRoute} from 'next';
Expand All @@ -170,59 +168,80 @@ const defaultLocale = 'en' as const;
const locales = ['en', 'de'] as const;

// Adapt this as necessary
const pathnames = ['/', '/about'];
const host = 'https://acme.com';

export default function sitemap(): MetadataRoute.Sitemap {
function getUrl(pathname: string, locale: string) {
return `${host}/${locale}${pathname === '/' ? '' : pathname}`;
}
// Adapt this as necessary
return [
getEntry('/'),
getEntry('/users'),
getEntry('/users/1'),
getEntry('/users/2')
];
}

return pathnames.map((pathname) => ({
function getEntry(pathname: string) {
return {
url: getUrl(pathname, defaultLocale),
lastModified: new Date(),
alternates: {
languages: Object.fromEntries(
locales.map((locale) => [locale, getUrl(pathname, locale)])
)
}
}));
};
}

function getUrl(pathname: string, locale: string) {
return `${host}/${locale}${pathname === '/' ? '' : pathname}`;
}
```

</Tab>
<Tab>

If you're using the [`pathnames`](/docs/routing#pathnames) setting, you can use the keys of your already declared `pathnames` and generate an entry for each locale via the [`getPathname`](/docs/routing/navigation#getpathname) function.
If you're using the [`pathnames`](/docs/routing#pathnames) setting, you can generate sitemap entries for each locale by using the [`getPathname`](/docs/routing/navigation#getpathname) function.

```tsx
import {MetadataRoute} from 'next';
import {locales, pathnames, defaultLocale} from '@/config';
import {locales, defaultLocale} from '@/config';
import {getPathname} from '@/navigation';

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

export default function sitemap(): MetadataRoute.Sitemap {
const keys = Object.keys(pathnames) as Array<keyof typeof pathnames>;

function getUrl(
key: keyof typeof pathnames,
locale: (typeof locales)[number]
) {
const pathname = getPathname({locale, href: key});
return `${HOST}/${locale}${pathname === '/' ? '' : pathname}`;
}
// Adapt this as necessary
return [
getEntry('/'),
getEntry('/users'),
getEntry({
pathname: '/users/[id]',
params: {id: '1'}
}),
getEntry({
pathname: '/users/[id]',
params: {id: '2'}
})
];
}

return keys.map((key) => ({
url: getUrl(key, defaultLocale),
lastModified: new Date(),
type Href = Parameters<typeof getPathname>[0]['href'];

function getEntry(href: Href) {
return {
url: getUrl(href, defaultLocale),
alternates: {
languages: Object.fromEntries(
locales.map((locale) => [locale, getUrl(key, locale)])
locales.map((locale) => [locale, getUrl(href, locale)])
)
}
}));
};
}

function getUrl(href: Href, locale: (typeof locales)[number]) {
const pathname = getPathname({locale, href});
return `${host}/${locale}${pathname === '/' ? '' : pathname}`;
}
```

Expand Down
68 changes: 68 additions & 0 deletions docs/pages/docs/environments/mdx.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import Details from 'components/Details';
import PartnerContentLink from 'components/PartnerContentLink';

# Markdown (MDX)

Especially for sites where the content varies significantly by locale and may require a different structure, it can be helpful to use Markdown or [MDX](https://mdxjs.com) to provide your localized content. To consume this content in a Next.js app, you can use the [`@next/mdx`](https://nextjs.org/docs/app/building-your-application/configuring/mdx) package, which allows you to import and render MDX content.

While you can create entire pages using `page.mdx` files, in an app that uses [the `[locale]` segment](/docs/getting-started/app-router), it can be beneficial to import localized MDX content based on the user's locale into a single `page.tsx` file.

After following the [setup instructions for `@next/mdx`](https://nextjs.org/docs/app/building-your-application/configuring/mdx), you can consider placing your localized MDX files next to a page that will render them:

```
src
└── app
└── [locale]
├── page.tsx
├── en.mdx
└── de.mdx
```

Now, in `page.tsx`, you can import the MDX content based on the user's locale:

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

export default async function HomePage({params}) {
try {
const Content = (await import(`./${params.locale}.mdx`)).default;
return <Content />;
} catch (error) {
notFound();
}
}
```

In this example, an MDX file might look like this:

```mdx filename="src/app/[locale]/en.mdx"
import Portrait from '@/components/Portrait';

# Home

Welcome to my site!

<Portrait />
```

Components that invoke hooks from `next-intl` like `useTranslations` can naturally be used in MDX content and will respect the user's locale.

<Details id="rich-text">
<summary>Is MDX required to format rich text?</summary>

Not at all! The built in message formatting of `next-intl` supports [rich text syntax](/docs/usage/messages#rich-text), which can be used to provide formatting, and to embed React components within messages.

MDX is best suited for cases where content varies significantly by locale. If all you're looking for is rich text formatting, the built-in message formatting may be an easier choice.

</Details>

<Details id="remote-files">
<summary>Can I load MDX content from a remote source?</summary>

Especially if you'd like to allow translators to collaborate on MDX files, you can consider uploading them to a translation management system like <PartnerContentLink href="https://crowdin.com/">Crowdin</PartnerContentLink>.

In this case, you can fetch the MDX content dynamically from within a page and parse it using a package like [`next-mdx-remote`](https://nextjs.org/docs/app/building-your-application/configuring/mdx#remote-mdx).

Note that MDX compiles to JavaScript and is dynamically evaluated. You should be sure to only load MDX content from a trusted source, otherwise this can lead to [arbitrary code execution](https://en.wikipedia.org/wiki/Arbitrary_code_execution).

</Details>
37 changes: 4 additions & 33 deletions docs/pages/docs/environments/server-client-components.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import {useTranslations} from 'next-intl';
// Since this component doesn't use any interactive features
// from React, it can be run as a Server Component.

export default function Index() {
const t = useTranslations('Index');
export default function HomePage() {
const t = useTranslations('HomePage');
return <h1>{t('title')}</h1>;
}
```
Expand Down 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)
2 changes: 2 additions & 0 deletions docs/pages/docs/getting-started/app-router.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,5 @@ You can pick from two options when setting up an app with `next-intl` and Next.j
settings, or if your app only supports a single language
</Card>
</Cards>

To get a sense of what kind of setup you'd like to use, you can also explore the [example apps](/examples).
Loading

0 comments on commit 881d415

Please sign in to comment.