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"
+ }
}