fix(dates): make DAY precision locale-aware in formatDocumentDate

DAY precision routed through formatDate() which hard-coded de-DE, so an
en/es reader saw the German month name ("24. Dezember 1943"). Route DAY
through Intl.DateTimeFormat(locale, …) like the other branches, keeping
the T12:00:00 UTC-safety convention. Add en/es DAY+MONTH parity cases to
docs/date-label-fixtures.json (TS-only; the Java title formatter stays
German by design) and assert them in the spec.

Refs #666

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-27 12:19:09 +02:00
parent b1b8fa4bed
commit 8ed5b1e9e3
3 changed files with 110 additions and 7 deletions

View File

@@ -17,9 +17,11 @@ type FixtureCase = {
expected: string;
};
type LocaleFixtureCase = FixtureCase & { locale: string };
const fixtures = JSON.parse(
readFileSync(resolve(process.cwd(), '../docs/date-label-fixtures.json'), 'utf-8')
) as { cases: FixtureCase[] };
) as { cases: FixtureCase[]; localeCases: LocaleFixtureCase[] };
describe('formatDocumentDate shared fixture table (de)', () => {
for (const c of fixtures.cases) {
@@ -37,6 +39,24 @@ describe('formatDocumentDate shared fixture table (de)', () => {
}
});
// TS-only locale parity (the Java title formatter is German-only by design, so
// localeCases are asserted here and never fed to DocumentTitleFormatterTest).
describe('formatDocumentDate shared fixture table (en/es locale parity)', () => {
for (const c of fixtures.localeCases) {
it(`${c.name} [${c.locale}]`, () => {
expect(
formatDocumentDate(
c.anchor,
c.precision as Parameters<typeof formatDocumentDate>[1],
c.end,
c.raw,
c.locale
)
).toBe(c.expected);
});
}
});
// ─── Anti-fabrication: suppressed components never leak ──────────────────────
describe('formatDocumentDate suppressed precision components', () => {
@@ -80,6 +100,40 @@ describe('formatDocumentDate localization', () => {
`${m.date_season_summer(undefined, { locale: 'en' })} 1916`
);
});
// DAY precision must honour the active locale (regression: it was hard-wired
// to de-DE, so an English/Spanish reader saw "24. Dezember 1943").
it('localizes the DAY month name in English', () => {
expect(formatDocumentDate('1943-12-24', 'DAY', null, null, 'en')).toBe(
new Intl.DateTimeFormat('en', { day: 'numeric', month: 'long', year: 'numeric' }).format(
new Date('1943-12-24T12:00:00')
)
);
});
it('localizes the DAY month name in Spanish', () => {
expect(formatDocumentDate('1943-12-24', 'DAY', null, null, 'es')).toBe(
new Intl.DateTimeFormat('es', { day: 'numeric', month: 'long', year: 'numeric' }).format(
new Date('1943-12-24T12:00:00')
)
);
});
it('localizes the MONTH month name in English', () => {
expect(formatDocumentDate('1916-06-01', 'MONTH', null, 'Juni 1916', 'en')).toBe(
new Intl.DateTimeFormat('en', { month: 'long', year: 'numeric' }).format(
new Date('1916-06-01T12:00:00')
)
);
});
it('localizes the MONTH month name in Spanish', () => {
expect(formatDocumentDate('1916-06-01', 'MONTH', null, 'Juni 1916', 'es')).toBe(
new Intl.DateTimeFormat('es', { month: 'long', year: 'numeric' }).format(
new Date('1916-06-01T12:00:00')
)
);
});
});
// ─── Security: untrusted raw must never influence the structured label ───────