From 7f4277d1d760bc983354bdbd620eb498ad6e17d7 Mon Sep 17 00:00:00 2001 From: MOH Date: Sat, 7 Mar 2026 14:10:30 +0100 Subject: [PATCH] Finalize multilingual routing and translations --- .gitignore | 1 + app/[locale]/(site)/about/page.tsx | 191 ++++--- app/[locale]/(site)/contact/page.tsx | 103 ++-- app/[locale]/(site)/layout.tsx | 12 +- app/[locale]/(site)/page.tsx | 194 +++---- app/[locale]/(site)/portfolio/[slug]/page.tsx | 192 ++++--- app/[locale]/(site)/portfolio/page.tsx | 117 ++-- app/[locale]/(site)/products/[slug]/page.tsx | 174 +++--- app/[locale]/(site)/products/page.tsx | 113 ++-- app/[locale]/(site)/success/page.tsx | 85 +-- app/[locale]/coming-soon/page.tsx | 188 ++++--- app/[locale]/layout.tsx | 4 +- app/globals.css | 343 +++++------- app/layout.tsx | 14 +- app/page.tsx | 5 - app/root/maintenance/page.tsx | 139 +++++ app/root/page.tsx | 312 ++++------- app/root/ui-kit/page.tsx | 76 +++ components.json | 7 +- components/footer.tsx | 44 -- .../layout/app-header.tsx | 21 +- components/layout/app-shell.tsx | 27 + .../layout/app-sidebar.tsx | 19 +- components/layout/container.tsx | 43 ++ components/layout/locale-toggle.tsx | 37 ++ components/layout/site-footer.tsx | 49 ++ components/layout/site-header.tsx | 132 +++++ components/navbar.tsx | 120 ---- components/theme-toggle.tsx | 13 +- components/ui/app-card.tsx | 59 ++ {src/components => components}/ui/badge.tsx | 12 +- {src/components => components}/ui/button.tsx | 23 +- {src/components => components}/ui/card.tsx | 37 +- components/ui/input.tsx | 21 + components/ui/label.tsx | 17 + .../ui/separator.tsx | 6 +- components/ui/tabs.tsx | 114 ++++ components/ui/textarea.tsx | 20 + components/ui/ui-kit-showcase.tsx | 522 ++++++++++++++++++ i18n/routing.ts | 5 +- lib/locale.ts | 41 ++ lib/metadata.ts | 56 ++ lib/root-navigation.ts | 40 ++ lib/site-data.ts | 30 +- messages/ar.json | 98 ++++ messages/de.json | 88 ++- messages/en.json | 86 ++- middleware.ts | 10 +- src/components/layout/app-shell.tsx | 24 - src/components/ui/input.tsx | 22 - src/components/ui/label.tsx | 9 - src/components/ui/textarea.tsx | 21 - tailwind.config.ts | 51 +- 53 files changed, 2805 insertions(+), 1382 deletions(-) delete mode 100644 app/page.tsx create mode 100644 app/root/maintenance/page.tsx create mode 100644 app/root/ui-kit/page.tsx delete mode 100644 components/footer.tsx rename {src/components => components}/layout/app-header.tsx (51%) create mode 100644 components/layout/app-shell.tsx rename {src/components => components}/layout/app-sidebar.tsx (71%) create mode 100644 components/layout/container.tsx create mode 100644 components/layout/locale-toggle.tsx create mode 100644 components/layout/site-footer.tsx create mode 100644 components/layout/site-header.tsx delete mode 100644 components/navbar.tsx create mode 100644 components/ui/app-card.tsx rename {src/components => components}/ui/badge.tsx (54%) rename {src/components => components}/ui/button.tsx (57%) rename {src/components => components}/ui/card.tsx (52%) create mode 100644 components/ui/input.tsx create mode 100644 components/ui/label.tsx rename {src/components => components}/ui/separator.tsx (80%) create mode 100644 components/ui/tabs.tsx create mode 100644 components/ui/textarea.tsx create mode 100644 components/ui/ui-kit-showcase.tsx create mode 100644 lib/locale.ts create mode 100644 lib/metadata.ts create mode 100644 lib/root-navigation.ts create mode 100644 messages/ar.json delete mode 100644 src/components/layout/app-shell.tsx delete mode 100644 src/components/ui/input.tsx delete mode 100644 src/components/ui/label.tsx delete mode 100644 src/components/ui/textarea.tsx diff --git a/.gitignore b/.gitignore index a84107a..551a541 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ yarn-error.log* # local env files .env .env*.local +docker-compose.override.yml # vercel .vercel diff --git a/app/[locale]/(site)/about/page.tsx b/app/[locale]/(site)/about/page.tsx index 9ae8bc1..540b191 100644 --- a/app/[locale]/(site)/about/page.tsx +++ b/app/[locale]/(site)/about/page.tsx @@ -1,7 +1,13 @@ +import type { Metadata } from "next"; import { Compass, Layers3, Users } from "lucide-react"; +import { getTranslations } from "next-intl/server"; +import { Container } from "@/components/layout/container"; import { MotionFade } from "@/components/motion-fade"; -import { resolveLocale } from "@/lib/site-data"; +import { buildLocalizedMetadata } from "@/lib/metadata"; +import { resolveLocale } from "@/lib/locale"; +import { AppCard } from "@/components/ui/app-card"; +import { CardContent } from "@/components/ui/card"; type AboutPageProps = { params: { @@ -9,114 +15,103 @@ type AboutPageProps = { }; }; -export default function AboutPage({ params: { locale } }: AboutPageProps) { +export async function generateMetadata({ + params: { locale }, +}: AboutPageProps): Promise { const localeKey = resolveLocale(locale); + const t = await getTranslations({ locale: localeKey, namespace: "aboutPage" }); - const copy = - localeKey === "de" - ? { - title: "Ueber uns", - intro: - "Wir bauen klare digitale Erlebnisse fuer Marken, Produkte und Teams.", - valuesTitle: "Wie wir arbeiten", - valueA: "Strategie zuerst", - valueAText: "Jedes Projekt startet mit Zielbild, Scope und Prioritaeten.", - valueB: "Saubere Systeme", - valueBText: "Wir setzen auf wartbare Komponenten und klare Strukturen.", - valueC: "Enge Zusammenarbeit", - valueCText: "Kurze Schleifen mit direktem Feedback im gesamten Ablauf.", - processTitle: "Unser Ablauf", - processOne: "Discovery und Zieldefinition", - processTwo: "Design und Prototyping", - processThree: "Build, Test und Launch", - } - : { - title: "About", - intro: - "We build clear digital experiences for brands, products and teams.", - valuesTitle: "How we work", - valueA: "Strategy first", - valueAText: "Each project starts with goals, scope and priorities.", - valueB: "Clean systems", - valueBText: "We rely on maintainable components and clear structure.", - valueC: "Close collaboration", - valueCText: "Short loops with direct feedback across the full process.", - processTitle: "Our process", - processOne: "Discovery and goal definition", - processTwo: "Design and prototyping", - processThree: "Build, test and launch", - }; + return buildLocalizedMetadata({ + locale: localeKey, + pathname: "/about", + title: t("title"), + description: t("intro"), + }); +} + +export default async function AboutPage({ params: { locale } }: AboutPageProps) { + const localeKey = resolveLocale(locale); + const t = await getTranslations({ locale: localeKey, namespace: "aboutPage" }); return ( -
+ -
-

