Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Adapt examples for rootParams #1619

Open
wants to merge 9 commits into
base: feat/support-rootparams
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/example-app-router-playground/messages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"hello": "Hallo {name}!"
},
"AsyncComponent": {
"basic": "AsyncComponent",
"basic": "AsyncComponent (de)",
"markup": "Markup with <important>bold content</important>",
"rich": "This is a <important>rich</important> text."
},
Expand Down
2 changes: 1 addition & 1 deletion examples/example-app-router-playground/next-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
8 changes: 4 additions & 4 deletions examples/example-app-router-playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,20 @@
"storybook": "storybook dev -p 6006"
},
"dependencies": {
"@mdx-js/react": "^3.0.1",
"@mdx-js/react": "^3.1.0",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"lodash": "^4.17.21",
"ms": "2.1.3",
"next": "^14.2.4",
"next": "15.1.1-canary.12",
"next-intl": "^3.0.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"zod": "^3.23.8"
},
"devDependencies": {
"@jest/globals": "^29.7.0",
"@mdx-js/loader": "^3.0.1",
"@next/mdx": "^14.2.5",
"@mdx-js/loader": "^3.1.0",
"@next/mdx": "15.1.1-canary.12",
"@playwright/test": "^1.48.1",
"@storybook/nextjs": "^8.2.9",
"@storybook/react": "^8.2.9",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import {Locale} from 'next-intl';
import {getLocale} from 'next-intl/server';

type Props = {
params: {
locale: Locale;
};
};

export default async function AboutPage({params}: Props) {
const Content = (await import(`./${params.locale}.mdx`)).default;
export default async function AboutPage() {
const locale = await getLocale();
const Content = (await import(`./${locale}.mdx`)).default;
return <Content />;
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
import {NextRequest, NextResponse} from 'next/server';
import {Locale} from 'next-intl';
import {getTranslations} from 'next-intl/server';

type Props = {
params: {
locale: Locale;
};
};

export async function GET(request: NextRequest, {params: {locale}}: Props) {
export async function GET(request: NextRequest) {
const name = request.nextUrl.searchParams.get('name');
if (!name) {
return new Response('Search param `name` was not provided.', {status: 400});
}

const t = await getTranslations({locale, namespace: 'ApiRoute'});
const t = await getTranslations('ApiRoute');
return NextResponse.json({message: t('hello', {name})});
}
34 changes: 17 additions & 17 deletions examples/example-app-router-playground/src/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {Metadata} from 'next';
import {notFound} from 'next/navigation';
import {Locale, NextIntlClientProvider, hasLocale} from 'next-intl';
import {NextIntlClientProvider, useLocale} from 'next-intl';
import {
getFormatter,
getNow,
Expand All @@ -11,18 +10,17 @@ import {ReactNode} from 'react';
import {routing} from '@/i18n/routing';
import Navigation from '../../components/Navigation';

type Props = {
children: ReactNode;
params: {locale: Locale};
};
export async function generateStaticParams() {
return routing.locales.map((locale) => ({locale}));
}

export const dynamicParams = false;

export async function generateMetadata({
params: {locale}
}: Omit<Props, 'children'>): Promise<Metadata> {
const t = await getTranslations({locale, namespace: 'LocaleLayout'});
const formatter = await getFormatter({locale});
const now = await getNow({locale});
const timeZone = await getTimeZone({locale});
export async function generateMetadata(): Promise<Metadata> {
const t = await getTranslations('LocaleLayout');
const formatter = await getFormatter();
const now = await getNow();
const timeZone = await getTimeZone();

return {
metadataBase: new URL('http://localhost:3000'),
Expand All @@ -35,10 +33,12 @@ export async function generateMetadata({
};
}

export default function LocaleLayout({children, params: {locale}}: Props) {
if (!hasLocale(routing.locales, locale)) {
notFound();
}
type Props = {
children: ReactNode;
};

export default function LocaleLayout({children}: Props) {
const locale = useLocale();

return (
<html lang={locale}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,34 @@
import {Metadata} from 'next';
import {Locale, useTranslations} from 'next-intl';
import {useTranslations} from 'next-intl';
import {getLocale} from 'next-intl/server';
import {use} from 'react';
import {getPathname} from '@/i18n/routing';

type Props = {
params: {
locale: Locale;
params: Promise<{
articleId: string;
};
}>;
};

export async function generateMetadata({params}: Props): Promise<Metadata> {
const {articleId} = await params;
const locale = await getLocale();

return {
alternates: {
canonical: getPathname({
href: {
pathname: '/news/[articleId]',
params: {articleId: params.articleId}
params: {articleId}
},
locale: params.locale
locale
})
}
};
}

export default function NewsArticle({params}: Props) {
const {articleId} = use(params);
const t = useTranslations('NewsArticle');
return <h1>{t('title', {articleId: params.articleId})}</h1>;
return <h1>{t('title', {articleId})}</h1>;
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import {ImageResponse} from 'next/og';
import {Locale} from 'next-intl';
import {getTranslations} from 'next-intl/server';

type Props = {
params: {
locale: Locale;
};
};

export default async function Image({params: {locale}}: Props) {
const t = await getTranslations({locale, namespace: 'OpenGraph'});
export default async function Image() {
const t = await getTranslations('OpenGraph');
return new ImageResponse(<div style={{fontSize: 128}}>{t('title')}</div>);
}
12 changes: 9 additions & 3 deletions examples/example-app-router-playground/src/app/[locale]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import Image from 'next/image';
import {useFormatter, useNow, useTimeZone, useTranslations} from 'next-intl';
import {use} from 'react';
import DropdownMenu from '@/components/DropdownMenu';
import RichText from '@/components/RichText';
import {Link} from '@/i18n/routing';
import AsyncComponent from '../../components/AsyncComponent';
import AsyncComponent, {
AsyncComponentGerman
} from '../../components/AsyncComponent';
import AsyncComponentWithNamespaceAndLocale from '../../components/AsyncComponentWithNamespaceAndLocale';
import AsyncComponentWithoutNamespace from '../../components/AsyncComponentWithoutNamespace';
import AsyncComponentWithoutNamespaceAndLocale from '../../components/AsyncComponentWithoutNamespaceAndLocale';
Expand All @@ -16,7 +19,7 @@ import MessagesAsPropsCounter from '../../components/client/01-MessagesAsPropsCo
import MessagesOnClientCounter from '../../components/client/02-MessagesOnClientCounter';

type Props = {
searchParams: Record<string, string>;
searchParams: Promise<Record<string, string>>;
};

export default function Index({searchParams}: Props) {
Expand Down Expand Up @@ -55,11 +58,14 @@ export default function Index({searchParams}: Props) {
</Link>
</div>
<ClientLink href="/">Link on client without provider</ClientLink>
<p data-testid="SearchParams">{JSON.stringify(searchParams, null, 2)}</p>
<p data-testid="SearchParams">
{JSON.stringify(use(searchParams), null, 2)}
</p>
<p data-testid="HasTitle">{JSON.stringify(t.has('title'))}</p>
<Image alt="" height={77} priority src="/assets/image.jpg" width={128} />
<AsyncComponent />
<AsyncComponentWithNamespaceAndLocale />
<AsyncComponentGerman />
<AsyncComponentWithoutNamespace />
<AsyncComponentWithoutNamespaceAndLocale />
<DropdownMenu />
Expand Down
17 changes: 0 additions & 17 deletions examples/example-app-router-playground/src/app/not-found.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ export default async function AsyncComponent() {
);
}

export async function AsyncComponentGerman() {
const t = await getTranslations({locale: 'de', namespace: 'AsyncComponent'});
return (
<p data-testid="AsyncComponentGerman" lang="de">
{t('basic')}
</p>
);
}

export async function TypeTest() {
const t = await getTranslations('AsyncComponent');

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import {getLocale, getTranslations} from 'next-intl/server';
import {getTranslations} from 'next-intl/server';

export default async function AsyncComponentWithNamespaceAndLocale() {
const locale = await getLocale();
const t = await getTranslations({locale, namespace: 'AsyncComponent'});
const t = await getTranslations('AsyncComponent');

return (
<div data-testid="AsyncComponentWithoutNamespaceAndLocale">
Expand All @@ -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');
Expand Down
Original file line number Diff line number Diff line change
@@ -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 (
<div data-testid="AsyncComponentWithoutNamespaceAndLocale">
Expand All @@ -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');
Expand Down
19 changes: 11 additions & 8 deletions examples/example-app-router-playground/src/i18n/request.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -30,15 +31,17 @@ 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 = headers().get('x-now');
const timeZone = headers().get('x-time-zone') ?? 'Europe/Vienna';
const headersList = await headers();
const now = headersList.get('x-now');
const timeZone = headersList.get('x-time-zone') ?? 'Europe/Vienna';
const localeMessages = (await import(`../../messages/${locale}.json`))
.default;
const messages = {...defaultMessages, ...localeMessages};
Expand Down
7 changes: 7 additions & 0 deletions examples/example-app-router-playground/tests/main.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,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');
Expand Down
2 changes: 1 addition & 1 deletion examples/example-app-router/next-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
2 changes: 1 addition & 1 deletion examples/example-app-router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
},
"dependencies": {
"clsx": "^2.1.1",
"next": "^14.2.4",
"next": "15.1.1-canary.12",
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: Revert once in stable

"next-intl": "^3.0.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
@@ -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() {
redirect('/en');
export default function RootRedirect() {
return redirect('/en');
}
Loading
Loading