diff --git a/hifi-src/src/components/CookieConsentBanner.jsx b/hifi-src/src/components/CookieConsentBanner.jsx
index f11f58a..af10d78 100644
--- a/hifi-src/src/components/CookieConsentBanner.jsx
+++ b/hifi-src/src/components/CookieConsentBanner.jsx
@@ -1,10 +1,12 @@
-import { useEffect, useState } from 'react';
+import { useState } from 'react';
import { Link } from 'react-router-dom';
import { useCookieConsent } from '../context/CookieConsentContext.jsx';
+import { useLanguage } from '../context/LanguageContext.jsx';
function SettingsModal({ onClose }) {
const { consent, saveCustom } = useCookieConsent();
const [external, setExternal] = useState(consent.external);
+ const { t } = useLanguage();
return (
e.stopPropagation()}
>
-
Cookie-Einstellungen
+
{t('cookieConsent.modalTitle')}
- Lege fest, welche Kategorien du zulassen möchtest. Mehr dazu in unserer{' '}
+ {t('cookieConsent.modalIntro')}{' '}
- Datenschutzerklärung
+ {t('cookieConsent.privacyPolicy')}
.
-
Notwendig
+
{t('cookieConsent.necessaryTitle')}
- Für den Betrieb der Website erforderlich (z. B. Login-Session, Theme- und Cookie-Einstellung).
- Kann nicht deaktiviert werden.
+ {t('cookieConsent.necessaryDesc')}
-
Externe Medien
+
{t('cookieConsent.externalTitle')}
- Google Maps und YouTube-Videos. Beim Laden werden Daten (u. a. deine IP-Adresse) an Google
- übertragen.
+ {t('cookieConsent.externalDesc')}
- Abbrechen
+ {t('cookieConsent.cancel')}
saveCustom({ external })}
className="rounded-md bg-brand-500 px-4 py-2 text-sm font-semibold text-white hover:bg-brand-600"
>
- Auswahl speichern
+ {t('cookieConsent.saveSelection')}
@@ -73,6 +73,7 @@ function SettingsModal({ onClose }) {
export default function CookieConsentBanner() {
const { decided, settingsOpen, acceptAll, rejectNonEssential, openSettings, closeSettings } = useCookieConsent();
+ const { t } = useLanguage();
return (
<>
@@ -80,10 +81,9 @@ export default function CookieConsentBanner() {
- Wir verwenden nur technisch notwendige Cookies. Für Google Maps und YouTube-Videos benötigen wir
- zusätzlich deine Zustimmung, da dabei Daten an Google übertragen werden. Mehr dazu in unserer{' '}
+ {t('cookieConsent.bannerText')} {t('cookieConsent.moreInfo')}{' '}
- Datenschutzerklärung
+ {t('cookieConsent.privacyPolicy')}
.
@@ -93,21 +93,21 @@ export default function CookieConsentBanner() {
onClick={openSettings}
className="rounded-md border border-neutral-300 px-3 py-2 text-sm dark:border-neutral-700"
>
- Einstellungen
+ {t('cookieConsent.settings')}
- Nur notwendige
+ {t('cookieConsent.onlyNecessary')}
- Alle akzeptieren
+ {t('cookieConsent.acceptAll')}
diff --git a/hifi-src/src/components/Footer.jsx b/hifi-src/src/components/Footer.jsx
index b04a052..57bc03d 100644
--- a/hifi-src/src/components/Footer.jsx
+++ b/hifi-src/src/components/Footer.jsx
@@ -2,24 +2,20 @@ import { Link, useLocation } from 'react-router-dom';
import DynamicIcon from './DynamicIcon.jsx';
import { useCookieConsent } from '../context/CookieConsentContext.jsx';
import { useSiteSettings } from '../context/SiteSettingsContext.jsx';
+import { useLanguage } from '../context/LanguageContext.jsx';
const digitsOnly = (value) => (value || '').replace(/[^\d+]/g, '');
const SHOP_ADDRESS_ENCODED = encodeURIComponent('Boxbrunner Str. 20a, 63916 Amorbach');
const DIRECTIONS_URL = `https://www.google.com/maps/dir/?api=1&destination=${SHOP_ADDRESS_ENCODED}`;
-const HOURS = [
- { days: 'Montag – Freitag', from: 9 * 60, to: 18 * 60, label: '9:00–18:00 Uhr' },
- { days: 'Samstag', from: 10 * 60, to: 13 * 60, label: '10:00–13:00 Uhr' },
- { days: 'Sonntag', from: null, to: null, label: 'Geschlossen' },
-];
-
function isOpenNow() {
const now = new Date();
const minutes = now.getHours() * 60 + now.getMinutes();
const day = now.getDay(); // 0 = Sonntag, 1-5 = Mo-Fr, 6 = Sa
- const todayHours = day === 0 ? HOURS[2] : day === 6 ? HOURS[1] : HOURS[0];
- return todayHours.from !== null && minutes >= todayHours.from && minutes < todayHours.to;
+ if (day === 0) return false;
+ if (day === 6) return minutes >= 10 * 60 && minutes < 13 * 60;
+ return minutes >= 9 * 60 && minutes < 18 * 60;
}
export default function Footer() {
@@ -31,6 +27,13 @@ export default function Footer() {
const { pathname } = useLocation();
const isHome = pathname === '/';
const { phone, contact_email: contactEmail } = useSiteSettings();
+ const { t } = useLanguage();
+
+ const HOURS = [
+ { key: 'monFri', days: t('footer.days.monFri'), label: t('contactPage.hoursWeek') },
+ { key: 'sat', days: t('footer.days.sat'), label: t('contactPage.hoursSat') },
+ { key: 'sun', days: t('footer.days.sun'), label: t('footer.closed') },
+ ];
return (
- Route planen
+ {t('footer.routePlan')}
-
Kontakt
+
{t('footer.contactHeading')}
{phone}
@@ -72,7 +75,7 @@ export default function Footer() {
to="/kontakt"
className="mt-4 flex items-center justify-center gap-1.5 rounded-md border border-neutral-300 px-3 py-2 text-xs font-semibold text-neutral-700 hover:border-brand-500 hover:text-brand-600 dark:border-neutral-700 dark:text-neutral-200"
>
- Kontaktformular
+ {t('footer.contactForm')}
@@ -80,7 +83,7 @@ export default function Footer() {
{HOURS.map((row) => (
-
+
{row.days}
{row.label}
@@ -105,16 +108,16 @@ export default function Footer() {
- © {new Date().getFullYear()} HifiPlanet. Alle Angaben ohne Gewähr.
+ {t('footer.copyright')(new Date().getFullYear())}
·
- Impressum
+ {t('footer.imprint')}
·
- Datenschutz
+ {t('footer.privacy')}
·
- AGB
+ {t('footer.terms')}
·
- Cookie-Einstellungen
+ {t('footer.cookieSettings')}
diff --git a/hifi-src/src/components/Lightbox.jsx b/hifi-src/src/components/Lightbox.jsx
index c29f421..4f8f5c1 100644
--- a/hifi-src/src/components/Lightbox.jsx
+++ b/hifi-src/src/components/Lightbox.jsx
@@ -1,7 +1,9 @@
import { useEffect } from 'react';
import { createPortal } from 'react-dom';
+import { useLanguage } from '../context/LanguageContext.jsx';
export default function Lightbox({ photos, index, onClose, onNavigate }) {
+ const { t } = useLanguage();
useEffect(() => {
if (index == null) return undefined;
const handleKey = (e) => {
@@ -21,7 +23,7 @@ export default function Lightbox({ photos, index, onClose, onNavigate }) {
{ stop(e); onClose(); }}
- aria-label="Schließen"
+ aria-label={t('lightbox.close')}
className="absolute right-4 top-4 rounded-full p-2 text-white/80 hover:bg-white/10 hover:text-white"
>
@@ -32,7 +34,7 @@ export default function Lightbox({ photos, index, onClose, onNavigate }) {
{photos.length > 1 && (
{ stop(e); onNavigate(-1); }}
- aria-label="Vorheriges Bild"
+ aria-label={t('lightbox.previous')}
className="absolute left-2 top-1/2 -translate-y-1/2 rounded-full p-2 text-white/80 hover:bg-white/10 hover:text-white sm:left-4"
>
@@ -51,7 +53,7 @@ export default function Lightbox({ photos, index, onClose, onNavigate }) {
{photos.length > 1 && (
{ stop(e); onNavigate(1); }}
- aria-label="Nächstes Bild"
+ aria-label={t('lightbox.next')}
className="absolute right-2 top-1/2 -translate-y-1/2 rounded-full p-2 text-white/80 hover:bg-white/10 hover:text-white sm:right-4"
>
diff --git a/hifi-src/src/components/MaintenanceNotice.jsx b/hifi-src/src/components/MaintenanceNotice.jsx
index efe738b..a9a0266 100644
--- a/hifi-src/src/components/MaintenanceNotice.jsx
+++ b/hifi-src/src/components/MaintenanceNotice.jsx
@@ -1,3 +1,5 @@
+import { useLanguage } from '../context/LanguageContext.jsx';
+
const WrenchIcon = () => (
@@ -5,14 +7,15 @@ const WrenchIcon = () => (
);
export default function MaintenanceNotice({ message, fullScreen = false }) {
+ const { t } = useLanguage();
const content = (
-
Wartungsarbeiten
+
{t('maintenance.title')}
- {message || 'Dieser Bereich wird gerade aktualisiert. Bitte schau in Kürze wieder vorbei.'}
+ {message || t('maintenance.defaultMessage')}
);
diff --git a/hifi-src/src/components/Navbar.jsx b/hifi-src/src/components/Navbar.jsx
index b41f3d7..b0ef261 100644
--- a/hifi-src/src/components/Navbar.jsx
+++ b/hifi-src/src/components/Navbar.jsx
@@ -1,10 +1,11 @@
-import { useEffect, useState } from 'react';
+import { useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { Link, useLocation } from 'react-router-dom';
import ThemeToggle from './ThemeToggle.jsx';
import DynamicIcon from './DynamicIcon.jsx';
import { useAuth } from '../context/AuthContext.jsx';
import { useSiteSettings } from '../context/SiteSettingsContext.jsx';
+import { useLanguage } from '../context/LanguageContext.jsx';
import logo from '../assets/logo.png';
const SHOP_URL = 'https://www.audio4cars.de/';
@@ -14,13 +15,134 @@ const digitsOnly = (value) => (value || '').replace(/[^\d+]/g, '');
const ITEM_DELAY_MS = 70;
const FLY_DURATION_MS = 500;
+function FlagDE({ className }) {
+ return (
+
+
+
+
+
+ );
+}
+
+function FlagGB({ className }) {
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const LANGUAGE_OPTIONS = [
+ { code: 'de', label: 'Deutsch', Flag: FlagDE },
+ { code: 'en', label: 'English', Flag: FlagGB },
+];
+
+function LanguageSwitcher({ overHero = false, variant = 'header' }) {
+ const { language, setLanguage, t } = useLanguage();
+ const [isOpen, setIsOpen] = useState(false);
+ const containerRef = useRef(null);
+ const current = LANGUAGE_OPTIONS.find((opt) => opt.code === language) || LANGUAGE_OPTIONS[0];
+ const isHeader = variant === 'header';
+
+ useEffect(() => {
+ if (!isOpen) return undefined;
+ const handleClickOutside = (e) => {
+ if (containerRef.current && !containerRef.current.contains(e.target)) setIsOpen(false);
+ };
+ const handleKeyDown = (e) => {
+ if (e.key === 'Escape') setIsOpen(false);
+ };
+ document.addEventListener('mousedown', handleClickOutside);
+ document.addEventListener('keydown', handleKeyDown);
+ return () => {
+ document.removeEventListener('mousedown', handleClickOutside);
+ document.removeEventListener('keydown', handleKeyDown);
+ };
+ }, [isOpen]);
+
+ return (
+
+
setIsOpen((v) => !v)}
+ aria-haspopup="listbox"
+ aria-expanded={isOpen}
+ aria-label={t('languageToggle.label')}
+ className={
+ isHeader
+ ? `flex items-center gap-1 rounded-full p-1.5 transition ${overHero ? 'hover:bg-white/10' : 'hover:bg-neutral-100 dark:hover:bg-neutral-800'}`
+ : 'flex items-center gap-2 text-sm font-semibold uppercase tracking-wide text-neutral-600 hover:text-brand-600'
+ }
+ >
+
+
+
+ {!isHeader && current.label}
+ {isHeader && (
+
+
+
+ )}
+
+
+ {isOpen && (
+
+ {LANGUAGE_OPTIONS.map((opt) => (
+ {
+ setLanguage(opt.code);
+ setIsOpen(false);
+ }}
+ className={`flex w-full items-center gap-2 px-3 py-2 text-left text-sm transition hover:bg-neutral-100 dark:hover:bg-neutral-800 ${
+ opt.code === language ? 'font-semibold text-brand-600 dark:text-brand-400' : 'text-neutral-700 dark:text-neutral-200'
+ }`}
+ >
+
+
+
+ {opt.label}
+
+ ))}
+
+ )}
+
+ );
+}
+
export default function Navbar() {
const [open, setOpen] = useState(false);
const [mounted, setMounted] = useState(false);
const [menuVisible, setMenuVisible] = useState(false);
const { user } = useAuth();
const { phone, whatsapp } = useSiteSettings();
- const totalItems = whatsapp ? 8 : 7;
+ const { t } = useLanguage();
+ const totalItems = whatsapp ? 9 : 8;
const location = useLocation();
const isHome = location.pathname === '/';
const [scrolled, setScrolled] = useState(!isHome);
@@ -74,8 +196,8 @@ export default function Navbar() {
const AdminIcon = user && (
setOpen(true)}
- aria-label="Menü öffnen"
+ aria-label={t('nav.openMenu')}
className={`rounded-md p-2 transition ${
transparent ? 'text-white hover:bg-white/10' : 'text-neutral-600 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-800'
}`}
@@ -113,6 +235,7 @@ export default function Navbar() {
{AdminIcon}
+ {!open && }
@@ -126,57 +249,61 @@ export default function Navbar() {
setOpen(false)}
- aria-label="Menü schließen"
+ aria-label={t('nav.closeMenu')}
className="absolute left-6 top-8 flex items-center gap-2 text-neutral-600 hover:text-brand-600 sm:left-8 sm:top-[65px]"
>
- Schließen
+ {t('nav.close')}
setOpen(false)} className={`text-3xl font-extrabold tracking-tight text-neutral-900 hover:text-brand-600 ${flyIn()}`} style={flyInStyle(0)}>
- Fahrzeuge
-
-
setOpen(false)} className={`text-3xl font-extrabold tracking-tight text-neutral-900 hover:text-brand-600 ${flyIn()}`} style={flyInStyle(1)}>
- Leistungen
-
-
setOpen(false)} className={`text-3xl font-extrabold tracking-tight text-neutral-900 hover:text-brand-600 ${flyIn()}`} style={flyInStyle(2)}>
- Galerie
-
-
setOpen(false)} className={`text-3xl font-extrabold tracking-tight text-neutral-900 hover:text-brand-600 ${flyIn()}`} style={flyInStyle(3)}>
- Kontakt
-
+ {t('nav.vehicles')}
+
+
setOpen(false)} className={`text-3xl font-extrabold tracking-tight text-neutral-900 hover:text-brand-600 ${flyIn()}`} style={flyInStyle(1)}>
+ {t('nav.services')}
+
+
setOpen(false)} className={`text-3xl font-extrabold tracking-tight text-neutral-900 hover:text-brand-600 ${flyIn()}`} style={flyInStyle(2)}>
+ {t('nav.gallery')}
+
+
setOpen(false)} className={`text-3xl font-extrabold tracking-tight text-neutral-900 hover:text-brand-600 ${flyIn()}`} style={flyInStyle(3)}>
+ {t('nav.contact')}
+
-
+
- {phone && (
-
-
- {phone}
-
- )}
- {whatsapp && (
+
+
+
+
+ {phone && (
+
+
+ {phone}
+
+ )}
+ {whatsapp && (
+
+
+ {t('nav.whatsapp')}
+
+ )}
-
- WhatsApp
-
- )}
-
-
- Zum Shop
+
+ {t('nav.shop')}
,
diff --git a/hifi-src/src/components/PublicLayout.jsx b/hifi-src/src/components/PublicLayout.jsx
index ac7b878..04f8350 100644
--- a/hifi-src/src/components/PublicLayout.jsx
+++ b/hifi-src/src/components/PublicLayout.jsx
@@ -8,6 +8,7 @@ import MaintenanceBypassBanner from './MaintenanceBypassBanner.jsx';
import CookieConsentBanner from './CookieConsentBanner.jsx';
import { MaintenanceProvider } from '../context/MaintenanceContext.jsx';
import { SiteSettingsProvider } from '../context/SiteSettingsContext.jsx';
+import { LanguageProvider } from '../context/LanguageContext.jsx';
import { useAuth } from '../context/AuthContext.jsx';
import { api } from '../api/client.js';
@@ -49,23 +50,29 @@ export default function PublicLayout() {
const isLegalPage = LEGAL_PATHS.includes(location.pathname);
if (status.global.enabled && !bypass && !isLegalPage) {
- return ;
+ return (
+
+
+
+ );
}
return (
-
-
-
- {bypass && status.global.enabled && }
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ {bypass && status.global.enabled && }
+
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/hifi-src/src/components/ThemeToggle.jsx b/hifi-src/src/components/ThemeToggle.jsx
index 722b114..22307e8 100644
--- a/hifi-src/src/components/ThemeToggle.jsx
+++ b/hifi-src/src/components/ThemeToggle.jsx
@@ -1,12 +1,14 @@
import { useTheme } from '../context/ThemeContext.jsx';
+import { useLanguage } from '../context/LanguageContext.jsx';
export default function ThemeToggle({ overHero = false }) {
const { theme, toggleTheme } = useTheme();
+ const { t } = useLanguage();
return (
(obj == null ? undefined : obj[part]), dict);
+}
+
+// Default-Wert (nicht null) analog zu MaintenanceContext/SiteSettingsContext, damit
+// Komponenten, die sowohl im öffentlichen Bereich (mit Provider) als auch im
+// Admin-Panel (ohne Provider, bleibt bewusst Deutsch) verwendet werden - z. B.
+// ThemeToggle - nicht abstürzen, wenn kein LanguageProvider im Baum ist.
+const LanguageContext = createContext({
+ language: 'de',
+ setLanguage: () => {},
+ toggleLanguage: () => {},
+ t: (key) => {
+ const value = lookup(dictionaries.de, key);
+ return value !== undefined ? value : key;
+ },
+});
+
+export function LanguageProvider({ children }) {
+ const [language, setLanguage] = useState(detectDefaultLanguage);
+
+ useEffect(() => {
+ document.documentElement.setAttribute('lang', language);
+ localStorage.setItem('hifi-lang', language);
+ }, [language]);
+
+ const toggleLanguage = () => setLanguage((l) => (l === 'de' ? 'en' : 'de'));
+
+ const t = (key) => {
+ const dict = dictionaries[language] || dictionaries.de;
+ const value = lookup(dict, key);
+ if (value !== undefined) return value;
+ const fallback = lookup(dictionaries.de, key);
+ return fallback !== undefined ? fallback : key;
+ };
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useLanguage() {
+ return useContext(LanguageContext);
+}
diff --git a/hifi-src/src/i18n/de.js b/hifi-src/src/i18n/de.js
new file mode 100644
index 0000000..2a42379
--- /dev/null
+++ b/hifi-src/src/i18n/de.js
@@ -0,0 +1,233 @@
+export default {
+ nav: {
+ openMenu: 'Menü öffnen',
+ closeMenu: 'Menü schließen',
+ close: 'Schließen',
+ adminPanel: 'Zum Admin-Panel',
+ vehicles: 'Fahrzeuge',
+ services: 'Leistungen',
+ gallery: 'Galerie',
+ contact: 'Kontakt',
+ whatsapp: 'WhatsApp',
+ shop: 'Zum Shop',
+ },
+ themeToggle: {
+ label: 'Theme umschalten',
+ },
+ languageToggle: {
+ label: 'Sprache auswählen',
+ },
+ footer: {
+ routePlan: 'Route planen',
+ contactHeading: 'Kontakt',
+ contactForm: 'Kontaktformular',
+ hoursHeading: 'Öffnungszeiten',
+ open: 'Geöffnet',
+ closed: 'Geschlossen',
+ days: {
+ monFri: 'Montag – Freitag',
+ sat: 'Samstag',
+ sun: 'Sonntag',
+ },
+ copyright: (year) => `© ${year} HifiPlanet. Alle Angaben ohne Gewähr.`,
+ imprint: 'Impressum',
+ privacy: 'Datenschutz',
+ terms: 'AGB',
+ cookieSettings: 'Cookie-Einstellungen',
+ },
+ cookieConsent: {
+ bannerText: 'Wir verwenden nur technisch notwendige Cookies. Für Google Maps und YouTube-Videos benötigen wir zusätzlich deine Zustimmung, da dabei Daten an Google übertragen werden.',
+ moreInfo: 'Mehr dazu in unserer',
+ privacyPolicy: 'Datenschutzerklärung',
+ settings: 'Einstellungen',
+ onlyNecessary: 'Nur notwendige',
+ acceptAll: 'Alle akzeptieren',
+ modalTitle: 'Cookie-Einstellungen',
+ modalIntro: 'Lege fest, welche Kategorien du zulassen möchtest. Mehr dazu in unserer',
+ necessaryTitle: 'Notwendig',
+ necessaryDesc: 'Für den Betrieb der Website erforderlich (z. B. Login-Session, Theme- und Cookie-Einstellung). Kann nicht deaktiviert werden.',
+ externalTitle: 'Externe Medien',
+ externalDesc: 'Google Maps und YouTube-Videos. Beim Laden werden Daten (u. a. deine IP-Adresse) an Google übertragen.',
+ cancel: 'Abbrechen',
+ saveSelection: 'Auswahl speichern',
+ },
+ maintenance: {
+ title: 'Wartungsarbeiten',
+ defaultMessage: 'Dieser Bereich wird gerade aktualisiert. Bitte schau in Kürze wieder vorbei.',
+ },
+ lightbox: {
+ close: 'Schließen',
+ previous: 'Vorheriges Bild',
+ next: 'Nächstes Bild',
+ },
+ home: {
+ metaTitle: 'Car-Hifi Umbauten nach Maß',
+ metaDescription: 'HifiPlanet – dein Car-Hifi Spezialist in Amorbach. Marke und Modell wählen, passende Sound-Pakete entdecken und unverbindlich anfragen.',
+ stickyCta: 'Bereit für deinen Sound-Umbau?',
+ selectVehicle: 'Fahrzeug auswählen',
+ getInTouch: 'Kontakt aufnehmen',
+ welcome: 'Willkommen bei HifiPlanet',
+ heroTitle: 'Audiophil aus Prinzip',
+ heroSubtitle: 'Dein Car-Hifi Spezialist für individuelle Sound-Umbauten. Wähle dein Fahrzeug, entdecke passende Pakete und frag unverbindlich an – wir kümmern uns um den Rest.',
+ stats: [
+ { value: 'Seit 2010', label: 'in Amorbach' },
+ { value: '20+', label: '3D-Drucker im Haus' },
+ { value: 'Eigene', label: 'CNC- & Laserwerkstatt' },
+ { value: 'Bundesweit', label: 'anerkannter Car-Hifi-Spezialist' },
+ ],
+ brandsHeading: 'Marken, mit denen wir arbeiten',
+ howItWorks: "So funktioniert's",
+ steps: [
+ { title: 'Marke & Modell wählen', text: 'Finde dein Fahrzeug in wenigen Klicks.' },
+ { title: 'Sound-Paket entdecken', text: 'Passende Pakete inkl. aktueller Preise auf einen Blick.' },
+ { title: 'Unverbindlich anfragen', text: 'Wir melden uns zeitnah mit allen Details bei dir.' },
+ ],
+ youtubeBadge: 'HifiPlanet auf YouTube',
+ youtubeTitle: 'Sieh dir unsere Umbauten in Aktion an',
+ youtubeText: 'Umbauten, Soundchecks und Verstärker-Tests aus unserer Werkstatt – „Den besten Sound gibt es nicht ab Werk!"',
+ youtubeChecklist: ['Umbauten Schritt für Schritt', 'Soundchecks & Vergleiche', 'Verstärker- & Komponenten-Tests'],
+ youtubeCta: 'Zum YouTube-Kanal',
+ youtubeEmbedName: 'Das YouTube-Video',
+ heroImageAlt: 'Ground Zero Subwoofer und Endstufen mit violetter Ambiente-Beleuchtung',
+ craftImageAlt: 'Individueller Subwoofer-Bau',
+ adviceImageAlt: 'Endstufen-Einbau mit Ambiente-Beleuchtung',
+ moreImageAlt: 'Hochwertige Endstufe und Subwoofer, Studioaufnahme',
+ craftEyebrow: 'Handwerk',
+ craftTitle: 'Präzisionsarbeit in jedem Detail',
+ craftText: 'Ob eigens gefertigte Subwoofer-Gehäuse, integrierte Beleuchtung oder unsichtbar verlegte Kabelwege – dank eigener CNC-, Laser- und 3D-Druck-Fertigung entstehen bei uns Lösungen, die es von der Stange nicht gibt.',
+ adviceEyebrow: 'Beratung',
+ adviceTitle: 'Von der ersten Idee bis zum letzten Schliff',
+ adviceText: 'Jedes Fahrzeug und jeder Anspruch ist anders. Deshalb beraten wir dich persönlich und unverbindlich – vom dezenten Upgrade bis zum kompromisslosen High-End-System.',
+ insightsTitle: 'Einblicke in unsere Umbauten',
+ insightsText: 'Vom unauffälligen Sound-Upgrade bis zum aufwendigen Komplettumbau – Handarbeit aus unserer Werkstatt in Amorbach.',
+ galleryAlts: [
+ 'Individueller Subwoofer-Bau mit Ground Zero Bässen',
+ 'Endstufen-Einbau mit violetter Ambiente-Beleuchtung',
+ 'Hochtöner- und Mitteltöner-Einbau von Focal Utopia',
+ 'Sony Navigationssystem mit Apple CarPlay im Cockpit',
+ 'Verbauter Hochtöner im Kofferraum',
+ 'Individueller Subwoofer-Einbau mit LED-Beleuchtung im Kofferraum',
+ ],
+ reviewsRating: '5,0 von 181 Kunden bewertet',
+ reviewsText: 'Das sagen unsere Kunden über uns.',
+ reviewsLink: 'Alle Bewertungen auf Google ansehen →',
+ testimonials: [
+ { name: 'Thomas K.', text: 'Über YouTube auf dieses Klang-Juwel gestoßen. Beratung und Empfehlungen sind erstklassig – der Tesla-Vorführwagen hat meine Leidenschaft für guten Sound neu entfacht.' },
+ { name: 'Andreas I.', text: 'Sehr professionelle Beratung und Service. Obwohl ich von VAG zu Mercedes gewechselt bin, wurde alles reibungslos angepasst. Die 3-Wege-Lautsprecher und zwei Subwoofer liefern außergewöhnliche Leistung.' },
+ { name: 'Thomas F.', text: 'Kompletter Audi A4 B9 Umbau hat meine Erwartungen übertroffen. Von Anfang bis Ende professionell, mit tadelloser Verarbeitung und Klangqualität.' },
+ { name: 'Drago G.', text: 'Beeindruckt von der CNC-Arbeit, die auf YouTube gezeigt wird. Angefangen mit 3D-gedruckten Lautsprecherringen für meinen Ford Mustang – daraus wurde ein viel größeres Projekt.' },
+ { name: 'Leonardo B.', text: 'Toller Service und meisterhafte Handwerkskunst. Saubere Installation, Leidenschaft in jedem Detail erkennbar.' },
+ { name: 'Marcel S.', text: 'Unschlagbares Preis-Leistungs-Verhältnis. Das Team hat sich viel Zeit genommen, um genau die Lösung für mein Budget zu finden.' },
+ { name: 'Julia W.', text: 'Endlich ein Betrieb, der auch Wohnmobile ernst nimmt. Die Soundanlage in unserem Camper klingt jetzt wie im Wohnzimmer.' },
+ { name: 'Kevin R.', text: 'Dashcam und Alarmanlage in einem Termin sauber verbaut, keine sichtbaren Kabel. Absolute Empfehlung für alle, die Wert auf Verarbeitung legen.' },
+ { name: 'Sabrina H.', text: 'Mein Oldtimer hat jetzt modernen Sound, ohne dass es dem Original-Look geschadet hätte. Genau das habe ich gesucht.' },
+ { name: 'Niklas P.', text: 'Von der ersten Anfrage bis zum fertigen Umbau lief alles reibungslos. Ehrliche Beratung ohne Verkaufsdruck.' },
+ ],
+ moreTitle: 'Mehr als nur Car-Hifi',
+ moreText: 'Neben individuellen Sound-Umbauten bieten wir CNC-Zerspanung, Lasertechnik, 3D-Druck und mehr – alles aus einer Hand in unserer eigenen Werkstatt.',
+ moreCta: 'Alle Leistungen entdecken',
+ faqHeading: 'Häufig gestellte Fragen',
+ faqs: [
+ { question: 'Was kostet eine Beratung?', answer: 'Beratung und Preisanfrage sind für dich komplett kostenlos und unverbindlich.' },
+ { question: 'Muss ich mein Fahrzeug vorbeibringen?', answer: 'Für eine erste Einschätzung reicht oft deine Anfrage über die Website. Für den Einbau selbst vereinbaren wir gemeinsam einen Termin bei uns in Amorbach.' },
+ { question: 'Wie lange dauert ein Umbau?', answer: 'Das hängt vom Umfang ab – von einem einfachen Lautsprecher-Tausch in wenigen Stunden bis zum aufwendigen Komplettumbau über mehrere Tage.' },
+ { question: 'Bietet ihr auch Lösungen für Leasingfahrzeuge?', answer: 'Ja, auf Wunsch bauen wir reversibel um, sodass dein Fahrzeug bei Rückgabe wieder in den Originalzustand versetzt werden kann.' },
+ { question: 'Arbeitet ihr nur mit bestimmten Marken?', answer: 'Nein, wir sind herstellerunabhängig und wählen die Komponenten, die am besten zu deinem Anspruch und Budget passen.' },
+ { question: 'Was ist, wenn mein Fahrzeug nicht gelistet ist?', answer: 'Kein Problem – schreib uns einfach über das Kontaktformular, wir finden für jedes Fahrzeug eine passende Lösung.' },
+ ],
+ },
+ vehicleSelect: {
+ metaTitle: 'Fahrzeug auswählen',
+ metaDescription: 'Wähle deine Fahrzeugmarke und dein Modell und entdecke passende Car-Hifi Sound-Pakete von HifiPlanet.',
+ title: 'Fahrzeug auswählen',
+ intro: 'Wähle zuerst deine Marke, dann dein Modell – wir zeigen dir direkt die passenden Sound-Pakete.',
+ empty: 'Es sind noch keine Marken hinterlegt.',
+ },
+ brandPage: {
+ metaTitleFallback: 'Modell auswählen',
+ metaDescription: (brand) => `Wähle dein ${brand} Modell und entdecke passende Car-Hifi Sound-Pakete von HifiPlanet.`,
+ loading: 'Lädt…',
+ breadcrumbVehicles: 'Fahrzeuge',
+ titleSuffix: 'Modell wählen',
+ empty: 'Für diese Marke sind noch keine Modelle hinterlegt.',
+ },
+ modelPage: {
+ metaTitleFallback: 'Sound-Pakete',
+ metaTitle: (brand, model) => `${brand} ${model} Sound-Pakete`,
+ metaDescription: (brand, model) => `Car-Hifi Sound-Pakete für ${brand} ${model} inkl. aktueller Preise – von HifiPlanet unverbindlich anfragen.`,
+ loading: 'Lädt…',
+ breadcrumbVehicles: 'Fahrzeuge',
+ titleSuffix: 'Sound-Pakete',
+ empty: 'Für dieses Modell sind noch keine Pakete hinterlegt.',
+ totalPrice: 'Gesamtpreis (ca.)',
+ productLoading: 'Produkt wird geladen…',
+ requestContact: 'Kontakt anfragen',
+ },
+ leistungen: {
+ metaTitle: 'Leistungen',
+ metaDescription: 'Car-Hifi, Wohnmobil & Caravan, Oldtimer, CNC-Zerspanung, Lasertechnik, 3D-Druck, Alarmanlagen und Dash Cams – alles aus einer Hand bei HifiPlanet in Amorbach.',
+ title: 'Unsere Leistungen',
+ intro: 'Von individuellen Sound-Umbauten bis zur eigenen CNC- und 3D-Druck-Fertigung – alles aus einer Hand in unserer Werkstatt in Amorbach.',
+ notListedTitle: 'Dein Projekt ist nicht dabei?',
+ notListedText: 'Sprich uns einfach an – wir finden gemeinsam die passende Lösung.',
+ contact: 'Kontakt aufnehmen',
+ },
+ contactPage: {
+ metaTitle: 'Kontakt',
+ metaDescription: 'Kontaktiere HifiPlanet in Amorbach für dein individuelles Car-Hifi Projekt – wir beraten dich gerne unverbindlich.',
+ title: 'Kontakt aufnehmen',
+ sentTitle: 'Danke für deine Anfrage!',
+ sentText: 'Wir melden uns so schnell wie möglich bei dir.',
+ contextIntro: 'Deine Anfrage bezieht sich auf:',
+ contextBrand: 'Marke',
+ contextModel: 'Modell',
+ contextPackage: 'Paket',
+ contextProduct: 'Produkt',
+ packagePrice: 'Paketpreis',
+ upgrades: 'Upgrades',
+ optionalUpgrades: 'Optionale Upgrades',
+ nameLabel: 'Name *',
+ emailLabel: 'E-Mail *',
+ phoneLabel: 'Telefon',
+ vinLabel: 'Fahrgestellnummer (FIN)',
+ vinPlaceholder: 'Optional – hilft uns bei der genauen Einschätzung deines Fahrzeugs',
+ messageLabel: 'Nachricht',
+ sending: 'Wird gesendet…',
+ submit: 'Anfrage senden',
+ cardTitle: 'HifiPlanet Amorbach',
+ address: 'Adresse',
+ phone: 'Telefon',
+ email: 'E-Mail',
+ hours: 'Öffnungszeiten',
+ hoursWeek: 'Mo–Fr: 9:00–18:00 Uhr',
+ hoursSat: 'Sa: 10:00–13:00 Uhr',
+ directionsTitle: 'Anfahrt',
+ mapEmbedName: 'Die Google-Maps-Karte',
+ routePlan: 'Route planen',
+ },
+ galleryOverview: {
+ metaTitle: 'Bildergalerie',
+ metaDescription: 'Einblicke in unsere Car-Hifi Umbauten – nach Marke sortiert. Wähle eine Marke und entdecke die Projekte.',
+ title: 'Bildergalerie',
+ intro: 'Wähle eine Marke und entdecke unsere Umbauten im Detail.',
+ loading: 'Lädt…',
+ empty: 'Es sind noch keine Galerie-Marken hinterlegt.',
+ },
+ galleryBrandPage: {
+ metaTitleFallback: 'Bildergalerie',
+ metaTitle: (brand) => `${brand} Umbauten`,
+ metaDescription: (brand) => `Einblicke in unsere ${brand} Car-Hifi Umbauten.`,
+ loading: 'Lädt…',
+ breadcrumbGallery: 'Galerie',
+ titleSuffix: 'Projekt wählen',
+ empty: 'Für diese Marke sind noch keine Projekte hinterlegt.',
+ },
+ galleryProjectPage: {
+ metaTitleFallback: 'Bildergalerie',
+ metaDescription: (project) => `Fotos unseres ${project} Umbaus.`,
+ loading: 'Lädt…',
+ breadcrumbGallery: 'Galerie',
+ empty: 'Für dieses Projekt sind noch keine Fotos hinterlegt.',
+ enlargeImage: 'Bild vergrößern',
+ },
+};
diff --git a/hifi-src/src/i18n/en.js b/hifi-src/src/i18n/en.js
new file mode 100644
index 0000000..04cb5ed
--- /dev/null
+++ b/hifi-src/src/i18n/en.js
@@ -0,0 +1,233 @@
+export default {
+ nav: {
+ openMenu: 'Open menu',
+ closeMenu: 'Close menu',
+ close: 'Close',
+ adminPanel: 'Go to admin panel',
+ vehicles: 'Vehicles',
+ services: 'Services',
+ gallery: 'Gallery',
+ contact: 'Contact',
+ whatsapp: 'WhatsApp',
+ shop: 'Visit shop',
+ },
+ themeToggle: {
+ label: 'Toggle theme',
+ },
+ languageToggle: {
+ label: 'Select language',
+ },
+ footer: {
+ routePlan: 'Get directions',
+ contactHeading: 'Contact',
+ contactForm: 'Contact form',
+ hoursHeading: 'Opening hours',
+ open: 'Open',
+ closed: 'Closed',
+ days: {
+ monFri: 'Monday – Friday',
+ sat: 'Saturday',
+ sun: 'Sunday',
+ },
+ copyright: (year) => `© ${year} HifiPlanet. All information without guarantee.`,
+ imprint: 'Legal notice',
+ privacy: 'Privacy',
+ terms: 'Terms',
+ cookieSettings: 'Cookie settings',
+ },
+ cookieConsent: {
+ bannerText: 'We only use technically necessary cookies. For Google Maps and YouTube videos we need your additional consent, since data is transferred to Google in that case.',
+ moreInfo: 'More details in our',
+ privacyPolicy: 'privacy policy',
+ settings: 'Settings',
+ onlyNecessary: 'Necessary only',
+ acceptAll: 'Accept all',
+ modalTitle: 'Cookie settings',
+ modalIntro: 'Choose which categories you want to allow. More details in our',
+ necessaryTitle: 'Necessary',
+ necessaryDesc: 'Required to operate the website (e.g. login session, theme and cookie preference). Cannot be disabled.',
+ externalTitle: 'External media',
+ externalDesc: 'Google Maps and YouTube videos. Loading them transfers data (including your IP address) to Google.',
+ cancel: 'Cancel',
+ saveSelection: 'Save selection',
+ },
+ maintenance: {
+ title: 'Under maintenance',
+ defaultMessage: 'This area is currently being updated. Please check back shortly.',
+ },
+ lightbox: {
+ close: 'Close',
+ previous: 'Previous image',
+ next: 'Next image',
+ },
+ home: {
+ metaTitle: 'Custom Car-Hifi Installations',
+ metaDescription: 'HifiPlanet – your car-hifi specialist in Amorbach, Germany. Choose your make and model, discover matching sound packages and get in touch, no obligation.',
+ stickyCta: 'Ready for your sound upgrade?',
+ selectVehicle: 'Select vehicle',
+ getInTouch: 'Get in touch',
+ welcome: 'Welcome to HifiPlanet',
+ heroTitle: 'Audiophile by principle',
+ heroSubtitle: 'Your car-hifi specialist for custom sound installations. Choose your vehicle, discover the right package and get in touch, no obligation – we take care of the rest.',
+ stats: [
+ { value: 'Since 2010', label: 'in Amorbach, Germany' },
+ { value: '20+', label: '3D printers in-house' },
+ { value: 'In-house', label: 'CNC & laser workshop' },
+ { value: 'Nationwide', label: 'recognized car-hifi specialist' },
+ ],
+ brandsHeading: 'Brands we work with',
+ howItWorks: 'How it works',
+ steps: [
+ { title: 'Choose make & model', text: 'Find your vehicle in just a few clicks.' },
+ { title: 'Discover a sound package', text: 'Matching packages with current prices at a glance.' },
+ { title: 'Get in touch', text: "We'll get back to you shortly with all the details." },
+ ],
+ youtubeBadge: 'HifiPlanet on YouTube',
+ youtubeTitle: 'See our installations in action',
+ youtubeText: 'Installations, sound checks and amplifier tests straight from our workshop – "The best sound doesn\'t come from the factory!"',
+ youtubeChecklist: ['Installations step by step', 'Sound checks & comparisons', 'Amplifier & component tests'],
+ youtubeCta: 'Visit our YouTube channel',
+ youtubeEmbedName: 'The YouTube video',
+ heroImageAlt: 'Ground Zero subwoofer and amplifiers with purple ambient lighting',
+ craftImageAlt: 'Custom subwoofer build',
+ adviceImageAlt: 'Amplifier installation with ambient lighting',
+ moreImageAlt: 'High-end amplifier and subwoofer, studio shot',
+ craftEyebrow: 'Craftsmanship',
+ craftTitle: 'Precision work in every detail',
+ craftText: 'Whether custom-built subwoofer enclosures, integrated lighting or invisibly routed cabling – our own CNC, laser and 3D-printing production means we can build solutions you simply can\'t buy off the shelf.',
+ adviceEyebrow: 'Consultation',
+ adviceTitle: 'From the first idea to the final touch',
+ adviceText: "Every vehicle and every requirement is different. That's why we advise you personally and without obligation – from a subtle upgrade to an uncompromising high-end system.",
+ insightsTitle: 'A look at our installations',
+ insightsText: 'From subtle sound upgrades to elaborate full installations – handcrafted in our workshop in Amorbach.',
+ galleryAlts: [
+ 'Custom subwoofer build with Ground Zero bass drivers',
+ 'Amplifier installation with purple ambient lighting',
+ 'Tweeter and midrange installation by Focal Utopia',
+ 'Sony navigation system with Apple CarPlay in the cockpit',
+ 'Installed tweeter in the trunk',
+ 'Custom subwoofer installation with LED lighting in the trunk',
+ ],
+ reviewsRating: 'Rated 5.0 by 181 customers',
+ reviewsText: 'Here\'s what our customers say about us.',
+ reviewsLink: 'See all reviews on Google →',
+ testimonials: [
+ { name: 'Thomas K.', text: "Found this gem through YouTube. Advice and recommendations are top-notch – the Tesla demo car reignited my passion for great sound." },
+ { name: 'Andreas I.', text: 'Very professional advice and service. Even though I switched from VAG to Mercedes, everything was adapted smoothly. The 3-way speakers and two subwoofers deliver outstanding performance.' },
+ { name: 'Thomas F.', text: 'The complete Audi A4 B9 build exceeded my expectations. Professional from start to finish, with flawless workmanship and sound quality.' },
+ { name: 'Drago G.', text: 'Impressed by the CNC work shown on YouTube. Started with 3D-printed speaker rings for my Ford Mustang – it turned into a much bigger project.' },
+ { name: 'Leonardo B.', text: 'Great service and masterful craftsmanship. Clean installation, passion visible in every detail.' },
+ { name: 'Marcel S.', text: 'Unbeatable value for money. The team took a lot of time to find the exact solution for my budget.' },
+ { name: 'Julia W.', text: 'Finally a shop that takes motorhomes seriously too. The sound system in our camper now sounds like a living room.' },
+ { name: 'Kevin R.', text: 'Dash cam and alarm system installed cleanly in a single appointment, no visible cables. Highly recommended for anyone who values quality workmanship.' },
+ { name: 'Sabrina H.', text: 'My classic car now has modern sound without compromising the original look. Exactly what I was looking for.' },
+ { name: 'Niklas P.', text: 'From the first inquiry to the finished build, everything went smoothly. Honest advice with no sales pressure.' },
+ ],
+ moreTitle: 'More than just car-hifi',
+ moreText: 'Besides custom sound installations, we also offer CNC machining, laser technology, 3D printing and more – all from a single source in our own workshop.',
+ moreCta: 'Discover all our services',
+ faqHeading: 'Frequently asked questions',
+ faqs: [
+ { question: 'How much does a consultation cost?', answer: 'A consultation and price estimate are completely free and non-binding.' },
+ { question: 'Do I need to bring my vehicle in?', answer: 'For an initial assessment, your inquiry through the website is often enough. For the installation itself, we\'ll schedule an appointment together at our workshop in Amorbach.' },
+ { question: 'How long does an installation take?', answer: 'It depends on the scope – from a simple speaker swap in a few hours to an elaborate full installation over several days.' },
+ { question: 'Do you offer solutions for leased vehicles?', answer: 'Yes, on request we install everything reversibly, so your vehicle can be returned to its original condition when handed back.' },
+ { question: 'Do you only work with certain brands?', answer: "No, we're brand-independent and choose the components that best fit your needs and budget." },
+ { question: "What if my vehicle isn't listed?", answer: "No problem – just message us through the contact form, we'll find a suitable solution for any vehicle." },
+ ],
+ },
+ vehicleSelect: {
+ metaTitle: 'Select your vehicle',
+ metaDescription: 'Choose your vehicle make and model and discover matching car-hifi sound packages from HifiPlanet.',
+ title: 'Select your vehicle',
+ intro: 'Choose your make first, then your model – we\'ll show you the matching sound packages right away.',
+ empty: 'No brands have been added yet.',
+ },
+ brandPage: {
+ metaTitleFallback: 'Select a model',
+ metaDescription: (brand) => `Choose your ${brand} model and discover matching car-hifi sound packages from HifiPlanet.`,
+ loading: 'Loading…',
+ breadcrumbVehicles: 'Vehicles',
+ titleSuffix: 'Select a model',
+ empty: 'No models have been added for this brand yet.',
+ },
+ modelPage: {
+ metaTitleFallback: 'Sound packages',
+ metaTitle: (brand, model) => `${brand} ${model} sound packages`,
+ metaDescription: (brand, model) => `Car-hifi sound packages for the ${brand} ${model} including current prices – get in touch with HifiPlanet, no obligation.`,
+ loading: 'Loading…',
+ breadcrumbVehicles: 'Vehicles',
+ titleSuffix: 'Sound packages',
+ empty: 'No packages have been added for this model yet.',
+ totalPrice: 'Total price (approx.)',
+ productLoading: 'Loading product…',
+ requestContact: 'Request contact',
+ },
+ leistungen: {
+ metaTitle: 'Services',
+ metaDescription: 'Car-hifi, motorhomes & caravans, classic cars, CNC machining, laser technology, 3D printing, alarm systems and dash cams – all from a single source at HifiPlanet in Amorbach.',
+ title: 'Our services',
+ intro: 'From custom sound installations to our own CNC and 3D-printing production – all from a single source in our workshop in Amorbach.',
+ notListedTitle: "Don't see your project?",
+ notListedText: "Just get in touch – we'll find the right solution together.",
+ contact: 'Get in touch',
+ },
+ contactPage: {
+ metaTitle: 'Contact',
+ metaDescription: 'Get in touch with HifiPlanet in Amorbach for your custom car-hifi project – we\'re happy to advise you, no obligation.',
+ title: 'Get in touch',
+ sentTitle: 'Thanks for your inquiry!',
+ sentText: "We'll get back to you as soon as possible.",
+ contextIntro: 'Your inquiry relates to:',
+ contextBrand: 'Brand',
+ contextModel: 'Model',
+ contextPackage: 'Package',
+ contextProduct: 'Product',
+ packagePrice: 'Package price',
+ upgrades: 'Upgrades',
+ optionalUpgrades: 'Optional upgrades',
+ nameLabel: 'Name *',
+ emailLabel: 'Email *',
+ phoneLabel: 'Phone',
+ vinLabel: 'Vehicle identification number (VIN)',
+ vinPlaceholder: 'Optional – helps us assess your vehicle accurately',
+ messageLabel: 'Message',
+ sending: 'Sending…',
+ submit: 'Send inquiry',
+ cardTitle: 'HifiPlanet Amorbach',
+ address: 'Address',
+ phone: 'Phone',
+ email: 'Email',
+ hours: 'Opening hours',
+ hoursWeek: 'Mon–Fri: 9:00 AM–6:00 PM',
+ hoursSat: 'Sat: 10:00 AM–1:00 PM',
+ directionsTitle: 'Directions',
+ mapEmbedName: 'The Google Maps map',
+ routePlan: 'Get directions',
+ },
+ galleryOverview: {
+ metaTitle: 'Photo gallery',
+ metaDescription: 'A look at our car-hifi installations – sorted by brand. Choose a brand and explore the projects.',
+ title: 'Photo gallery',
+ intro: 'Choose a brand and explore our installations in detail.',
+ loading: 'Loading…',
+ empty: 'No gallery brands have been added yet.',
+ },
+ galleryBrandPage: {
+ metaTitleFallback: 'Photo gallery',
+ metaTitle: (brand) => `${brand} installations`,
+ metaDescription: (brand) => `A look at our ${brand} car-hifi installations.`,
+ loading: 'Loading…',
+ breadcrumbGallery: 'Gallery',
+ titleSuffix: 'Select a project',
+ empty: 'No projects have been added for this brand yet.',
+ },
+ galleryProjectPage: {
+ metaTitleFallback: 'Photo gallery',
+ metaDescription: (project) => `Photos of our ${project} installation.`,
+ loading: 'Loading…',
+ breadcrumbGallery: 'Gallery',
+ empty: 'No photos have been added for this project yet.',
+ enlargeImage: 'Enlarge image',
+ },
+};
diff --git a/hifi-src/src/i18n/index.js b/hifi-src/src/i18n/index.js
new file mode 100644
index 0000000..4817674
--- /dev/null
+++ b/hifi-src/src/i18n/index.js
@@ -0,0 +1,4 @@
+import de from './de.js';
+import en from './en.js';
+
+export const dictionaries = { de, en };
diff --git a/hifi-src/src/pages/public/BrandPage.jsx b/hifi-src/src/pages/public/BrandPage.jsx
index 3030db5..eb5ed88 100644
--- a/hifi-src/src/pages/public/BrandPage.jsx
+++ b/hifi-src/src/pages/public/BrandPage.jsx
@@ -5,12 +5,14 @@ import usePageMeta from '../../hooks/usePageMeta.js';
import MaintenanceNotice from '../../components/MaintenanceNotice.jsx';
import MaintenanceBypassBanner from '../../components/MaintenanceBypassBanner.jsx';
import { useMaintenance } from '../../context/MaintenanceContext.jsx';
+import { useLanguage } from '../../context/LanguageContext.jsx';
export default function BrandPage() {
const { brandSlug } = useParams();
const [data, setData] = useState(null);
const [error, setError] = useState('');
const maintenance = useMaintenance();
+ const { t } = useLanguage();
useEffect(() => {
if (maintenance.vehicles.enabled && !maintenance.bypass) return;
@@ -21,10 +23,8 @@ export default function BrandPage() {
}, [brandSlug, maintenance.vehicles.enabled, maintenance.bypass]);
usePageMeta({
- title: data ? `${data.brand.name} Modelle` : 'Modell auswählen',
- description: data
- ? `Wähle dein ${data.brand.name} Modell und entdecke passende Car-Hifi Sound-Pakete von HifiPlanet.`
- : undefined,
+ title: data ? `${data.brand.name} ${t('brandPage.titleSuffix')}` : t('brandPage.metaTitleFallback'),
+ description: data ? t('brandPage.metaDescription')(data.brand.name) : undefined,
path: `/fahrzeuge/${brandSlug}`,
});
@@ -37,21 +37,21 @@ export default function BrandPage() {
}
if (!data) {
- return Lädt…
;
+ return {t('brandPage.loading')}
;
}
return (
{maintenance.vehicles.enabled && maintenance.bypass &&
}
- Fahrzeuge / {data.brand.name}
+ {t('brandPage.breadcrumbVehicles')} / {data.brand.name}
- {data.brand.name} – Modell wählen
+ {data.brand.name} – {t('brandPage.titleSuffix')}
{data.models.length === 0 && (
-
Für diese Marke sind noch keine Modelle hinterlegt.
+
{t('brandPage.empty')}
)}
diff --git a/hifi-src/src/pages/public/ContactPage.jsx b/hifi-src/src/pages/public/ContactPage.jsx
index 6cb5b09..b6fce9b 100644
--- a/hifi-src/src/pages/public/ContactPage.jsx
+++ b/hifi-src/src/pages/public/ContactPage.jsx
@@ -4,9 +4,7 @@ import { api } from '../../api/client.js';
import usePageMeta from '../../hooks/usePageMeta.js';
import ExternalEmbed from '../../components/ExternalEmbed.jsx';
import { useSiteSettings } from '../../context/SiteSettingsContext.jsx';
-
-const formatPrice = (value) =>
- new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(value);
+import { useLanguage } from '../../context/LanguageContext.jsx';
const digitsOnly = (value) => (value || '').replace(/[^\d+]/g, '');
@@ -15,6 +13,9 @@ const SHOP_ADDRESS_ENCODED = encodeURIComponent('Boxbrunner Str. 20a, 63916 Amor
export default function ContactPage() {
const [params] = useSearchParams();
const { phone, contact_email: contactEmail } = useSiteSettings();
+ const { t, language } = useLanguage();
+ const formatPrice = (value) =>
+ new Intl.NumberFormat(language === 'de' ? 'de-DE' : 'en-US', { style: 'currency', currency: 'EUR' }).format(value);
const [form, setForm] = useState({ name: '', email: '', phone: '', vin: '', message: '' });
const [status, setStatus] = useState('idle');
const [error, setError] = useState('');
@@ -41,8 +42,8 @@ export default function ContactPage() {
.reduce((sum, u) => sum + Number(u.price), 0);
usePageMeta({
- title: 'Kontakt',
- description: 'Kontaktiere HifiPlanet in Amorbach für dein individuelles Car-Hifi Projekt – wir beraten dich gerne unverbindlich.',
+ title: t('contactPage.metaTitle'),
+ description: t('contactPage.metaDescription'),
path: '/kontakt',
});
@@ -81,30 +82,30 @@ export default function ContactPage() {
return (
-
Kontakt aufnehmen
+
{t('contactPage.title')}
{status === 'sent' ? (
-
Danke für deine Anfrage!
-
Wir melden uns so schnell wie möglich bei dir.
+
{t('contactPage.sentTitle')}
+
{t('contactPage.sentText')}
) : (
<>
{hasContext && (
-
Deine Anfrage bezieht sich auf:
+
{t('contactPage.contextIntro')}
- {context.brand && Marke: {context.brand} }
- {context.model && Modell: {context.model} }
- {context.package && Paket: {context.package} }
- {context.product && Produkt: {context.product} }
+ {context.brand && {t('contactPage.contextBrand')}: {context.brand} }
+ {context.model && {t('contactPage.contextModel')}: {context.model} }
+ {context.package && {t('contactPage.contextPackage')}: {context.package} }
+ {context.product && {t('contactPage.contextProduct')}: {context.product} }
{packageTotal != null && (
- Paketpreis: {formatPrice(packageTotal)}
- {upgradesTotal > 0 && <> + {formatPrice(upgradesTotal)} Upgrades = {formatPrice(packageTotal + upgradesTotal)}>}
+ {t('contactPage.packagePrice')}: {formatPrice(packageTotal)}
+ {upgradesTotal > 0 && <> + {formatPrice(upgradesTotal)} {t('contactPage.upgrades')} = {formatPrice(packageTotal + upgradesTotal)}>}
)}
@@ -112,7 +113,7 @@ export default function ContactPage() {
{upgrades.length > 0 && (
-
Optionale Upgrades
+
{t('contactPage.optionalUpgrades')}
{upgrades.map((u) => (
@@ -135,7 +136,7 @@ export default function ContactPage() {