Files
meezi/web/website/src/app/[locale]/layout.tsx
T

121 lines
3.6 KiB
TypeScript
Raw Normal View History

import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { NextIntlClientProvider } from "next-intl";
import { getMessages, getTranslations } from "next-intl/server";
import { routing } from "@/i18n/routing";
import { LocaleHtml } from "@/components/locale-html";
const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL ?? "https://meezi.ir";
export async function generateMetadata({
params,
}: {
params: Promise<{ locale: string }>;
}): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: "meta" });
const ogLocale = locale === "fa" ? "fa_IR" : "en_US";
const canonicalBase = `${BASE_URL}/${locale}`;
return {
metadataBase: new URL(BASE_URL),
title: {
default: t("homeTitle"),
template: `%s | ${t("siteName")}`,
},
description: t("siteDescription"),
icons: {
icon: [
{ url: "/icon-32.png", sizes: "32x32", type: "image/png" },
{ url: "/icon-192.png", sizes: "192x192", type: "image/png" },
{ url: "/icon-512.png", sizes: "512x512", type: "image/png" },
],
shortcut: "/icon-32.png",
apple: "/icon-180.png",
},
keywords:
locale === "fa"
? ["میزی", "مدیریت کافه", "منوی دیجیتال", "سیستم POS", "نرم افزار رستوران", "QR کد کافه", "مدیریت رستوران ایران"]
: ["Meezi", "cafe management", "restaurant software", "QR menu", "POS system", "Iran cafe", "digital menu"],
authors: [{ name: "Meezi", url: BASE_URL }],
creator: "Meezi",
publisher: "Meezi",
openGraph: {
type: "website",
locale: ogLocale,
alternateLocale: locale === "fa" ? "en_US" : "fa_IR",
url: canonicalBase,
siteName: t("siteName"),
title: t("homeTitle"),
description: t("siteDescription"),
images: [
{
url: `${BASE_URL}/api/og?t=${encodeURIComponent(t("homeTitle"))}&s=${encodeURIComponent(t("siteDescription"))}&rtl=${locale === "fa" ? "1" : "0"}`,
width: 1200,
height: 630,
alt: t("siteName"),
},
],
},
twitter: {
card: "summary_large_image",
site: "@MeeziApp",
creator: "@MeeziApp",
title: t("homeTitle"),
description: t("siteDescription"),
images: [`${BASE_URL}/api/og?t=${encodeURIComponent(t("homeTitle"))}&s=${encodeURIComponent(t("siteDescription"))}&rtl=${locale === "fa" ? "1" : "0"}`],
},
alternates: {
canonical: canonicalBase,
languages: {
"fa": `${BASE_URL}/fa`,
"en": `${BASE_URL}/en`,
"x-default": `${BASE_URL}/fa`,
},
},
robots: {
index: true,
follow: true,
googleBot: {
index: true,
follow: true,
"max-video-preview": -1,
"max-image-preview": "large",
"max-snippet": -1,
},
},
verification: {
// Add Google Search Console verification token here when ready
// google: "your-token",
},
category: "technology",
};
}
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;
if (!routing.locales.includes(locale as "fa" | "en")) notFound();
const messages = await getMessages();
const dir = locale === "fa" ? "rtl" : "ltr";
return (
<>
<LocaleHtml locale={locale} dir={dir} />
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
</>
);
}