mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 06:06:10 +00:00
fix(i18n): drop dot and dash separators from visible strings
Every bundle value that chained facts with " · " now reads as a phrase, a comma list or a parenthesis, in all seven locales, and the em-dash separators in the day, documents, correlation, sharing, environment, vaccination and Apple Health import strings become sentences or clauses. The day view's running list names the record first, with the dose in the name and a life event's category as a chip, and puts a quiet day count below: "Day 4, since 31 December" instead of a dot chain. Document meta, location periods, vaccination rows and mood crosstab rows drop their composed dots too. A guard walks every bundle and fails on any " · " outside a reasoned allowlist, and on an allowlist entry that no longer needs its place.
This commit is contained in:
1 parent
d8a3614120
commit
94542e7f2d
39 files changed
+1027
-881
No files matched your search
@@ -0,0 +1,76 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { readFileSync, readdirSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
||||
/**
|
||||
* No middle-dot separator in visible strings.
|
||||
*
|
||||
* A value like "Added {date} · {size}" or "Weight · body fat · muscle" reads
|
||||
* as a list of fragments rather than as the app's plain voice. Facts are
|
||||
* joined with a comma, a colon or a real phrase instead. This guard walks
|
||||
* every bundle under `messages/` and fails on any string that contains a
|
||||
* space, U+00B7 and a space. A middle dot inside a unit ("mL/(kg·min)") has
|
||||
* no surrounding spaces and is not matched.
|
||||
*
|
||||
* A key that genuinely needs the pattern goes into `ALLOWED` with the reason
|
||||
* written next to it. An allowed key that no longer contains the pattern
|
||||
* fails too, so the list cannot go stale.
|
||||
*/
|
||||
|
||||
const MESSAGES_DIR = join(__dirname, "../../messages");
|
||||
const SEPARATOR = " · ";
|
||||
|
||||
/** Key path -> why the separator is right there. Empty by intent. */
|
||||
const ALLOWED: Record<string, string> = {};
|
||||
|
||||
function flatten(
|
||||
obj: unknown,
|
||||
prefix: string,
|
||||
out: Map<string, string>,
|
||||
): Map<string, string> {
|
||||
if (obj == null || typeof obj !== "object") return out;
|
||||
for (const [k, v] of Object.entries(obj as Record<string, unknown>)) {
|
||||
const key = prefix ? `${prefix}.${k}` : k;
|
||||
if (typeof v === "string") out.set(key, v);
|
||||
else if (typeof v === "object") flatten(v, key, out);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const bundles = readdirSync(MESSAGES_DIR)
|
||||
.filter((file) => file.endsWith(".json"))
|
||||
.sort();
|
||||
|
||||
describe("message bundles carry no ' · ' separator", () => {
|
||||
it("finds the bundles", () => {
|
||||
expect(bundles.length).toBeGreaterThanOrEqual(7);
|
||||
});
|
||||
|
||||
for (const file of bundles) {
|
||||
const values = flatten(
|
||||
JSON.parse(readFileSync(join(MESSAGES_DIR, file), "utf8")),
|
||||
"",
|
||||
new Map(),
|
||||
);
|
||||
|
||||
it(`${file} walks a non-empty set of strings`, () => {
|
||||
expect(values.size).toBeGreaterThan(1000);
|
||||
});
|
||||
|
||||
it(`${file} has no dot separator outside the allowlist`, () => {
|
||||
const offenders = [...values]
|
||||
.filter(
|
||||
([key, value]) => value.includes(SEPARATOR) && !(key in ALLOWED),
|
||||
)
|
||||
.map(([key, value]) => `${key} = ${JSON.stringify(value)}`);
|
||||
expect(offenders).toEqual([]);
|
||||
});
|
||||
|
||||
it(`${file} has no stale allowlist entry`, () => {
|
||||
const stale = Object.keys(ALLOWED).filter(
|
||||
(key) => !(values.get(key) ?? "").includes(SEPARATOR),
|
||||
);
|
||||
expect(stale).toEqual([]);
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user