From 6e5fe3e1996b2d205c700722f70943bed5751f40 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 05:47:45 +0000 Subject: [PATCH] fix(pages): englischer Datumsformatierer nur einmal angelegt, Kommentar im Melden-Dialog berichtigt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - format.ts: `Intl.DateTimeFormat('en-US', …)` wurde bei jedem Aufruf von formatDate neu angelegt (Meine Karten, Galerie, Detailseite, Logo-Infozeile rufen es je Karte). Er hängt nicht von der Sprache ab und steht jetzt einmal auf Modulebene (EN_DATE); die Ausgabe ist gleich, die vorhandenen Tests bleiben unverändert. - report-dialog.ts: Der Kommentar zu reportDialog sprach noch vom „Servertext“; angezeigt wird `apiErrorText`, der Text zum Schlüssel des Fehlers in der Sprache der Seite. - Test: fünf Aufrufe auf Englisch legen höchstens einen Formatierer an (frische Modulkopie, Konstruktor gezählt). Co-Authored-By: Claude Opus 5.5 --- web/src/community/report-dialog.ts | 3 ++- web/src/pages/format.test.ts | 18 +++++++++++++++++- web/src/pages/format.ts | 5 ++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/web/src/community/report-dialog.ts b/web/src/community/report-dialog.ts index 6b2e00f..4ac82a2 100644 --- a/web/src/community/report-dialog.ts +++ b/web/src/community/report-dialog.ts @@ -44,7 +44,8 @@ export interface ReportDialogOptions { /** * Öffnet den Dialog. Löst auf, sobald er geschlossen ist: mit dem Ergebnis der letzten Meldung, sonst mit `cancelled`. - * Fehler des Dienstes erscheinen mit ihrem Servertext im Dialog; der Dialog bleibt dann offen für einen neuen Versuch. + * Fehler des Dienstes erscheinen im Dialog in der Sprache der Seite (`apiErrorText`: der Text zum Schlüssel des Fehlers, nicht der + * Servertext); der Dialog bleibt dann offen für einen neuen Versuch. */ export function reportDialog(opts: ReportDialogOptions): Promise { return new Promise((resolve, reject) => { diff --git a/web/src/pages/format.test.ts b/web/src/pages/format.test.ts index 94ad9c5..49cd9a0 100644 --- a/web/src/pages/format.test.ts +++ b/web/src/pages/format.test.ts @@ -1,7 +1,11 @@ -import { describe, expect, it } from 'vitest'; +import { afterEach, describe, expect, it, vi } from 'vitest'; import { setLang } from '../i18n/i18n'; import { formatDate, reportsLabel } from './format'; +afterEach(() => { + vi.restoreAllMocks(); +}); + describe('formatDate', () => { it('schreibt dd.mm.yyyy', () => { expect(formatDate(new Date(2026, 9, 4, 12).toISOString())).toBe('04.10.2026'); @@ -19,6 +23,18 @@ describe('formatDate', () => { expect(formatDate('kaputt')).toBe(''); }); + it('der englische Formatierer wird einmal angelegt und danach wiederverwendet, nicht bei jedem Aufruf neu', async () => { + // Eine frische Kopie des Moduls (und von i18n.ts), damit das Anlegen mitgezählt wird, wann immer es geschieht. + vi.resetModules(); + const created = vi.spyOn(Intl, 'DateTimeFormat'); + const fresh = await import('./format'); + (await import('../i18n/i18n')).setLang('en', { persist: false }); + const iso = new Date(2026, 9, 4, 12).toISOString(); + const results = Array.from({ length: 5 }, () => fresh.formatDate(iso)); + expect(results).toEqual(Array(5).fill('Oct 4, 2026')); + expect(created.mock.calls.length).toBeLessThanOrEqual(1); + }); + it('folgt der Sprache bei jedem Aufruf', () => { const iso = new Date(2026, 9, 4, 12).toISOString(); expect(formatDate(iso)).toBe('04.10.2026'); diff --git a/web/src/pages/format.ts b/web/src/pages/format.ts index 9572a7d..38d95b0 100644 --- a/web/src/pages/format.ts +++ b/web/src/pages/format.ts @@ -1,11 +1,14 @@ // Gemeinsame Formatierung der Seiten (Meine Karten, Community-Detailseite, Galerie-Raster, Logo-Infozeile). import { currentLang, t } from '../i18n/i18n'; +// Der englische Formatierer hängt nicht von der Sprache der Seite ab (er wird nur bei Englisch benutzt): einmal angelegt, nicht je Aufruf. +const EN_DATE = new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); + /** Deutsch `dd.mm.yyyy`, Englisch `Oct 4, 2026`, jeweils in der Zeitzone des Betrachters; leer, wenn das Datum unlesbar ist. */ export function formatDate(iso: string): string { const date = new Date(iso); if (Number.isNaN(date.getTime())) return ''; - if (currentLang() === 'en') return new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric', year: 'numeric' }).format(date); + if (currentLang() === 'en') return EN_DATE.format(date); const pad = (n: number) => String(n).padStart(2, '0'); return `${pad(date.getDate())}.${pad(date.getMonth() + 1)}.${date.getFullYear()}`; }