import type { Metadata, Viewport } from 'next'; import { Inter, JetBrains_Mono } from 'next/font/google'; import { notFound } from 'next/navigation'; import { NextIntlClientProvider } from 'next-intl'; import { getMessages, getTranslations } from 'next-intl/server'; import { Toaster } from 'sonner'; import { AuthProvider } from '@/components/providers/auth-provider'; import { NotificationsProvider } from '@/components/providers/notifications-provider'; import { QueryProvider } from '@/components/providers/query-provider'; import { ThemeProvider } from '@/components/providers/theme-provider'; import { WebVitals } from '@/components/providers/web-vitals'; import { JsonLd, generateWebsiteJsonLd } from '@/components/seo/json-ld'; import type { Locale } from '@/i18n/config'; import { routing } from '@/i18n/routing'; import '../globals.css'; const inter = Inter({ subsets: ['latin', 'vietnamese'], display: 'swap', variable: '--font-inter', }); const jetbrainsMono = JetBrains_Mono({ subsets: ['latin'], display: 'swap', variable: '--font-jetbrains-mono', }); const siteUrl = process.env['NEXT_PUBLIC_SITE_URL'] || 'https://goodgo.vn'; export const viewport: Viewport = { width: 'device-width', initialScale: 1, themeColor: '#15803d', }; export async function generateMetadata({ params, }: { params: Promise<{ locale: string }>; }): Promise { const { locale } = await params; const t = await getTranslations({ locale, namespace: 'metadata' }); return { metadataBase: new URL(siteUrl), title: { default: t('title'), template: '%s | GoodGo', }, description: t('description'), keywords: [ 'bất động sản', 'mua bán nhà đất', 'cho thuê nhà', 'goodgo', 'nhà đất việt nam', 'real estate vietnam', ], authors: [{ name: 'GoodGo' }], creator: 'GoodGo', openGraph: { type: 'website', locale: locale === 'vi' ? 'vi_VN' : 'en_US', url: siteUrl, siteName: 'GoodGo', title: t('ogTitle'), description: t('ogDescription'), images: [ { url: '/og-image.png', width: 1200, height: 630, alt: t('ogTitle'), }, ], }, twitter: { card: 'summary_large_image', title: t('ogTitle'), description: t('ogDescription'), images: ['/og-image.png'], }, robots: { index: true, follow: true, googleBot: { index: true, follow: true, 'max-video-preview': -1, 'max-image-preview': 'large', 'max-snippet': -1, }, }, }; } export function generateStaticParams() { return routing.locales.map((locale) => ({ locale })); } export default async function LocaleLayout({ children, params, }: { children: React.ReactNode; params: Promise<{ locale: string }>; }) { const { locale } = await params; // Validate locale if (!routing.locales.includes(locale as Locale)) { notFound(); } const messages = await getMessages(); const t = await getTranslations({ locale, namespace: 'common' }); return ( {t('skipToContent')} {children} ); }