Files
HealthLog/src/components/settings/__tests__/sections-i18n-parity.test.ts
T
Marc-André Bombeck 33d7d103da feat(settings): standardize settings and admin section framing
Introduce a shared SettingsSectionFrame (the settings twin of the admin
renderer's SectionFrame) that emits the canonical visible heading +
subheading, and route all 19 settings sections through it. Every settings
page now shows a real h1 + subtitle in the reference Übersicht style
instead of an invisible sr-only title; the section bodies drop their own
headings and off-style intro paragraphs (the explanatory copy folds into
the frame subtitle).

- Route the admin About page through SectionFrame so it carries the same
  visible heading as every other admin section.
- Rename the AI settings section from KI-Auswertung to KI-Anbieter across
  six locales: the page configures the provider and key, not an analysis.
- Add a heading-block spacer to the settings and admin sidebars so the
  first menu item lines up with the top of the first card.
- Remove the always-on core-domains card from Module: a tracking area that
  can't be turned off doesn't need listing.
- Move the proactive-coach control out of Benachrichtigungen into Coach.
- Move admin module availability out of the Coach section into its own
  page: it gates every module, not just the coach.
- Give the Layout Insights editor a proper Insights heading.

Adds settings.sections.<slug>.subtitle and the new admin keys across all
six locales; removes the now-unused module core-domain strings.
2026-06-18 21:28:33 +02:00

94 lines
3.1 KiB
TypeScript

/**
* v1.4.16 phase B6 — settings i18n parity.
*
* Every slug declared in `SETTINGS_SECTION_SLUGS` MUST have a
* `settings.sections.<slug>.title` AND a `settings.sections.<slug>.subtitle`
* resolved in both English and German. The sidebar reads `.title`; the shared
* `<SettingsSectionFrame>` reads `.title` + `.subtitle` for the visible page
* heading + subheading (v1.18.6 W9), so a missing key paints a raw key in
* production.
*/
import { describe, expect, it } from "vitest";
import en from "../../../../messages/en.json" with { type: "json" };
import de from "../../../../messages/de.json" with { type: "json" };
import { SETTINGS_SECTION_SLUGS } from "../section-slugs";
type Bag = Record<string, unknown>;
function lookup(bag: Bag, path: string[]): string | null {
let cursor: unknown = bag;
for (const segment of path) {
if (typeof cursor !== "object" || cursor === null) return null;
cursor = (cursor as Bag)[segment];
}
return typeof cursor === "string" ? cursor : null;
}
describe("settings sections — i18n parity (B6)", () => {
for (const slug of SETTINGS_SECTION_SLUGS) {
it(`every locale resolves settings.sections.${slug}.title`, () => {
const enValue = lookup(en as Bag, [
"settings",
"sections",
slug,
"title",
]);
const deValue = lookup(de as Bag, [
"settings",
"sections",
slug,
"title",
]);
expect(enValue, `EN missing title for ${slug}`).toBeTypeOf("string");
expect(enValue?.length ?? 0).toBeGreaterThan(0);
expect(deValue, `DE missing title for ${slug}`).toBeTypeOf("string");
expect(deValue?.length ?? 0).toBeGreaterThan(0);
});
it(`every locale resolves settings.sections.${slug}.subtitle`, () => {
const enValue = lookup(en as Bag, [
"settings",
"sections",
slug,
"subtitle",
]);
const deValue = lookup(de as Bag, [
"settings",
"sections",
slug,
"subtitle",
]);
expect(enValue, `EN missing subtitle for ${slug}`).toBeTypeOf("string");
expect(enValue?.length ?? 0).toBeGreaterThan(0);
expect(deValue, `DE missing subtitle for ${slug}`).toBeTypeOf("string");
expect(deValue?.length ?? 0).toBeGreaterThan(0);
});
}
it("EN and DE subtitle for every slug are different (catches accidental copy-paste)", () => {
for (const slug of SETTINGS_SECTION_SLUGS) {
const enValue = lookup(en as Bag, [
"settings",
"sections",
slug,
"subtitle",
]);
const deValue = lookup(de as Bag, [
"settings",
"sections",
slug,
"subtitle",
]);
// It's legitimate for some titles to coincide between locales when
// the proper noun is the same (e.g. "Dashboard", "API & Tokens").
// Descriptions, however, MUST be translated — a copy-paste of the
// EN value into the DE bag is a bug.
expect(
deValue,
`DE subtitle for ${slug} is identical to EN — needs translation`,
).not.toBe(enValue);
}
});
});