fix(i18n): route all date/number display through locale-aware formatters - #1845
Conversation
Sweep of 14 raw toLocale*/Intl./toFixed call sites across 10 components that bypassed client/src/lib/formatters.ts, rendering en-US dates, decimal separators, and weekday labels for German-locale users regardless of their selected locale. Extends formatters.ts with formatDate's monthStyle option, formatPercent's locale+digits options, and four new locale-aware helpers (formatWeekdayShort, formatWeekdayMonthDay, formatFileSize, formatHours, formatDateTimeWithZone), each exposed through useFormatters(). Reroutes GanttTooltip/GanttHeader, MiniGanttCard, SignatureCapture's canvas-burned timestamp, BackupsPage file sizes, SourceUtilizationCard/BudgetSourcesPage percentages, and diary work durations through the shared formatters. Adds a locale param to calendarUtils.ts's formatDateForAria, matching its sibling getMonthName/ getDayName convention. Bonus fix: DocumentCard and DocumentDetailPanel previously called `new Date(document.created).toLocaleDateString(...)` directly on a YYYY-MM-DD-only date string, which parses as UTC midnight and can render the wrong calendar day for users behind UTC in the evening. Routing these through formatDate (which parses date components directly to avoid this shift) fixes locale and this latent off-by-one-day bug. Also fixes a bug found during QA: BudgetSourcesPage destructured `formatPercent: _formatPercent` (unused-var alias) while the new tooltip code referenced the bare `formatPercent` binding, causing a ReferenceError on segment hover. Adds a new direct unit test file for SignatureCapture (closing a pre-existing test-file-parity gap) and de-DE locale coverage across all touched components. Fixes #1813 Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com> Co-Authored-By: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com> Co-Authored-By: Claude translator (Sonnet 4.5) <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>
…dependencies GanttChart.test.tsx, DocumentBrowser.test.tsx, and DiaryEntryEditPage.test.tsx render DocumentCard/GanttHeader/DiaryEntryForm without a LocaleProvider (or, for GanttChart, with a formatters.js mock missing the new formatWeekdayMonthDay export). Those components now depend on locale context per the #1813 formatter consolidation, which their existing test setups didn't anticipate, failing Quality Gates' test job (shards 2/3/5). Adds a fixed-locale LocaleContext.js mock to DocumentBrowser.test.tsx and DiaryEntryEditPage.test.tsx (matching GanttChart.test.tsx's existing pattern), and adds the missing formatWeekdayMonthDay stub to GanttChart's formatters.js mock. Test-only change, no production code touched. Fixes #1813 Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>
InvoicePaperlessPickerModal.test.tsx renders the embedded DocumentBrowser -> DocumentCard (a grandparent consumer, two hops from the directly-changed component), which the prior consumer-blast-radius fix missed since it only checked direct importers. Adds the same LocaleContext.js mock block used in DocumentBrowser.test.tsx to unblock DocumentCard's useFormatters() call. Followed up with a full transitive-closure walk (grep for importers, recursively, to fixpoint) across every remaining consumer of the 11 components changed in #1813: LinkedDocumentsSection, LinkedDocumentCard, DocumentBrowser, GanttChart, CalendarView, DashboardPage, SignatureSection, DiaryEntryEditPage, DiaryEntryDetailPage, DiaryEntryCard, HouseholdItemDetailPage, WorkItemDetailPage, InvoiceDetailPage, SubsidyProgramsPage, TimelinePage, DiaryPage, BudgetSourcesPage, BackupsPage, and App.test.tsx — 841 tests total, all green, no further gaps. Test-only change, no production code touched. Fixes #1813 Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>
|
[security-engineer] Security review of #1845 (formatter consolidation, Scope confirmed: Reviewed areas:
No injection surface, no auth/authz-relevant code, no sensitive data exposure, no new dependencies. This is a pure client-side display-formatting refactor. Verdict: APPROVED |
|
[product-architect] APPROVED Reviewed the formatter consolidation against the areas I own (API design, conventions, no server/shared contract impact). Verdict: approve — findings are informational only. Verified
Informational (non-blocking, no action required)
Not re-flagging the known follow-ups (DateRangePicker locale-vs-resolvedLocale, SignatureCapture error |
|
[ux-designer] Design/i18n-output review of #1845. Reviewed scope: Locale-output spot-checks (script-verified with
All match the PR's own new test assertions in English non-regression at touched sites:
One informational, non-blocking finding: Aria-label grammar ( Visible-text change surface: Limited to exactly the two declared cases (document dates, Test coverage: Every touched call site has both an explicit de-DE assertion and an explicit "no regression in en-US" assertion (e.g. Verdict: APPROVED. No token, dark-mode, accessibility, or English-rendering regressions found. Locale outputs verified correct for all new formatters. The one thousands-separator edge case and the pre-existing aria grammar-order gap are both informational/non-blocking. Note: |
|
🎉 This PR is included in version 2.13.0-beta.14 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
|
🎉 This PR is included in version 2.13.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Summary
toLocale*/Intl.*/display-toFixedcall sites across 10 components (documents, Gantt tooltip/header, mini-Gantt, signature capture, backups, budget sources, diary) throughclient/src/lib/formatters.ts, fixing en-US-pinned dates, decimal separators, and weekday labels for German-locale users.formatters.tswithformatDate'smonthStyleoption,formatPercent'slocale+digitsoptions, and 4 new locale-aware helpers (formatWeekdayShort,formatWeekdayMonthDay,formatFileSize,formatHours,formatDateTimeWithZone), all exposed viauseFormatters(). Adds alocaleparam tocalendarUtils.ts'sformatDateForAria, matching its siblinggetMonthName/getDayNameconvention.DocumentCard/DocumentDetailPanelpreviously callednew Date(document.created).toLocaleDateString(...)directly on aYYYY-MM-DD-only date string, which parses as UTC midnight and can render the wrong calendar day for users behind UTC in the evening. Routing throughformatDate(which parses date components directly to avoid this shift) fixes locale and this latent off-by-one-day bug.BudgetSourcesPagedestructuredformatPercent: _formatPercent(unused-var alias) while new tooltip code referenced the bareformatPercentbinding, causing aReferenceErroron segment hover.SignatureCapture(closing a pre-existing test-file-parity gap, 40 tests) plus de-DE locale coverage across all touched components.Fixes #1813
Test plan
npx tsc -p client/tsconfig.json --noEmitclean (full-program check, catches theformatPercentReferenceError class ts-jest's isolated compilation missed)git stashdiff againstorigin/beta)Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) noreply@anthropic.com
Co-Authored-By: Claude frontend-developer (Haiku 4.5) noreply@anthropic.com
Co-Authored-By: Claude translator (Sonnet 4.5) noreply@anthropic.com
Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) noreply@anthropic.com