content: replace scaffolding with bilingual pages and case studies

Ship the final German and English copy, compose every route from shared page primitives, and cover claims, parity and contact briefing in tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-25 17:34:04 +02:00
parent 9ea2885c7b
commit d954361724
95 changed files with 4161 additions and 419 deletions

View File

@@ -0,0 +1,115 @@
import { APP_LOCALES } from '../i18n/locale';
import { ROUTE_IDS } from '../routing/route-ids';
import { type SiteContent } from './content.contracts';
import { SITE_CONTENT_DATA } from './site-content';
const TITLE_SUFFIX = ' | Antonio Ledebuhr';
const FORBIDDEN =
/lorem|todo|tbd|platzhaltertext|placeholder text|wird derzeit aufgebaut|being built|coming soon|xxx/i;
function walkStrings(
value: unknown,
visit: (text: string, path: string) => void,
path = '',
skipKeys: ReadonlySet<string> = new Set(),
): void {
if (typeof value === 'string') {
visit(value, path);
return;
}
if (Array.isArray(value)) {
value.forEach((item, index) => walkStrings(item, visit, `${path}[${index}]`, skipKeys));
return;
}
if (value && typeof value === 'object') {
for (const [key, child] of Object.entries(value)) {
if (skipKeys.has(key)) {
continue;
}
walkStrings(child, visit, path ? `${path}.${key}` : key, skipKeys);
}
}
}
describe('content completeness', () => {
it('covers every route in both locales with real page copy', () => {
for (const locale of APP_LOCALES) {
const site = SITE_CONTENT_DATA[locale];
expect(site.pages.home).toBe(site.home);
expect(site.pages.contact).toBe(site.contact);
expect(site.pages.stack).toBe(site.stack);
expect(site.pages.imprint).toBe(site.legal.imprint);
expect(site.pages.privacy).toBe(site.legal.privacy);
expect(site.pages.servicesAi).toBe(site.services.servicesAi);
for (const routeId of ROUTE_IDS) {
const page = site.pages[routeId];
expect(page, `${locale}.${routeId}`).toBeTruthy();
expect(page.title.length).toBeGreaterThan(0);
expect(page.title.endsWith(TITLE_SUFFIX)).toBe(true);
expect(page.description.length).toBeGreaterThanOrEqual(60);
expect(page.hero.headline.length).toBeGreaterThan(0);
expect(page.hero.headline.includes(TITLE_SUFFIX)).toBe(false);
if (routeId !== 'notFound') {
const filledSections = page.sections.filter((section) =>
(section.body ?? []).some((paragraph) => paragraph.trim().length > 0),
);
expect(filledSections.length, `${locale}.${routeId} sections`).toBeGreaterThanOrEqual(2);
}
}
}
});
it('keeps the content tree free of scaffolding, except legal review fields', () => {
const skipped = new Set(['reviewNotice', 'reviewTodos']);
for (const locale of APP_LOCALES) {
const site = SITE_CONTENT_DATA[locale];
const hits: string[] = [];
walkStrings(
site,
(text, path) => {
if (FORBIDDEN.test(text)) {
hits.push(`${path}: ${text}`);
}
},
locale,
skipped,
);
expect(hits).toEqual([]);
for (const legalId of ['imprint', 'privacy'] as const) {
const legal = site.legal[legalId];
expect(legal.reviewNotice.trim().length).toBeGreaterThan(20);
expect(legal.reviewTodos.length).toBeGreaterThan(0);
expect(legal.reviewTodos.every((item) => item.trim().length > 0)).toBe(true);
}
}
});
it('does not treat legal review fields as an implicit skip of the rest of the legal pages', () => {
for (const locale of APP_LOCALES) {
const site: SiteContent = SITE_CONTENT_DATA[locale];
for (const legalId of ['imprint', 'privacy'] as const) {
const legal = site.legal[legalId];
const remainderHits: string[] = [];
walkStrings(
{ hero: legal.hero, sections: legal.sections, legalSections: legal.legalSections },
(text, path) => {
if (FORBIDDEN.test(text)) {
remainderHits.push(`${path}: ${text}`);
}
},
`${locale}.${legalId}`,
);
expect(remainderHits).toEqual([]);
}
}
});
});