Serve the site in English, Spanish and Arabic, add a captcha, make the message optional

- Move every route under app/[lang]; a proxy keeps English on unprefixed URLs and serves /es and /ar
- Interface strings and homepage copy per locale; content pages load a translated JSON when one exists
- Language switch in the header, hreflang alternates and a multilingual sitemap
- Right-to-left layout for Arabic with logical CSS properties, Noto Sans Arabic and no letter-spacing
- Self-hosted proof-of-work "I'm not a robot" check (no third-party account) replaces Turnstile
- The message field is optional; failed attempts refill the form instead of clearing it
This commit is contained in:
Alejandro Veras
2026-10-13 10:49:14 -04:00
parent 2f9b63ccac
commit 5bf472b735
65 changed files with 2386 additions and 756 deletions
+21 -2
View File
@@ -1,13 +1,32 @@
import type { Metadata } from "next";
import { locales, localize, ogLocale, type Locale } from "@/i18n/config";
import type { Page } from "./pages";
/** Canonical URL plus hreflang alternates for a locale-free path. */
export function localeAlternates(path: string, locale: Locale): Metadata["alternates"] {
return {
canonical: localize(path, locale),
languages: {
...Object.fromEntries(locales.map((l) => [l, localize(path, l)])),
"x-default": path,
},
};
}
export function pageMetadata(page: Page): Metadata {
const title = page.meta.title ?? "AI Growth Technologies";
const description = page.meta.description ?? undefined;
return {
title,
description,
alternates: { canonical: page.path },
openGraph: { type: "website", url: page.path, siteName: "AI Growth Technologies", title, description },
alternates: localeAlternates(page.path, page.locale),
openGraph: {
type: "website",
url: localize(page.path, page.locale),
siteName: "AI Growth Technologies",
locale: ogLocale[page.locale],
title,
description,
},
};
}