Files
ServerKit/frontend/src/utils/time.js
T
Juan DenisandClaude Opus 5 7050b24137 feat(i18n): one locale-aware format door; delete the timeAgo shim
Plan 79 §C + ratchet 5.

utils/intl.js owns dates, times, relative time, durations, numbers, percents
and lists. Three things the scattered calls could not do:

- A module-level locale published by LocaleContext, not read from a hook, so
  the API error mapper, log formatter and CSV export format in the panel's
  language too. useFormat() is a subscription over the same module, not a
  second door.
- Formatter instances cached per (locale, kind, options). The
  toLocaleDateString() calls this replaces built a fresh formatter per cell
  per render, and Intl construction is the expensive half.
- Relative time via Intl.RelativeTimeFormat rather than translation keys --
  the browser already carries every language's plural wording, and keying
  "{{count}} days ago" would mean maintaining that table by hand.

This fixes a locale bug that predates any translation work: 124 call sites
use toLocale*() with no argument, which follows the BROWSER's locale, so a
panel set to Spanish already rendered English chrome around Spanish dates.
formatNumber.js was worse -- it built Intl.NumberFormat('en') at module load,
hardcoding English AND making it impossible to follow a switch.

The three human-facing utils now delegate instead of reimplementing, so their
existing callers become locale-aware without being touched. formatBytes gains
the locale decimal separator ("1,5 KB" in es) by replacing a toFixed + regex
trim with maximumFractionDigits, which trims identically in en -- parity
proved case by case against the pre-migration examples.

utils/timeAgo.js is deleted rather than left as a shim; its 6 importers move
to utils/time. A door nobody is forced through is a door nobody uses.

One bug found by the tests: formatNumber(null) returned "0", because
Number(null) is 0 -- a missing metric would have rendered as a real zero.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 23:22:28 -04:00

30 lines
1.1 KiB
JavaScript

// Relative-time and duration helpers.
//
// Plan 79 §C: the implementations moved to `utils/intl.js`, which is the one
// locale-aware formatting door. These names stay because ten files import
// them and the two output shapes are deliberate — but they are now thin
// delegations, not a second implementation. Notably the date tail of both
// styles used to be `toLocaleDateString()` with no argument, which follows the
// BROWSER's locale rather than the panel's.
//
// - timeAgo(iso) compact: "just now", "4m", "3h", "2d", else a date
// - formatRelativeTime verbose, now fully localized by Intl:
// "4 minutes ago", "hace 4 minutos"
// - formatDuration(secs) "45s", "3m 20s"
import { formatRelative, formatRelativeShort, formatDuration as formatDurationIntl } from './intl.js';
export function timeAgo(iso) {
return formatRelativeShort(iso);
}
export function formatRelativeTime(iso) {
return formatRelative(iso);
}
export function formatDuration(seconds) {
return formatDurationIntl(seconds);
}
export default timeAgo;