Files
portfolio/app/components/ClientWrappers.tsx
T
denshooter 31560a712f
CI / CD / test-build (push) Failing after 5m43s
CI / CD / deploy-dev (push) Has been skipped
CI / CD / deploy-production (push) Has been skipped
feat: comprehensive UI/a11y/i18n fixes and pre-push quality test
- Fix ClientWrappers missing 'about' namespace (MISSING_MESSAGE error)
- Add system/light/dark theme toggle with prefers-color-scheme detection
- Rewrite 404 page with i18n, accessibility, and proper navigation
- Rewrite books page with Header/Footer, i18n, and semantic HTML
- Add i18n keys to About, Footer, and both locale files
- Fix dark mode contrast: text-stone-300/600 -> text-stone-400
- Replace raw hex bg-[#fdfcf8] with bg-stone-50 across all components
- Guard console.error in ChatWidget and manage/page behind NODE_ENV
- Add aria-label to admin login form
- Remove emoji from manage page password toggle
- Update stale dates in privacy-policy and legal-notice
- Fix ScrollFadeIn index->delay prop type error in books page
- Fix privacy-policy and legal-notice landmark structure
- Add pre-push-check.test.ts: 13-category static analysis
  (i18n parity, namespace coverage, key resolution, accessibility,
   email validation, hex colors, emojis, console guards, env docs, types)
- Add explicit i18n check step to CI workflow
2026-05-14 15:42:52 +02:00

98 lines
2.6 KiB
TypeScript

"use client";
/**
* Transitional Wrapper für bestehende Components
* Nutzt direkt JSON Messages statt komplexe Translation-Loader
*/
import { NextIntlClientProvider } from 'next-intl';
import dynamic from 'next/dynamic';
// Lazy-load below-fold components so their JS doesn't block initial paint / LCP.
// SSR stays on (default) so content is in the initial HTML for SEO.
const About = dynamic(() => import('./About'));
const Projects = dynamic(() => import('./Projects'));
const Contact = dynamic(() => import('./Contact'));
const Footer = dynamic(() => import('./Footer'));
import type {
AboutTranslations,
ProjectsTranslations,
ContactTranslations,
FooterTranslations,
} from '@/types/translations';
import enMessages from '@/messages/en.json';
import deMessages from '@/messages/de.json';
const messageMap = { en: enMessages, de: deMessages };
function getNormalizedLocale(locale: string): 'en' | 'de' {
return locale.startsWith('de') ? 'de' : 'en';
}
export function AboutClient({ locale }: { locale: string; translations: AboutTranslations }) {
const normalLocale = getNormalizedLocale(locale);
const baseMessages = messageMap[normalLocale];
const messages = {
home: {
about: baseMessages.home.about
},
about: baseMessages.about
};
return (
<NextIntlClientProvider locale={locale} messages={messages}>
<About />
</NextIntlClientProvider>
);
}
export function ProjectsClient({ locale }: { locale: string; translations: ProjectsTranslations }) {
const normalLocale = getNormalizedLocale(locale);
const baseMessages = messageMap[normalLocale];
const messages = {
home: {
projects: baseMessages.home.projects
}
};
return (
<NextIntlClientProvider locale={locale} messages={messages}>
<Projects />
</NextIntlClientProvider>
);
}
export function ContactClient({ locale }: { locale: string; translations: ContactTranslations }) {
const normalLocale = getNormalizedLocale(locale);
const baseMessages = messageMap[normalLocale];
const messages = {
home: {
contact: baseMessages.home.contact
}
};
return (
<NextIntlClientProvider locale={locale} messages={messages}>
<Contact />
</NextIntlClientProvider>
);
}
export function FooterClient({ locale }: { locale: string; translations: FooterTranslations }) {
const normalLocale = getNormalizedLocale(locale);
const baseMessages = messageMap[normalLocale];
const messages = {
footer: baseMessages.footer
};
return (
<NextIntlClientProvider locale={locale} messages={messages}>
<Footer />
</NextIntlClientProvider>
);
}