- {copy.title} -

-

- {copy.intro} -

-
+ + +

+ {t("title")} +

+

+ {t("intro")} +

+
+
-
-

- {copy.valuesTitle} -

-
-
- -

- {copy.valueA} -

-

- {copy.valueAText} -

-
-
- -

- {copy.valueB} -

-

- {copy.valueBText} -

-
-
- -

- {copy.valueC} -

-

- {copy.valueCText} -

-
-
-
+ + +

+ {t("valuesTitle")} +

+
+ {[ + { + icon: Compass, + title: t("valueA"), + text: t("valueAText"), + }, + { + icon: Layers3, + title: t("valueB"), + text: t("valueBText"), + }, + { + icon: Users, + title: t("valueC"), + text: t("valueCText"), + }, + ].map((item) => { + const Icon = item.icon; + + return ( + + + +

+ {item.title} +

+

{item.text}

+
+
+ ); + })} +
+
+
-
-

- {copy.processTitle} -

-
    - {[copy.processOne, copy.processTwo, copy.processThree].map((step, index) => ( -
  1. - - {index + 1} - -

    {step}

    -
  2. - ))} -
-
+ + +

+ {t("processTitle")} +

+
    + {[t("processOne"), t("processTwo"), t("processThree")].map((step, index) => ( + + + + {index + 1} + +

    {step}

    +
    +
    + ))} +
