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 16 commits into
base: v4
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
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
import {Locale} from 'next-intl';
import {getLocale} from 'next-intl/server';

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

export default async function AboutPage({params}: Props) {
const {locale} = await params;
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,22 +1,12 @@
import {NextRequest, NextResponse} from 'next/server';
import {Locale} from 'next-intl';
import {getTranslations} from 'next-intl/server';

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

export async function GET(request: NextRequest, props: Props) {
const params = await props.params;
const {locale} = params;

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})});
}
40 changes: 17 additions & 23 deletions examples/example-app-router-playground/src/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,29 @@
import {Metadata} from 'next';
import {Inter} from 'next/font/google';
import {notFound} from 'next/navigation';
import {Locale, NextIntlClientProvider, hasLocale} from 'next-intl';
import {NextIntlClientProvider, useLocale} from 'next-intl';
import {
getFormatter,
getNow,
getTimeZone,
getTranslations
} from 'next-intl/server';
import {ReactNode} from 'react';
import {Inter} from 'next/font/google';
import {routing} from '@/i18n/routing';
import Navigation from '../../components/Navigation';

type Props = {
children: ReactNode;
params: Promise<{locale: Locale}>;
};

const inter = Inter({subsets: ['latin']});

export async function generateMetadata(
props: Omit<Props, 'children'>
): Promise<Metadata> {
const params = await props.params;
const {locale} = params;
export async function generateStaticParams() {
return routing.locales.map((locale) => ({locale}));
}

export const dynamicParams = false;

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 @@ -41,13 +36,12 @@ export async function generateMetadata(
};
}

export default async function LocaleLayout({params, children}: Props) {
const {locale} = await params;
type Props = {
children: ReactNode;
};

// Ensure that the incoming `locale` is valid
if (!hasLocale(routing.locales, locale)) {
notFound();
}
export default function LocaleLayout({children}: Props) {
const locale = useLocale();

return (
<html className={inter.className} lang={locale}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
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/navigation';

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

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

return {
alternates: {
Expand All @@ -26,8 +27,8 @@ export async function generateMetadata({params}: Props): Promise<Metadata> {
};
}

export default function NewsArticle(props: Props) {
const {articleId} = use(props.params);
export default function NewsArticle({params}: Props) {
const {articleId} = use(params);
const t = useTranslations('NewsArticle');
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>);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import {use} from 'react';
import DropdownMenu from '@/components/DropdownMenu';
import RichText from '@/components/RichText';
import {Link} from '@/i18n/navigation';
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 Down Expand Up @@ -62,6 +64,7 @@ export default function Index(props: Props) {
<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
14 changes: 8 additions & 6 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,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';
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 @@ -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');
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() {
export default function RootRedirect() {
redirect('/en');
}
24 changes: 8 additions & 16 deletions examples/example-app-router/src/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
@@ -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']});
Expand All @@ -18,25 +17,18 @@ export function generateStaticParams() {
return routing.locales.map((locale) => ({locale}));
}

export async function generateMetadata(props: Omit<Props, 'children'>) {
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 (
<html className="h-full" lang={locale}>
Expand Down
15 changes: 2 additions & 13 deletions examples/example-app-router/src/app/[locale]/page.tsx
Original file line number Diff line number Diff line change
@@ -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 (
Expand Down
Loading
Loading