@@ -12,11 +11,10 @@ export default async function AsyncComponentWithNamespaceAndLocale() {
}
export async function TypeTest() {
- const locale = await getLocale();
- const t = await getTranslations({locale});
+ const t = await getTranslations();
// @ts-expect-error
- await getTranslations({locale, namespace: 'Unknown'});
+ await getTranslations('Unknown');
// @ts-expect-error
t('AsyncComponent.unknown');
diff --git a/examples/example-app-router-playground/src/components/AsyncComponentWithoutNamespaceAndLocale.tsx b/examples/example-app-router-playground/src/components/AsyncComponentWithoutNamespaceAndLocale.tsx
index 3986dacd9..1fd8b1d09 100644
--- a/examples/example-app-router-playground/src/components/AsyncComponentWithoutNamespaceAndLocale.tsx
+++ b/examples/example-app-router-playground/src/components/AsyncComponentWithoutNamespaceAndLocale.tsx
@@ -1,8 +1,7 @@
-import {getLocale, getTranslations} from 'next-intl/server';
+import {getTranslations} from 'next-intl/server';
export default async function AsyncComponentWithoutNamespaceAndLocale() {
- const locale = await getLocale();
- const t = await getTranslations({locale});
+ const t = await getTranslations();
return (
@@ -12,8 +11,7 @@ export default async function AsyncComponentWithoutNamespaceAndLocale() {
}
export async function TypeTest() {
- const locale = await getLocale();
- const t = await getTranslations({locale});
+ const t = await getTranslations();
// @ts-expect-error
t('AsyncComponent.unknown');
diff --git a/examples/example-app-router-playground/src/i18n/request.tsx b/examples/example-app-router-playground/src/i18n/request.tsx
index ee1fa2264..d237604ce 100644
--- a/examples/example-app-router-playground/src/i18n/request.tsx
+++ b/examples/example-app-router-playground/src/i18n/request.tsx
@@ -1,4 +1,5 @@
import {headers} from 'next/headers';
+import {unstable_rootParams as rootParams} from 'next/server';
import {Formats, hasLocale} from 'next-intl';
import {getRequestConfig} from 'next-intl/server';
import defaultMessages from '../../messages/en.json';
@@ -30,12 +31,13 @@ export const formats = {
}
} satisfies Formats;
-export default getRequestConfig(async ({requestLocale}) => {
- // Typically corresponds to the `[locale]` segment
- const requested = await requestLocale;
- const locale = hasLocale(routing.locales, requested)
- ? requested
- : routing.defaultLocale;
+export default getRequestConfig(async ({locale}) => {
+ if (!locale) {
+ const params = await rootParams();
+ locale = hasLocale(routing.locales, params.locale)
+ ? params.locale
+ : routing.defaultLocale;
+ }
const now = (await headers()).get('x-now');
const timeZone = (await headers()).get('x-time-zone') ?? 'Europe/Vienna';
diff --git a/examples/example-app-router-playground/tests/main.spec.ts b/examples/example-app-router-playground/tests/main.spec.ts
index 61d522dd3..f9b7f987d 100644
--- a/examples/example-app-router-playground/tests/main.spec.ts
+++ b/examples/example-app-router-playground/tests/main.spec.ts
@@ -681,6 +681,13 @@ it('can use async APIs in async components', async ({page}) => {
.getByText('AsyncComponent');
});
+it('can use an explicit locale in an async component', async ({page}) => {
+ await page.goto('/de');
+ await expect(page.getByTestId('AsyncComponentGerman')).toHaveText(
+ 'AsyncComponent (de)'
+ );
+});
+
it('supports custom prefixes', async ({page}) => {
await page.goto('/spain');
await expect(page).toHaveURL('/spain');
diff --git a/examples/example-app-router-playground/src/app/layout.tsx b/examples/example-app-router/src/app/(unlocalized)/layout.tsx
similarity index 54%
rename from examples/example-app-router-playground/src/app/layout.tsx
rename to examples/example-app-router/src/app/(unlocalized)/layout.tsx
index e05792cc7..b2b918bd7 100644
--- a/examples/example-app-router-playground/src/app/layout.tsx
+++ b/examples/example-app-router/src/app/(unlocalized)/layout.tsx
@@ -4,8 +4,7 @@ type Props = {
children: ReactNode;
};
-// Since we have a `not-found.tsx` page on the root, a layout file
-// is required, even if it's just passing children through.
export default function RootLayout({children}: Props) {
+ // No need for a layout, as this only renders a redirect
return children;
}
diff --git a/examples/example-app-router/src/app/page.tsx b/examples/example-app-router/src/app/(unlocalized)/page.tsx
similarity index 77%
rename from examples/example-app-router/src/app/page.tsx
rename to examples/example-app-router/src/app/(unlocalized)/page.tsx
index d5d37ccca..2e49dc5ba 100644
--- a/examples/example-app-router/src/app/page.tsx
+++ b/examples/example-app-router/src/app/(unlocalized)/page.tsx
@@ -1,6 +1,6 @@
import {redirect} from 'next/navigation';
// This page only renders when the app is built statically (output: 'export')
-export default function RootPage() {
+export default function RootRedirect() {
redirect('/en');
}
diff --git a/examples/example-app-router/src/app/[locale]/layout.tsx b/examples/example-app-router/src/app/[locale]/layout.tsx
index ab05d540c..0303f2d9d 100644
--- a/examples/example-app-router/src/app/[locale]/layout.tsx
+++ b/examples/example-app-router/src/app/[locale]/layout.tsx
@@ -1,15 +1,14 @@
-import {notFound} from 'next/navigation';
-import {Locale, hasLocale, NextIntlClientProvider} from 'next-intl';
-import {getTranslations, setRequestLocale} from 'next-intl/server';
+import {NextIntlClientProvider} from 'next-intl';
+import {getLocale, getTranslations} from 'next-intl/server';
import {ReactNode} from 'react';
import {clsx} from 'clsx';
import {Inter} from 'next/font/google';
import {routing} from '@/i18n/routing';
import Navigation from '@/components/Navigation';
+import '@/styles.css';
type Props = {
children: ReactNode;
- params: Promise<{locale: Locale}>;
};
const inter = Inter({subsets: ['latin']});
@@ -18,25 +17,18 @@ export function generateStaticParams() {
return routing.locales.map((locale) => ({locale}));
}
-export async function generateMetadata(props: Omit
) {
- const {locale} = await props.params;
+export const dynamicParams = false;
- const t = await getTranslations({locale, namespace: 'LocaleLayout'});
+export async function generateMetadata() {
+ const t = await getTranslations('LocaleLayout');
return {
title: t('title')
};
}
-export default async function LocaleLayout({children, params}: Props) {
- // Ensure that the incoming `locale` is valid
- const {locale} = await params;
- if (!hasLocale(routing.locales, locale)) {
- notFound();
- }
-
- // Enable static rendering
- setRequestLocale(locale);
+export default async function LocaleLayout({children}: Props) {
+ const locale = await getLocale();
return (
diff --git a/examples/example-app-router/src/app/[locale]/page.tsx b/examples/example-app-router/src/app/[locale]/page.tsx
index f640ce5ad..c3a9bd142 100644
--- a/examples/example-app-router/src/app/[locale]/page.tsx
+++ b/examples/example-app-router/src/app/[locale]/page.tsx
@@ -1,18 +1,7 @@
-import {Locale, useTranslations} from 'next-intl';
-import {setRequestLocale} from 'next-intl/server';
-import {use} from 'react';
+import {useTranslations} from 'next-intl';
import PageLayout from '@/components/PageLayout';
-type Props = {
- params: Promise<{locale: Locale}>;
-};
-
-export default function IndexPage({params}: Props) {
- const {locale} = use(params);
-
- // Enable static rendering
- setRequestLocale(locale);
-
+export default function IndexPage() {
const t = useTranslations('IndexPage');
return (
diff --git a/examples/example-app-router/src/app/[locale]/pathnames/page.tsx b/examples/example-app-router/src/app/[locale]/pathnames/page.tsx
index fad686586..ecadf9581 100644
--- a/examples/example-app-router/src/app/[locale]/pathnames/page.tsx
+++ b/examples/example-app-router/src/app/[locale]/pathnames/page.tsx
@@ -1,18 +1,7 @@
-import {Locale, useTranslations} from 'next-intl';
-import {setRequestLocale} from 'next-intl/server';
-import {use} from 'react';
+import {useTranslations} from 'next-intl';
import PageLayout from '@/components/PageLayout';
-type Props = {
- params: Promise<{locale: Locale}>;
-};
-
-export default function PathnamesPage({params}: Props) {
- const {locale} = use(params);
-
- // Enable static rendering
- setRequestLocale(locale);
-
+export default function PathnamesPage() {
const t = useTranslations('PathnamesPage');
return (
diff --git a/examples/example-app-router/src/app/layout.tsx b/examples/example-app-router/src/app/layout.tsx
deleted file mode 100644
index aae3d9f0c..000000000
--- a/examples/example-app-router/src/app/layout.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import {ReactNode} from 'react';
-import './styles.css';
-
-type Props = {
- children: ReactNode;
-};
-
-// Since we have a `not-found.tsx` page on the root, a layout file
-// is required, even if it's just passing children through.
-export default function RootLayout({children}: Props) {
- return children;
-}
diff --git a/examples/example-app-router/src/app/not-found.tsx b/examples/example-app-router/src/app/not-found.tsx
deleted file mode 100644
index 47de61028..000000000
--- a/examples/example-app-router/src/app/not-found.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-'use client';
-
-import Error from 'next/error';
-
-// This page renders when a route like `/unknown.txt` is requested.
-// In this case, the layout at `app/[locale]/layout.tsx` receives
-// an invalid value as the `[locale]` param and calls `notFound()`.
-
-export default function GlobalNotFound() {
- return (
-
-
- ;
-
-
- );
-}
diff --git a/examples/example-app-router/src/i18n/request.ts b/examples/example-app-router/src/i18n/request.ts
index 370fc6d0c..81ba3e509 100644
--- a/examples/example-app-router/src/i18n/request.ts
+++ b/examples/example-app-router/src/i18n/request.ts
@@ -1,12 +1,12 @@
+import {unstable_rootParams as rootParams} from 'next/server';
import {hasLocale} from 'next-intl';
import {getRequestConfig} from 'next-intl/server';
import {routing} from './routing';
-export default getRequestConfig(async ({requestLocale}) => {
- // Typically corresponds to the `[locale]` segment
- const requested = await requestLocale;
- const locale = hasLocale(routing.locales, requested)
- ? requested
+export default getRequestConfig(async () => {
+ const params = await rootParams();
+ const locale = hasLocale(routing.locales, params.locale)
+ ? params.locale
: routing.defaultLocale;
return {
diff --git a/examples/example-app-router/src/app/styles.css b/examples/example-app-router/src/styles.css
similarity index 100%
rename from examples/example-app-router/src/app/styles.css
rename to examples/example-app-router/src/styles.css
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6b3e1d6c8..b27593ab8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -349,7 +349,7 @@ importers:
version: 29.7.0
'@mdx-js/loader':
specifier: ^3.0.1
- version: 3.1.0(webpack@5.97.1(esbuild@0.24.2))
+ version: 3.1.0(acorn@8.14.0)(webpack@5.97.1(esbuild@0.24.2))
'@mdx-js/react':
specifier: ^3.0.1
version: 3.1.0(@types/react@18.3.12)(react@18.3.1)
@@ -358,7 +358,7 @@ importers:
version: 15.1.6
'@next/mdx':
specifier: ^15.0.0-rc.1
- version: 15.0.0-rc.1(@mdx-js/loader@3.1.0(webpack@5.97.1(esbuild@0.24.2)))(@mdx-js/react@3.1.0(@types/react@18.3.12)(react@18.3.1))
+ version: 15.0.0-rc.1(@mdx-js/loader@3.1.0(acorn@8.14.0)(webpack@5.97.1(esbuild@0.24.2)))(@mdx-js/react@3.1.0(@types/react@18.3.12)(react@18.3.1))
'@playwright/test':
specifier: ^1.48.1
version: 1.48.1
@@ -3350,9 +3350,6 @@ packages:
'@mdx-js/mdx@2.3.0':
resolution: {integrity: sha512-jLuwRlz8DQfQNiUCJR50Y09CGPq3fLtmtUQfVrj79E0JWu3dvsVcxVIcfhR5h0iXu+/z++zDrYeiJqifRynJkA==}
- '@mdx-js/mdx@3.0.1':
- resolution: {integrity: sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==}
-
'@mdx-js/mdx@3.1.0':
resolution: {integrity: sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw==}
@@ -12453,9 +12450,6 @@ packages:
remark-mdx@2.3.0:
resolution: {integrity: sha512-g53hMkpM0I98MU266IzDFMrTD980gNF3BJnkyFcmN+dD873mQeD5rdMO3Y2X+x8umQfbSE0PcoEDl7ledSA+2g==}
- remark-mdx@3.0.1:
- resolution: {integrity: sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA==}
-
remark-mdx@3.1.0:
resolution: {integrity: sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA==}
@@ -17726,13 +17720,14 @@ snapshots:
- supports-color
- typescript
- '@mdx-js/loader@3.1.0(webpack@5.97.1(esbuild@0.24.2))':
+ '@mdx-js/loader@3.1.0(acorn@8.14.0)(webpack@5.97.1(esbuild@0.24.2))':
dependencies:
- '@mdx-js/mdx': 3.0.1
+ '@mdx-js/mdx': 3.1.0(acorn@8.14.0)
source-map: 0.7.4
optionalDependencies:
webpack: 5.97.1(esbuild@0.24.2)
transitivePeerDependencies:
+ - acorn
- supports-color
'@mdx-js/mdx@2.3.0':
@@ -17757,34 +17752,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@mdx-js/mdx@3.0.1':
- dependencies:
- '@types/estree': 1.0.6
- '@types/estree-jsx': 1.0.5
- '@types/hast': 3.0.4
- '@types/mdx': 2.0.13
- collapse-white-space: 2.1.0
- devlop: 1.1.0
- estree-util-build-jsx: 3.0.1
- estree-util-is-identifier-name: 3.0.0
- estree-util-to-js: 2.0.0
- estree-walker: 3.0.3
- hast-util-to-estree: 3.1.0
- hast-util-to-jsx-runtime: 2.3.2
- markdown-extensions: 2.0.0
- periscopic: 3.1.0
- remark-mdx: 3.0.1
- remark-parse: 11.0.0
- remark-rehype: 11.1.1
- source-map: 0.7.4
- unified: 11.0.5
- unist-util-position-from-estree: 2.0.0
- unist-util-stringify-position: 4.0.0
- unist-util-visit: 5.0.0
- vfile: 6.0.3
- transitivePeerDependencies:
- - supports-color
-
'@mdx-js/mdx@3.1.0(acorn@8.14.0)':
dependencies:
'@types/estree': 1.0.6
@@ -17903,11 +17870,11 @@ snapshots:
dependencies:
fast-glob: 3.3.1
- '@next/mdx@15.0.0-rc.1(@mdx-js/loader@3.1.0(webpack@5.97.1(esbuild@0.24.2)))(@mdx-js/react@3.1.0(@types/react@18.3.12)(react@18.3.1))':
+ '@next/mdx@15.0.0-rc.1(@mdx-js/loader@3.1.0(acorn@8.14.0)(webpack@5.97.1(esbuild@0.24.2)))(@mdx-js/react@3.1.0(@types/react@18.3.12)(react@18.3.1))':
dependencies:
source-map: 0.7.4
optionalDependencies:
- '@mdx-js/loader': 3.1.0(webpack@5.97.1(esbuild@0.24.2))
+ '@mdx-js/loader': 3.1.0(acorn@8.14.0)(webpack@5.97.1(esbuild@0.24.2))
'@mdx-js/react': 3.1.0(@types/react@18.3.12)(react@18.3.1)
'@next/swc-android-arm-eabi@12.3.4':
@@ -30221,13 +30188,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
- remark-mdx@3.0.1:
- dependencies:
- mdast-util-mdx: 3.0.0
- micromark-extension-mdxjs: 3.0.0
- transitivePeerDependencies:
- - supports-color
-
remark-mdx@3.1.0:
dependencies:
mdast-util-mdx: 3.0.0