+
+
-
+ ); } diff --git a/app/[locale]/(site)/contact/page.tsx b/app/[locale]/(site)/contact/page.tsx index 493a00d..a24261d 100644 --- a/app/[locale]/(site)/contact/page.tsx +++ b/app/[locale]/(site)/contact/page.tsx @@ -1,13 +1,18 @@ +import type { Metadata } from "next"; import { Mail, MapPin, Phone } from "lucide-react"; import Link from "next/link"; +import { getTranslations } from "next-intl/server"; +import { Container } from "@/components/layout/container"; import { MotionFade } from "@/components/motion-fade"; -import { resolveLocale } from "@/lib/site-data"; -import { Button } from "@/src/components/ui/button"; -import { Card, CardContent, CardHeader, CardTitle } from "@/src/components/ui/card"; -import { Input } from "@/src/components/ui/input"; -import { Label } from "@/src/components/ui/label"; -import { Textarea } from "@/src/components/ui/textarea"; +import { buildLocalizedMetadata } from "@/lib/metadata"; +import { getLocalizedPath, resolveLocale } from "@/lib/locale"; +import { AppCard } from "@/components/ui/app-card"; +import { Button } from "@/components/ui/button"; +import { CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; type ContactPageProps = { params: { @@ -15,92 +20,80 @@ type ContactPageProps = { }; }; -export default function ContactPage({ params: { locale } }: ContactPageProps) { +export async function generateMetadata({ + params: { locale }, +}: ContactPageProps): Promise { const localeKey = resolveLocale(locale); + const t = await getTranslations({ locale: localeKey, namespace: "contactPage" }); - const copy = - localeKey === "de" - ? { - title: "Kontakt", - intro: "Schreib uns kurz dein Ziel und wir melden uns zeitnah.", - name: "Name", - email: "E-Mail", - message: "Nachricht", - submit: "Senden", - preview: "Success Seite ansehen", - phone: "+49 30 123456", - mail: "hello@moh-sass.dev", - city: "Berlin, Germany", - } - : { - title: "Contact", - intro: "Share your goal and we will get back quickly.", - name: "Name", - email: "Email", - message: "Message", - submit: "Submit", - preview: "Open success page", - phone: "+49 30 123456", - mail: "hello@moh-sass.dev", - city: "Berlin, Germany", - }; + return buildLocalizedMetadata({ + locale: localeKey, + pathname: "/contact", + title: t("title"), + description: t("intro"), + }); +} + +export default async function ContactPage({ params: { locale } }: ContactPageProps) { + const localeKey = resolveLocale(locale); + const t = await getTranslations({ locale: localeKey, namespace: "contactPage" }); return ( -
+ - + - {copy.title} -

{copy.intro}

+ {t("title")} +

{t("intro")}

  • - - {copy.phone} + + +49 30 123456
  • - - {copy.mail} + + hello@moh-sass.dev
  • - - {copy.city} + + {t("city")}
-
+
- +