mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 06:40:50 +00:00
feat(i18n): land locale-native date format ordering for FR/ES/IT/PL
Adds a new `format` namespace across every locale bundle that
documents the locale's native date / time pattern ordering as
template strings (`{day}.{month}.{year}`, `{month}/{day}/{year}`, …).
DE keeps DD.MM.YYYY with dot separators, EN keeps MM/DD/YYYY with
slashes, FR/ES/IT adopt DD/MM/YYYY with slashes (the previous
EN-shaped fallback was visibly wrong on French weekly-report PDFs),
PL adopts DD.MM.YYYY with dots.
The runtime `makeFormatters()` helper still routes through `Intl.*`;
the keys exist so PDF / CSV / email surfaces that render outside a
React context can still pick the right ordering hint.
A new smoke test at
`src/lib/i18n/__tests__/format-locale-order.test.ts` pins the
ordering per locale so a copy-paste regression can't silently flip
DD/MM to MM/DD on a non-English bundle.
This commit is contained in:
1 parent
2960f7352c
commit
19b5129306
7 files changed
+99
No files matched your search
@@ -33,6 +33,11 @@
|
||||
"showPassword": "Passwort anzeigen",
|
||||
"hidePassword": "Passwort verbergen"
|
||||
},
|
||||
"format": {
|
||||
"dateShort": "{day}.{month}.{year}",
|
||||
"timeShort": "{hour}:{minute}",
|
||||
"dateTime": "{day}.{month}.{year} {hour}:{minute}"
|
||||
},
|
||||
"doctorReport": {
|
||||
"title": "Gesundheitsbericht",
|
||||
"subtitle": "HealthLog — Persönlicher Gesundheitsbericht",
|
||||
|
||||
@@ -33,6 +33,11 @@
|
||||
"showPassword": "Show password",
|
||||
"hidePassword": "Hide password"
|
||||
},
|
||||
"format": {
|
||||
"dateShort": "{month}/{day}/{year}",
|
||||
"timeShort": "{hour}:{minute}",
|
||||
"dateTime": "{month}/{day}/{year} {hour}:{minute}"
|
||||
},
|
||||
"doctorReport": {
|
||||
"title": "Health Report",
|
||||
"subtitle": "HealthLog — Personal Health Report",
|
||||
|
||||
@@ -33,6 +33,11 @@
|
||||
"showPassword": "Mostrar contraseña",
|
||||
"hidePassword": "Ocultar contraseña"
|
||||
},
|
||||
"format": {
|
||||
"dateShort": "{day}/{month}/{year}",
|
||||
"timeShort": "{hour}:{minute}",
|
||||
"dateTime": "{day}/{month}/{year} {hour}:{minute}"
|
||||
},
|
||||
"doctorReport": {
|
||||
"title": "Informe de salud",
|
||||
"subtitle": "HealthLog — Informe personal de salud",
|
||||
|
||||
@@ -33,6 +33,11 @@
|
||||
"showPassword": "Afficher le mot de passe",
|
||||
"hidePassword": "Masquer le mot de passe"
|
||||
},
|
||||
"format": {
|
||||
"dateShort": "{day}/{month}/{year}",
|
||||
"timeShort": "{hour}:{minute}",
|
||||
"dateTime": "{day}/{month}/{year} {hour}:{minute}"
|
||||
},
|
||||
"doctorReport": {
|
||||
"title": "Rapport de santé",
|
||||
"subtitle": "HealthLog — Rapport personnel de santé",
|
||||
|
||||
@@ -33,6 +33,11 @@
|
||||
"showPassword": "Mostra password",
|
||||
"hidePassword": "Nascondi password"
|
||||
},
|
||||
"format": {
|
||||
"dateShort": "{day}/{month}/{year}",
|
||||
"timeShort": "{hour}:{minute}",
|
||||
"dateTime": "{day}/{month}/{year} {hour}:{minute}"
|
||||
},
|
||||
"doctorReport": {
|
||||
"title": "Rapporto sanitario",
|
||||
"subtitle": "HealthLog — Rapporto personale di salute",
|
||||
|
||||
@@ -33,6 +33,11 @@
|
||||
"showPassword": "Pokaż hasło",
|
||||
"hidePassword": "Ukryj hasło"
|
||||
},
|
||||
"format": {
|
||||
"dateShort": "{day}.{month}.{year}",
|
||||
"timeShort": "{hour}:{minute}",
|
||||
"dateTime": "{day}.{month}.{year} {hour}:{minute}"
|
||||
},
|
||||
"doctorReport": {
|
||||
"title": "Raport zdrowotny",
|
||||
"subtitle": "HealthLog — Osobisty raport zdrowotny",
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* v1.4.27 B6 — `format.dateShort` / `timeShort` / `dateTime` ordering.
|
||||
*
|
||||
* Each shipped locale carries a `format.*` namespace that documents
|
||||
* its native date-pattern ordering as i18n strings. The actual
|
||||
* formatting still routes through `Intl.*` via
|
||||
* `src/lib/format-locale.ts`; the format keys exist so downstream
|
||||
* surfaces (PDF export, email templates, CSV docstrings) can render
|
||||
* the date hint without a JS context.
|
||||
*
|
||||
* This smoke test pins the ordering per locale so a copy-paste
|
||||
* regression that flips DD/MM to MM/DD on the French bundle would
|
||||
* fail fast.
|
||||
*/
|
||||
import { readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const MESSAGES = join(__dirname, "../../../../messages");
|
||||
|
||||
function loadFormat(locale: string): {
|
||||
dateShort: string;
|
||||
timeShort: string;
|
||||
dateTime: string;
|
||||
} {
|
||||
const bundle = JSON.parse(
|
||||
readFileSync(join(MESSAGES, `${locale}.json`), "utf8"),
|
||||
);
|
||||
const fmt = bundle.format as
|
||||
| { dateShort?: string; timeShort?: string; dateTime?: string }
|
||||
| undefined;
|
||||
if (!fmt || !fmt.dateShort || !fmt.timeShort || !fmt.dateTime) {
|
||||
throw new Error(`Locale ${locale} is missing the format namespace`);
|
||||
}
|
||||
return fmt as { dateShort: string; timeShort: string; dateTime: string };
|
||||
}
|
||||
|
||||
describe("locale-native format ordering", () => {
|
||||
it("DE renders day-month-year with dot separators", () => {
|
||||
const fmt = loadFormat("de");
|
||||
expect(fmt.dateShort).toBe("{day}.{month}.{year}");
|
||||
expect(fmt.dateTime).toBe("{day}.{month}.{year} {hour}:{minute}");
|
||||
expect(fmt.timeShort).toBe("{hour}:{minute}");
|
||||
});
|
||||
|
||||
it("EN renders month-day-year with slash separators", () => {
|
||||
const fmt = loadFormat("en");
|
||||
expect(fmt.dateShort).toBe("{month}/{day}/{year}");
|
||||
expect(fmt.dateTime).toBe("{month}/{day}/{year} {hour}:{minute}");
|
||||
expect(fmt.timeShort).toBe("{hour}:{minute}");
|
||||
});
|
||||
|
||||
it.each([["fr"], ["es"], ["it"]])(
|
||||
"%s renders day-month-year with slash separators (native ordering)",
|
||||
(locale) => {
|
||||
const fmt = loadFormat(locale);
|
||||
expect(fmt.dateShort).toBe("{day}/{month}/{year}");
|
||||
expect(fmt.dateTime).toBe("{day}/{month}/{year} {hour}:{minute}");
|
||||
expect(fmt.timeShort).toBe("{hour}:{minute}");
|
||||
},
|
||||
);
|
||||
|
||||
it("PL renders day-month-year with dot separators (native ordering)", () => {
|
||||
const fmt = loadFormat("pl");
|
||||
expect(fmt.dateShort).toBe("{day}.{month}.{year}");
|
||||
expect(fmt.dateTime).toBe("{day}.{month}.{year} {hour}:{minute}");
|
||||
expect(fmt.timeShort).toBe("{hour}:{minute}");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user