From 23eae2823e6f8e3b55caa1cf2b9dd40cbb4fe12e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 7 Aug 2026 23:20:12 +0200 Subject: [PATCH] refactor(i18n): shorten the five descriptions the gate could not see MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gate the diet shipped with matched ` + {/* What the app does and does not claim about a trace. Its own + paragraph, so the page explainer above stays one sentence. */} +

+ {t("insights.subPage.ecgDisclaimer")} +

); diff --git a/src/components/labs/labs-settings.tsx b/src/components/labs/labs-settings.tsx index bed665cba..0f7bb3cae 100644 --- a/src/components/labs/labs-settings.tsx +++ b/src/components/labs/labs-settings.tsx @@ -144,6 +144,9 @@ export function LabsSettings() { /> } /> + {/* "Less accurate — review every value" is safety copy, so it reads + foreground in the body rather than muted beside the switch. */} +

{t("labs.localOcr.detail")}

{/* Biomarker catalog — define / edit / delete. */} diff --git a/src/components/settings/__tests__/card-description-length.test.ts b/src/components/settings/__tests__/card-description-length.test.ts index 66e04c492..ca2969100 100644 --- a/src/components/settings/__tests__/card-description-length.test.ts +++ b/src/components/settings/__tests__/card-description-length.test.ts @@ -7,10 +7,10 @@ import { join } from "node:path"; * (design standards §3). * * Scope is deliberately the strings that actually reach a card header's - * description slot — `` — because - * that is the slot the rule is about. A hint in a card body is prose and is - * allowed to be prose; `scripts/measure-description-lengths.mjs` sweeps those - * for review without gating them. + * description slot, whether the prop sits on the header or on a shell that + * forwards it. That is the slot the rule is about. A hint in a card BODY is + * prose and is allowed to be prose; `scripts/measure-description-lengths.mjs` + * sweeps those for review without gating them. * * Both EN and DE are checked. DE runs 15-25% longer than EN, and a rule that * only holds in English produces a slot that wraps to two lines in half the @@ -46,19 +46,57 @@ function walk(dir: string, out: string[] = []): string[] { return out; } -/** Every `description={t("…")}` inside a `` element. */ +/** + * Elements whose `description` prop reaches `SettingsCardHeader`'s slot. + * + * The first cut of this guard matched `` — the body tier, where the + * standards allow prose. Gating it would enforce a rule §3 does not make for + * that tier. + */ +const DESCRIPTION_HOSTS = [ + "SettingsCardHeader", + "ExportCardShell", + "ImportCardShell", +]; + +/** Component roots that can host a card header, directly or via a shell. */ +const ROOTS = [ + join("src", "components", "settings"), + join("src", "components", "admin"), + join("src", "components", "measurement-reminders"), + join("src", "components", "labs"), + join("src", "components", "insights"), + join("src", "components", "medications"), + join("src", "components", "records"), +]; + +/** Every `description={t("…")}` on an element that owns the header's slot. */ function describedKeys(): Map { const keys = new Map(); - for (const dir of ["settings", "admin", "measurement-reminders"]) { - for (const file of walk(join(ROOT, "src", "components", dir))) { + for (const root of ROOTS) { + for (const file of walk(join(ROOT, root))) { const source = readFileSync(file, "utf8"); - const header = /` or `>` that closes the opening // tag; a `description={t("x")}` after that belongs to something else. - const end = source.indexOf("/>", match.index); - const slice = source.slice(match.index, end === -1 ? undefined : end); + const selfClose = source.indexOf("/>", match.index); + const childrenOpen = source.indexOf("\n >", match.index); + const end = [selfClose, childrenOpen] + .filter((i) => i !== -1) + .sort((a, b) => a - b)[0]; + const slice = source.slice(match.index, end ?? undefined); const desc = /description=\{t\(\s*"([^"]+)"/.exec(slice); if (!desc) continue; const list = keys.get(desc[1]) ?? []; @@ -96,7 +134,7 @@ describe("card header descriptions — one sentence, ≤120 characters", () => { it("finds the description slots it is meant to check", () => { // A matcher that quietly matches nothing passes every assertion below it. - expect(keys.size).toBeGreaterThan(25); + expect(keys.size).toBeGreaterThan(35); }); for (const locale of ["en", "de"] as const) { diff --git a/src/components/settings/export-section.tsx b/src/components/settings/export-section.tsx index 68572a87e..faebdaf83 100644 --- a/src/components/settings/export-section.tsx +++ b/src/components/settings/export-section.tsx @@ -484,7 +484,16 @@ function FullBackupCard() { } > - {/* v1.28 backup-completeness — the description above lists what's + {/* What is in the file, and the shape it has. This used to be the + card's description — twelve enumerated record types in a muted + `text-xs` meta slot, which is neither the tier nor the length that + slot is for. */} +

+ {t("settings.sections.export.cards.fullBackup.contents")}{" "} + {t("settings.sections.export.cards.fullBackup.detail")} +

+ + {/* v1.28 backup-completeness — the paragraph above lists what's included; this line honestly discloses what isn't (document binaries, workout GPS/sample series) rather than implying "everything" is in the file. Always visible, not gated on the diff --git a/src/components/settings/import-panel/csv-import-card.tsx b/src/components/settings/import-panel/csv-import-card.tsx index 6be0e730e..88f956bd6 100644 --- a/src/components/settings/import-panel/csv-import-card.tsx +++ b/src/components/settings/import-panel/csv-import-card.tsx @@ -113,6 +113,10 @@ export function CsvImportCard() { title={t("settings.sections.export.import.csv.title")} description={t("settings.sections.export.import.csv.description")} > +

+ {t("settings.sections.export.import.csv.detail")} +

+