refactor(i18n,format): translate ETA strings; consolidate time/duration formatters

- formatEta was hardcoded English (violated the trilingual rule) -> time.eta.* keys
  in EN/HU/DE; Scheduler's countdown 'now' likewise.
- relativeFromMs added to format.ts; NotificationsPanel drops its duplicate
  relativeTime/relativeFromMs and the now-orphaned notifications.time.* keys.
- Channels' fmtTotalDuration moved to format.ts as formatTotalHours.
This commit is contained in:
npeter83 2026-06-29 00:22:21 +02:00
parent 5768c1a5cb
commit 9eaec27c7c
10 changed files with 61 additions and 64 deletions

View file

@ -2,8 +2,14 @@ import i18n from "../i18n";
export function relativeTime(iso: string | null): string {
if (!iso) return "";
const then = new Date(iso).getTime();
const secs = Math.max(0, (Date.now() - then) / 1000);
return relativeFromMs(new Date(iso).getTime());
}
/** Relative-time label from an epoch-millis timestamp (the ms-based half of relativeTime, for
* client-side notifications whose timestamps are already numbers). One implementation, one
* set of `time.*` strings shared instead of re-implemented per component. */
export function relativeFromMs(ms: number): string {
const secs = Math.max(0, (Date.now() - ms) / 1000);
const units: [number, string][] = [
[60, "time.secondsAgo"],
[3600, "time.minutesAgo"],
@ -51,17 +57,21 @@ export function quotaActionLabel(action: string): string {
return i18n.t(`quotaActions.${action}`, { defaultValue: action });
}
/** Coarse, human ETA for a remaining duration in seconds ("~3 hours", "~2 days"). */
/** Coarse, human ETA for a remaining duration in seconds ("~3 hours", "~2 days"). Translated
* via the `time.eta.*` keys (was hardcoded English, violating the trilingual rule). */
export function formatEta(seconds: number): string {
if (seconds <= 0) return "done";
if (seconds <= 0) return i18n.t("time.eta.done");
const hours = seconds / 3600;
if (hours < 1) return "< 1 hour";
if (hours < 24) {
const h = Math.round(hours);
return `~${h} hour${h === 1 ? "" : "s"}`;
}
const d = Math.round(hours / 24);
return `~${d} day${d === 1 ? "" : "s"}`;
if (hours < 1) return i18n.t("time.eta.lessThanHour");
if (hours < 24) return i18n.t("time.eta.hours", { count: Math.round(hours) });
return i18n.t("time.eta.days", { count: Math.round(hours / 24) });
}
/** Compact whole-channel total length ("12 h", "45 m"), hours-scale so H:MM:SS isn't huge. */
export function formatTotalHours(sec: number): string {
if (!sec) return "—";
const h = Math.round(sec / 3600);
return h >= 1 ? `${h.toLocaleString()} h` : `${Math.max(1, Math.round(sec / 60))} m`;
}
export function formatViews(n: number | null): string {