From e794e0ca57b199651d464854e1a7dd62d0a533e5 Mon Sep 17 00:00:00 2001 From: Till JS Date: Fri, 24 Apr 2026 17:20:11 +0200 Subject: [PATCH] feat(i18n): locale-aware formatters, migrate hardcoded de-DE call-sites MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bisher pinnten 185+ call-sites die UI-Zahlen/Datumsausgabe hart auf Deutsch ("de" / "de-DE" an toLocaleDate{,Time}String, Intl.*Format, date-fns locale), unabhängig von der aktiven Sprache. EN-User sahen dadurch deutsche Datums-/Zahlenformate mitten im englischen UI. - $lib/i18n/format.ts (neu): formatDate / formatTime / formatDateTime / formatNumber / formatCurrency / getDateFnsLocale. Alle lesen die aktive Locale aus svelte-i18n's locale-store und mappen de→de-DE etc. für Intl. - Codemod: 119 Direktaufrufe in 79 Files migriert (.toLocaleDateString / .toLocaleTimeString / new Intl.{Number,DateTime}Format). Erkennt new Date() / Number()-Receiver zum Disambiguieren von .toLocaleString('de-DE'). - date-fns: 19 Files auf getDateFnsLocale() umgestellt; hardcoded `import { de } from 'date-fns/locale'` entfernt. - Skipped (Collision): 14 Files hatten lokale format*-Wrapper; diese bleiben vorerst als gesonderte Folge-Refactorings stehen. Ca. 58 deep-gekapselte Aufrufe im .toLocaleString/.toLocaleDateString-Idiom sind über diese Wrapper noch zu migrieren. - svelte-check: 0 Errors / 0 Warnings. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../components/admin/ProjectDataCard.svelte | 3 +- .../widgets/ActivityFeedWidget.svelte | 5 +- .../widgets/CalendarEventsWidget.svelte | 5 +- .../dashboard/widgets/TasksTodayWidget.svelte | 5 +- .../widgets/TransactionsWidget.svelte | 3 +- .../layout/use-sync-status-items.svelte.ts | 3 +- apps/mana/apps/web/src/lib/i18n/format.ts | 90 +++++++++++++++++++ .../src/lib/modules/ai-health/ListView.svelte | 5 +- .../lib/modules/ai-missions/ListView.svelte | 3 +- .../calendar/components/AgendaView.svelte | 4 +- .../calendar/components/CalendarHeader.svelte | 9 +- .../calendar/components/DateStrip.svelte | 12 +-- .../components/EventDetailModal.svelte | 11 +-- .../calendar/components/MiniCalendar.svelte | 6 +- .../calendar/components/MonthView.svelte | 3 +- .../components/QuickEventPopover.svelte | 2 +- .../components/SlotSuggestions.svelte | 5 +- .../calendar/components/WeekView.svelte | 7 +- .../modules/calendar/quick-input-adapter.ts | 5 +- .../modules/calendar/views/DetailView.svelte | 5 +- .../lib/modules/cards/views/DetailView.svelte | 7 +- .../lib/modules/chat/views/DetailView.svelte | 3 +- .../citycorners/views/DetailView.svelte | 5 +- .../modules/contacts/views/DetailView.svelte | 5 +- .../core/widgets/TasksTodayWidget.svelte | 3 +- .../core/widgets/UpcomingEventsWidget.svelte | 5 +- .../web/src/lib/modules/dreams/queries.ts | 5 +- .../components/DiscoveredEventCard.svelte | 7 +- .../events/components/EventCard.svelte | 7 +- .../events/components/PublicRsvpList.svelte | 3 +- .../lib/modules/events/quick-input-adapter.ts | 5 +- .../apps/web/src/lib/modules/events/tools.ts | 3 +- .../modules/events/views/DetailView.svelte | 3 +- .../habits/components/DayTimeline.svelte | 3 +- .../habits/components/HabitDetail.svelte | 5 +- .../components/fields/FieldRenderer.svelte | 5 +- .../modules/inventory/views/DetailView.svelte | 5 +- .../web/src/lib/modules/journal/queries.ts | 5 +- .../meditate/components/SessionCard.svelte | 3 +- .../lib/modules/memoro/llm-watcher.svelte.ts | 5 +- .../modules/memoro/views/DetailView.svelte | 5 +- .../lib/modules/music/views/DetailView.svelte | 7 +- .../apps/web/src/lib/modules/news/queries.ts | 3 +- .../apps/web/src/lib/modules/notes/queries.ts | 3 +- .../picture/components/ImageLightbox.svelte | 3 +- .../lib/modules/presi/views/DetailView.svelte | 5 +- .../modules/questions/views/DetailView.svelte | 5 +- .../components/AchievementCard.svelte | 3 +- .../components/EditSkillModal.svelte | 3 +- .../modules/skilltree/views/DetailView.svelte | 5 +- .../src/lib/modules/spaces/ListView.svelte | 3 +- .../src/lib/modules/spiral/ListView.svelte | 3 +- .../modules/storage/views/DetailView.svelte | 5 +- .../src/lib/modules/stretch/ListView.svelte | 3 +- .../modules/times/components/EntryItem.svelte | 7 +- .../modules/times/components/EntryList.svelte | 3 +- .../web/src/lib/modules/times/utils/export.ts | 9 +- .../lib/modules/times/views/DetailView.svelte | 5 +- .../web/src/lib/modules/todo/ListView.svelte | 7 +- .../todo/components/QuickAddTask.svelte | 3 +- .../modules/todo/components/TaskItem.svelte | 5 +- .../components/kanban/KanbanTaskCard.svelte | 4 +- .../lib/modules/todo/quick-input-adapter.ts | 3 +- .../lib/modules/todo/views/DetailView.svelte | 5 +- .../lib/modules/uload/views/DetailView.svelte | 5 +- .../src/lib/modules/website/ListView.svelte | 3 +- .../web/src/lib/modules/website/embeds.ts | 5 +- .../components/CurrentConditions.svelte | 3 +- .../wetter/components/DailyForecast.svelte | 3 +- .../wetter/components/HourlyForecast.svelte | 3 +- .../wetter/components/NowcastBar.svelte | 7 +- .../wetter/components/SourceComparison.svelte | 5 +- .../apps/web/src/lib/modules/wetter/tools.ts | 5 +- .../modules/wishes/views/DetailView.svelte | 5 +- .../writing/components/DraftCard.svelte | 3 +- .../modules/writing/views/DetailView.svelte | 6 +- .../(app)/calendar/event/[id]/+page.svelte | 8 +- .../routes/(app)/cards/progress/+page.svelte | 3 +- .../src/routes/(app)/chat/[id]/+page.svelte | 3 +- .../routes/(app)/contacts/[id]/+page.svelte | 7 +- .../web/src/routes/(app)/context/+page.svelte | 3 +- .../(app)/context/documents/+page.svelte | 3 +- .../(app)/context/documents/[id]/+page.svelte | 5 +- .../routes/(app)/context/spaces/+page.svelte | 3 +- .../(app)/context/spaces/[id]/+page.svelte | 3 +- .../web/src/routes/(app)/finance/+page.svelte | 3 +- .../(app)/inventory/items/[id]/+page.svelte | 3 +- .../src/routes/(app)/memoro/[id]/+page.svelte | 3 +- .../routes/(app)/picture/board/+page.svelte | 3 +- .../src/routes/(app)/skilltree/+page.svelte | 3 +- .../src/routes/(app)/timeline/+page.svelte | 4 +- .../(app)/timeline/analytics/+page.svelte | 4 +- .../src/routes/(app)/times/clock/+page.svelte | 5 +- .../routes/(app)/times/reports/+page.svelte | 3 +- .../web/src/routes/(app)/uload/+page.svelte | 4 +- .../(app)/uload/analytics/[id]/+page.svelte | 5 +- 96 files changed, 337 insertions(+), 184 deletions(-) create mode 100644 apps/mana/apps/web/src/lib/i18n/format.ts diff --git a/apps/mana/apps/web/src/lib/components/admin/ProjectDataCard.svelte b/apps/mana/apps/web/src/lib/components/admin/ProjectDataCard.svelte index c2c455314..498d03f6e 100644 --- a/apps/mana/apps/web/src/lib/components/admin/ProjectDataCard.svelte +++ b/apps/mana/apps/web/src/lib/components/admin/ProjectDataCard.svelte @@ -1,4 +1,5 @@ diff --git a/apps/mana/apps/web/src/lib/components/dashboard/widgets/ActivityFeedWidget.svelte b/apps/mana/apps/web/src/lib/components/dashboard/widgets/ActivityFeedWidget.svelte index d563fee7f..a63840873 100644 --- a/apps/mana/apps/web/src/lib/components/dashboard/widgets/ActivityFeedWidget.svelte +++ b/apps/mana/apps/web/src/lib/components/dashboard/widgets/ActivityFeedWidget.svelte @@ -1,4 +1,5 @@ diff --git a/apps/mana/apps/web/src/lib/modules/calendar/components/WeekView.svelte b/apps/mana/apps/web/src/lib/modules/calendar/components/WeekView.svelte index 4b7738934..be75a2c96 100644 --- a/apps/mana/apps/web/src/lib/modules/calendar/components/WeekView.svelte +++ b/apps/mana/apps/web/src/lib/modules/calendar/components/WeekView.svelte @@ -1,4 +1,5 @@ diff --git a/apps/mana/apps/web/src/lib/modules/dreams/queries.ts b/apps/mana/apps/web/src/lib/modules/dreams/queries.ts index 73bb5cbd9..9843e2da6 100644 --- a/apps/mana/apps/web/src/lib/modules/dreams/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/dreams/queries.ts @@ -1,3 +1,4 @@ +import { formatDate } from '$lib/i18n/format'; /** * Reactive Queries & Pure Helpers for Dreams module. * @@ -113,7 +114,7 @@ export function groupByMonth(dreams: Dream[]): Array<{ label: string; dreams: Dr const groups = new Map(); for (const d of dreams) { const date = new Date(d.dreamDate); - const label = date.toLocaleDateString('de-DE', { month: 'long', year: 'numeric' }); + const label = formatDate(date, { month: 'long', year: 'numeric' }); if (!groups.has(label)) groups.set(label, []); groups.get(label)!.push(d); } @@ -128,7 +129,7 @@ export function formatDreamDate(iso: string): string { if (diffDays === 0) return 'Heute Nacht'; if (diffDays === 1) return 'Gestern Nacht'; if (diffDays < 7) return `vor ${diffDays} Tagen`; - return date.toLocaleDateString('de-DE', { day: 'numeric', month: 'short', year: 'numeric' }); + return formatDate(date, { day: 'numeric', month: 'short', year: 'numeric' }); } /** Map of symbol name → most recent dreamDate that references it. */ diff --git a/apps/mana/apps/web/src/lib/modules/events/components/DiscoveredEventCard.svelte b/apps/mana/apps/web/src/lib/modules/events/components/DiscoveredEventCard.svelte index 844e6c7c6..b7a9216db 100644 --- a/apps/mana/apps/web/src/lib/modules/events/components/DiscoveredEventCard.svelte +++ b/apps/mana/apps/web/src/lib/modules/events/components/DiscoveredEventCard.svelte @@ -1,4 +1,5 @@ diff --git a/apps/mana/apps/web/src/lib/modules/events/components/PublicRsvpList.svelte b/apps/mana/apps/web/src/lib/modules/events/components/PublicRsvpList.svelte index 777d354f6..09008f320 100644 --- a/apps/mana/apps/web/src/lib/modules/events/components/PublicRsvpList.svelte +++ b/apps/mana/apps/web/src/lib/modules/events/components/PublicRsvpList.svelte @@ -1,4 +1,5 @@ diff --git a/apps/mana/apps/web/src/lib/modules/spiral/ListView.svelte b/apps/mana/apps/web/src/lib/modules/spiral/ListView.svelte index e42952f29..c22b36d7c 100644 --- a/apps/mana/apps/web/src/lib/modules/spiral/ListView.svelte +++ b/apps/mana/apps/web/src/lib/modules/spiral/ListView.svelte @@ -4,6 +4,7 @@ a spiral pattern. Extracted from the former /spiral standalone route. --> diff --git a/apps/mana/apps/web/src/lib/modules/times/components/EntryList.svelte b/apps/mana/apps/web/src/lib/modules/times/components/EntryList.svelte index 254d485f6..72beb3a0e 100644 --- a/apps/mana/apps/web/src/lib/modules/times/components/EntryList.svelte +++ b/apps/mana/apps/web/src/lib/modules/times/components/EntryList.svelte @@ -1,4 +1,5 @@ diff --git a/apps/mana/apps/web/src/lib/modules/website/embeds.ts b/apps/mana/apps/web/src/lib/modules/website/embeds.ts index 4c4753b5c..e209e86a9 100644 --- a/apps/mana/apps/web/src/lib/modules/website/embeds.ts +++ b/apps/mana/apps/web/src/lib/modules/website/embeds.ts @@ -1,3 +1,4 @@ +import { formatDateTime } from '$lib/i18n/format'; /** * Module-embed resolvers — client-side functions that walk Dexie to * pre-fetch data for `moduleEmbed` blocks at publish time. @@ -260,11 +261,11 @@ function formatEventSubtitle( location: string | null | undefined ): string { const start = new Date(startIso); - const dateParts = new Intl.DateTimeFormat('de-DE', { + const dateParts = formatDateTime(start, { day: '2-digit', month: 'short', year: 'numeric', - }).format(start); + }); let timePart = ''; if (!allDay) { diff --git a/apps/mana/apps/web/src/lib/modules/wetter/components/CurrentConditions.svelte b/apps/mana/apps/web/src/lib/modules/wetter/components/CurrentConditions.svelte index 732941ebe..20d3517bf 100644 --- a/apps/mana/apps/web/src/lib/modules/wetter/components/CurrentConditions.svelte +++ b/apps/mana/apps/web/src/lib/modules/wetter/components/CurrentConditions.svelte @@ -3,6 +3,7 @@ conditions, wind, humidity, pressure, UV index, and last-updated time. --> diff --git a/apps/mana/apps/web/src/lib/modules/wetter/components/DailyForecast.svelte b/apps/mana/apps/web/src/lib/modules/wetter/components/DailyForecast.svelte index b2c4a513e..0f74a9e2a 100644 --- a/apps/mana/apps/web/src/lib/modules/wetter/components/DailyForecast.svelte +++ b/apps/mana/apps/web/src/lib/modules/wetter/components/DailyForecast.svelte @@ -2,6 +2,7 @@ 7-day daily forecast — shows min/max temp, weather icon, precipitation. -->