mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 06:06:10 +00:00
fix(i18n): one German word per thing across Settings
Anbieter instead of Provider, Standard instead of Default, Gesundheitsscore for every spelling of the score, and Insights where the area was called Auswertungen. The Coach's rate-limit notice no longer shows English in the German interface. A guard reads the Settings namespaces of the German catalog and fails on the retired spellings.
This commit is contained in:
1 parent
ec240bc07a
commit
131b48cc49
4 files changed
+79
-24
No files matched your search
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* The German Settings glossary: one word per thing.
|
||||
*
|
||||
* Settings used "Provider" beside "Anbieter", "Default" beside "Standard",
|
||||
* and the score as "Health Score", "Health-Score", "Gesundheits-Score" and
|
||||
* "Gesundheitsscore", sometimes on one screen. The settled words are
|
||||
* Anbieter, Standard and Gesundheitsscore; the Insights area is "Insights"
|
||||
* (the name already dominant in the catalog). This reads the German catalog
|
||||
* under the Settings namespaces and fails on the retired spellings.
|
||||
*
|
||||
* Product names are not glossary words: "ChatGPT", "OpenAI" and the like are
|
||||
* left alone, and so is "Provider" inside a code-shaped token.
|
||||
*/
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import de from "../../messages/de.json";
|
||||
|
||||
const NAMESPACES = ["settings", "thresholds"] as const;
|
||||
|
||||
const RETIRED: ReadonlyArray<{ pattern: RegExp; use: string }> = [
|
||||
{ pattern: /\bProvider\b/, use: "Anbieter" },
|
||||
{ pattern: /\bDefaults?\b/, use: "Standard" },
|
||||
{ pattern: /\bHealth[- ]?Score\b/i, use: "Gesundheitsscore" },
|
||||
{ pattern: /\bGesundheits-Score\b/, use: "Gesundheitsscore" },
|
||||
];
|
||||
|
||||
function walk(node: unknown, path: string, out: Array<[string, string]>) {
|
||||
if (typeof node === "string") out.push([path, node]);
|
||||
else if (node && typeof node === "object") {
|
||||
for (const [key, value] of Object.entries(node)) {
|
||||
walk(value, path ? `${path}.${key}` : key, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe("German Settings glossary", () => {
|
||||
const strings: Array<[string, string]> = [];
|
||||
for (const ns of NAMESPACES) {
|
||||
walk((de as Record<string, unknown>)[ns], ns, strings);
|
||||
}
|
||||
|
||||
it("reads the catalog (an empty read is a broken walker)", () => {
|
||||
expect(strings.length).toBeGreaterThan(500);
|
||||
});
|
||||
|
||||
it("uses the settled words", () => {
|
||||
const offenders: string[] = [];
|
||||
for (const [key, value] of strings) {
|
||||
for (const { pattern, use } of RETIRED) {
|
||||
if (pattern.test(value)) offenders.push(`${key}: "${value}" → ${use}`);
|
||||
}
|
||||
}
|
||||
expect(offenders).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -218,7 +218,7 @@ describe("<AiSection> — dropdown-driven UX (B2)", () => {
|
||||
// both ship without the "KI"/"AI" prefix per the project-voice rule
|
||||
// ("Aktiver KI-Provider" → "Aktiver Provider"). The picker below it is
|
||||
// labelled for what it does: it chooses the form, not the order.
|
||||
expect(html).toContain("Aktiver Provider");
|
||||
expect(html).toContain("Aktiver Anbieter");
|
||||
expect(html).toContain("Anbieter einrichten");
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user