diff --git a/app/components/About.tsx b/app/components/About.tsx index 000af7a..1044fd3 100644 --- a/app/components/About.tsx +++ b/app/components/About.tsx @@ -57,32 +57,32 @@ const About = () => { const techStack = [ { - category: "Frontend & Mobile", + category: t("techStack.categories.frontendMobile"), icon: Globe, items: ["Next.js", "Tailwind CSS", "Flutter"], }, { - category: "Backend & DevOps", + category: t("techStack.categories.backendDevops"), icon: Server, items: ["Docker Swarm", "Traefik", "Nginx Proxy Manager", "Redis"], }, { - category: "Tools & Automation", + category: t("techStack.categories.toolsAutomation"), icon: Wrench, - items: ["Git", "CI/CD", "n8n", "Self-hosted Services"], + items: ["Git", "CI/CD", "n8n", t("techStack.items.selfHostedServices")], }, { - category: "Security & Admin", + category: t("techStack.categories.securityAdmin"), icon: Shield, items: ["CrowdSec", "Suricata", "Mailcow"], }, ]; const hobbies: Array<{ icon: typeof Code; text: string }> = [ - { icon: Code, text: "Self-Hosting & DevOps" }, - { icon: Gamepad2, text: "Gaming" }, - { icon: Server, text: "Setting up Game Servers" }, - { icon: Activity, text: "Jogging to clear my mind and stay active" }, + { icon: Code, text: t("hobbies.selfHosting") }, + { icon: Gamepad2, text: t("hobbies.gaming") }, + { icon: Server, text: t("hobbies.gameServers") }, + { icon: Activity, text: t("hobbies.jogging") }, ]; return ( @@ -151,7 +151,7 @@ const About = () => { variants={fadeInUp} className="text-2xl font-bold text-stone-900 mb-6" > - My Tech Stack + {t("techStackTitle")}
{techStack.map((stack, idx) => ( @@ -209,7 +209,7 @@ const About = () => { variants={fadeInUp} className="text-xl font-bold text-stone-900 mb-4" > - When I'm Not Coding + {t("hobbiesTitle")}
{hobbies.map((hobby, idx) => ( diff --git a/app/components/ConsentBanner.tsx b/app/components/ConsentBanner.tsx index 1d6f8a9..8f5f56b 100644 --- a/app/components/ConsentBanner.tsx +++ b/app/components/ConsentBanner.tsx @@ -21,9 +21,11 @@ export default function ConsentBanner() { essential: t("essential"), analytics: t("analytics"), chat: t("chat"), + alwaysOn: t("alwaysOn"), acceptAll: t("acceptAll"), acceptSelected: t("acceptSelected"), rejectAll: t("rejectAll"), + hide: t("hide"), }; if (minimized) { @@ -56,14 +58,14 @@ export default function ConsentBanner() { aria-label="Minimize privacy banner" title="Minimize" > - Hide + {s.hide}
{s.essential}
-
Always on
+
{s.alwaysOn}
@@ -419,12 +421,12 @@ const Contact = () => { {isSubmitting ? ( <>
- Sending Message... + {tForm("sending")} ) : ( <> - Send Message + {tForm("send")} )} diff --git a/app/components/Footer.tsx b/app/components/Footer.tsx index 4fa4433..6308938 100644 --- a/app/components/Footer.tsx +++ b/app/components/Footer.tsx @@ -5,17 +5,15 @@ import { motion } from 'framer-motion'; import { Heart, Code } from 'lucide-react'; import { SiGithub, SiLinkedin } from 'react-icons/si'; import Link from 'next/link'; -import { useLocale } from "next-intl"; +import { useLocale, useTranslations } from "next-intl"; import { useConsent } from "./ConsentProvider"; const Footer = () => { - const [currentYear, setCurrentYear] = useState(2024); const locale = useLocale(); + const t = useTranslations("footer"); const { resetConsent } = useConsent(); - useEffect(() => { - setCurrentYear(new Date().getFullYear()); - }, []); + const [currentYear] = useState(() => new Date().getFullYear()); const socialLinks = [ { icon: SiGithub, href: 'https://github.com/Denshooter', label: 'GitHub' }, @@ -45,7 +43,7 @@ const Footer = () => { dk0 -

Software Engineer

+

{t("role")}

@@ -88,7 +86,7 @@ const Footer = () => { > - Made in Germany + {t("madeIn")} @@ -105,21 +103,21 @@ const Footer = () => { href={`/${locale}/legal-notice`} className="text-stone-500 hover:text-stone-800 transition-colors duration-200" > - Impressum + {t("legalNotice")} - Privacy Policy + {t("privacyPolicy")} {
- Built with + {t("builtWith")} Next.js TypeScript diff --git a/app/components/Projects.tsx b/app/components/Projects.tsx index 765c099..3a4baa5 100644 --- a/app/components/Projects.tsx +++ b/app/components/Projects.tsx @@ -5,7 +5,7 @@ import { motion, Variants } from "framer-motion"; import { ExternalLink, Github, ArrowRight, Calendar } from "lucide-react"; import Link from "next/link"; import Image from "next/image"; -import { useLocale } from "next-intl"; +import { useLocale, useTranslations } from "next-intl"; const fadeInUp: Variants = { hidden: { opacity: 0, y: 20 }, @@ -48,6 +48,7 @@ interface Project { const Projects = () => { const [projects, setProjects] = useState([]); const locale = useLocale(); + const t = useTranslations("home.projects"); useEffect(() => { const loadProjects = async () => { @@ -82,11 +83,10 @@ const Projects = () => { className="text-center mb-20" >

- Selected Works + {t("title")}

- A collection of projects I've worked on, ranging from web - applications to experiments. + {t("subtitle")}

@@ -140,7 +140,7 @@ const Projects = () => { {project.featured && (
- Featured + {t("featured")}
)} @@ -253,7 +253,7 @@ const Projects = () => { href={`/${locale}/projects`} className="inline-flex items-center gap-2 px-8 py-4 bg-white border border-stone-200 rounded-full text-stone-700 font-medium hover:bg-stone-50 hover:border-stone-300 hover:gap-3 transition-all duration-500 ease-out shadow-sm hover:shadow-md" > - View All Projects + {t("viewAll")}
diff --git a/messages/de.json b/messages/de.json index d02d101..c64c1f2 100644 --- a/messages/de.json +++ b/messages/de.json @@ -17,9 +17,11 @@ "essential": "Essentiell", "analytics": "Analytics", "chat": "Chatbot", + "alwaysOn": "Immer aktiv", "acceptAll": "Alles akzeptieren", "acceptSelected": "Auswahl akzeptieren", - "rejectAll": "Alles ablehnen" + "rejectAll": "Alles ablehnen", + "hide": "Ausblenden" } , "home": { @@ -39,14 +41,83 @@ "p2": "Ich entwickle Full-Stack Web-Apps mit Next.js und Mobile-Apps mit Flutter. Besonders spannend finde ich DevOps: eigene Infrastruktur, Automatisierung und CI/CD Deployments.", "p3": "Wenn ich nicht code oder an Servern schraube, findest du mich beim Gaming, Joggen oder beim Experimentieren mit Automationen.", "funFactTitle": "Fun Fact", - "funFactBody": "Auch wenn ich viel automatisiere, nutze ich für Kalender & Notizen noch Stift und Papier – das hilft mir beim Fokus." + "funFactBody": "Auch wenn ich viel automatisiere, nutze ich für Kalender & Notizen noch Stift und Papier – das hilft mir beim Fokus.", + "techStackTitle": "Mein Tech Stack", + "hobbiesTitle": "Wenn ich nicht code", + "techStack": { + "categories": { + "frontendMobile": "Frontend & Mobile", + "backendDevops": "Backend & DevOps", + "toolsAutomation": "Tools & Automation", + "securityAdmin": "Security & Admin" + }, + "items": { + "selfHostedServices": "Self-hosted Services" + } + }, + "hobbies": { + "selfHosting": "Self-Hosting & DevOps", + "gaming": "Gaming", + "gameServers": "Game-Server einrichten", + "jogging": "Joggen zum Kopf freibekommen und aktiv bleiben" + } + }, + "projects": { + "title": "Ausgewählte Projekte", + "subtitle": "Eine Auswahl an Projekten, an denen ich gearbeitet habe – von Web-Apps bis zu Experimenten.", + "featured": "Featured", + "viewAll": "Alle Projekte ansehen" }, "contact": { "title": "Kontakt", "subtitle": "Du willst zusammenarbeiten oder hast Fragen zu meinen Projekten? Schreib mir gerne!", "getInTouch": "Melde dich", - "getInTouchBody": "Ich bin immer offen für neue Chancen, spannende Projekte oder einfach einen Tech-Talk." + "getInTouchBody": "Ich bin immer offen für neue Chancen, spannende Projekte oder einfach einen Tech-Talk.", + "info": { + "email": "E-Mail", + "location": "Ort", + "locationValue": "Osnabrück, Deutschland" + }, + "form": { + "title": "Nachricht senden", + "sending": "Sende Nachricht…", + "send": "Nachricht senden", + "labels": { + "name": "Name", + "email": "E-Mail", + "subject": "Betreff", + "message": "Nachricht", + "requiredMarker": "*" + }, + "placeholders": { + "name": "Dein Name", + "email": "dein@email.de", + "subject": "Worum geht’s?", + "message": "Erzähl mir mehr über dein Projekt oder deine Frage…" + }, + "errors": { + "nameRequired": "Name ist erforderlich", + "nameMin": "Name muss mindestens 2 Zeichen haben", + "emailRequired": "E-Mail ist erforderlich", + "emailInvalid": "Bitte eine gültige E-Mail-Adresse eingeben", + "subjectRequired": "Betreff ist erforderlich", + "subjectMin": "Betreff muss mindestens 3 Zeichen haben", + "messageRequired": "Nachricht ist erforderlich", + "messageMin": "Nachricht muss mindestens 10 Zeichen haben" + }, + "characters": "{count} Zeichen" + } } } + , + "footer": { + "role": "Software Engineer", + "madeIn": "Made in Germany", + "legalNotice": "Impressum", + "privacyPolicy": "Datenschutz", + "privacySettings": "Datenschutz-Einstellungen", + "privacySettingsTitle": "Datenschutz-Banner wieder anzeigen", + "builtWith": "Built with" + } } diff --git a/messages/en.json b/messages/en.json index ebe8db0..8a5b079 100644 --- a/messages/en.json +++ b/messages/en.json @@ -17,9 +17,11 @@ "essential": "Essential", "analytics": "Analytics", "chat": "Chatbot", + "alwaysOn": "Always on", "acceptAll": "Accept all", "acceptSelected": "Accept selected", - "rejectAll": "Reject all" + "rejectAll": "Reject all", + "hide": "Hide" } , "home": { @@ -39,14 +41,83 @@ "p2": "I love building full-stack web applications with Next.js and mobile apps with Flutter. But what really excites me is DevOps: I run my own infrastructure and automate deployments with CI/CD.", "p3": "When I'm not coding or tinkering with servers, you'll find me gaming, jogging, or experimenting with automation workflows.", "funFactTitle": "Fun Fact", - "funFactBody": "Even though I automate a lot, I still use pen and paper for my calendar and notes – it helps me stay focused." + "funFactBody": "Even though I automate a lot, I still use pen and paper for my calendar and notes – it helps me stay focused.", + "techStackTitle": "My Tech Stack", + "hobbiesTitle": "When I'm Not Coding", + "techStack": { + "categories": { + "frontendMobile": "Frontend & Mobile", + "backendDevops": "Backend & DevOps", + "toolsAutomation": "Tools & Automation", + "securityAdmin": "Security & Admin" + }, + "items": { + "selfHostedServices": "Self-hosted services" + } + }, + "hobbies": { + "selfHosting": "Self-Hosting & DevOps", + "gaming": "Gaming", + "gameServers": "Setting up game servers", + "jogging": "Jogging to clear my mind and stay active" + } + }, + "projects": { + "title": "Selected Works", + "subtitle": "A collection of projects I've worked on, ranging from web applications to experiments.", + "featured": "Featured", + "viewAll": "View All Projects" }, "contact": { "title": "Contact Me", "subtitle": "Interested in working together or have questions about my projects? Feel free to reach out!", "getInTouch": "Get In Touch", - "getInTouchBody": "I'm always available to discuss new opportunities, interesting projects, or simply chat about technology and innovation." + "getInTouchBody": "I'm always available to discuss new opportunities, interesting projects, or simply chat about technology and innovation.", + "info": { + "email": "Email", + "location": "Location", + "locationValue": "Osnabrück, Germany" + }, + "form": { + "title": "Send Message", + "sending": "Sending message…", + "send": "Send Message", + "labels": { + "name": "Name", + "email": "Email", + "subject": "Subject", + "message": "Message", + "requiredMarker": "*" + }, + "placeholders": { + "name": "Your name", + "email": "your@email.com", + "subject": "What's this about?", + "message": "Tell me more about your project or question…" + }, + "errors": { + "nameRequired": "Name is required", + "nameMin": "Name must be at least 2 characters", + "emailRequired": "Email is required", + "emailInvalid": "Please enter a valid email address", + "subjectRequired": "Subject is required", + "subjectMin": "Subject must be at least 3 characters", + "messageRequired": "Message is required", + "messageMin": "Message must be at least 10 characters" + }, + "characters": "{count} characters" + } } } + , + "footer": { + "role": "Software Engineer", + "madeIn": "Made in Germany", + "legalNotice": "Legal notice", + "privacyPolicy": "Privacy policy", + "privacySettings": "Privacy settings", + "privacySettingsTitle": "Show privacy settings banner again", + "builtWith": "Built with" + } }