feat(website): Next.js 16 marketing website with RTL/Farsi
Marketing website for Meezi platform: - Server-side rendered pages: home, demo, blog, pricing - RTL/Farsi layout with Vazirmatn font - SEO metadata and Open Graph tags - proxy.ts for Next.js 16 middleware convention - MEEZI_API_URL internal Docker network routing Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
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: { locale: string };
|
||||
}): Promise<Metadata> {
|
||||
const { locale } = await Promise.resolve(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"),
|
||||
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: { locale: string };
|
||||
}) {
|
||||
const { locale } = await Promise.resolve(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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user