This commit is contained in:
Nông Đức Huy
2026-08-13 23:20:22 +07:00
parent 3e5d38ec18
commit 3d6b0e0d4e
145 changed files with 7817 additions and 801 deletions
@@ -0,0 +1,73 @@
import type { Metadata, Viewport } from 'next';
import { notFound } from 'next/navigation';
import { hasLocale, NextIntlClientProvider } from 'next-intl';
import { getTranslations, setRequestLocale } from 'next-intl/server';
import { LOCALE_TAGS, type Locale } from '@sport/types';
import { routing } from '@/i18n/routing';
import '@/styles/globals.css';
/**
* This is the root layout — there is no `app/layout.tsx`. Every route lives
* under `[locale]`, so the `<html lang>` attribute can be set from the resolved
* locale rather than hard-coded, which screen readers and translation tools
* both depend on.
*/
export function generateStaticParams() {
return routing.locales.map((locale) => ({ locale }));
}
export async function generateMetadata({
params,
}: {
params: Promise<{ locale: string }>;
}): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: 'meta' });
return {
title: { default: t('defaultTitle'), template: `%s | ${t('siteName')}` },
description: t('defaultDescription'),
metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL ?? 'http://localhost:3000'),
// Tells search engines these are translations of one another rather than
// duplicate content — the single most important i18n SEO signal.
alternates: {
canonical: '/',
languages: { vi: '/', en: '/en' },
},
};
}
export const viewport: Viewport = {
themeColor: '#111111',
width: 'device-width',
initialScale: 1,
};
export default async function LocaleLayout({
children,
params,
}: {
children: React.ReactNode;
params: Promise<{ locale: string }>;
}) {
const { locale } = await params;
if (!hasLocale(routing.locales, locale)) {
notFound();
}
// Required for static rendering: without it every page under this layout
// opts into dynamic rendering the moment it reads a translation.
setRequestLocale(locale);
return (
<html lang={LOCALE_TAGS[locale as Locale]} suppressHydrationWarning>
<body className="min-h-screen antialiased">
<NextIntlClientProvider>{children}</NextIntlClientProvider>
</body>
</html>
);
}