i18n(relative-time): branch on count === 1 to render singular forms

formatRelativeTime always read the *Ago key with count=1, so a German user reading the Insights hero saw 'vor 1 Minuten' / 'vor 1 Stunden'. Splits each bucket into One / Other variants across all locales and branches on count === 1 in the helper, matching the dashboard.staleHintWeeksOne / Other pattern already in the bundle.
This commit is contained in:
Marc-André Bombeck committed 2026-05-21 21:45:45 +02:00
1 parent cb2ee59259
commit b8ca1c74f2
7 files changed
+63 -21

No files matched your search

+6 -3
View File
@@ -1147,9 +1147,12 @@
"providerRateLimitBody": "Anbieter ist temporär überlastet; wieder verfügbar in ~5 min."
},
"relativeJustNow": "gerade eben",
"relativeMinutesAgo": "vor {count} Minuten",
"relativeHoursAgo": "vor {count} Stunden",
"relativeDaysAgo": "vor {count} Tagen",
"relativeMinutesAgoOne": "vor {count} Minute",
"relativeMinutesAgoOther": "vor {count} Minuten",
"relativeHoursAgoOne": "vor {count} Stunde",
"relativeHoursAgoOther": "vor {count} Stunden",
"relativeDaysAgoOne": "vor {count} Tag",
"relativeDaysAgoOther": "vor {count} Tagen",
"navOverview": "Übersicht",
"navBloodPressure": "Blutdruck",
"navWeight": "Gewicht",
+6 -3
View File
@@ -1147,9 +1147,12 @@
"providerRateLimitBody": "Provider temporarily rate-limited; retry in ~5 min."
},
"relativeJustNow": "just now",
"relativeMinutesAgo": "{count} minutes ago",
"relativeHoursAgo": "{count} hours ago",
"relativeDaysAgo": "{count} days ago",
"relativeMinutesAgoOne": "{count} minute ago",
"relativeMinutesAgoOther": "{count} minutes ago",
"relativeHoursAgoOne": "{count} hour ago",
"relativeHoursAgoOther": "{count} hours ago",
"relativeDaysAgoOne": "{count} day ago",
"relativeDaysAgoOther": "{count} days ago",
"navOverview": "Overview",
"navBloodPressure": "Blood Pressure",
"navWeight": "Weight",
+6 -3
View File
@@ -1147,9 +1147,12 @@
"settingsContextHint": "Bloques opcionales en los que el coach puede apoyarse. Si desactivas alguno, se omitirá en cada respuesta."
},
"relativeJustNow": "ahora mismo",
"relativeMinutesAgo": "hace {count} minutos",
"relativeHoursAgo": "hace {count} horas",
"relativeDaysAgo": "hace {count} días",
"relativeMinutesAgoOne": "hace {count} minuto",
"relativeMinutesAgoOther": "hace {count} minutos",
"relativeHoursAgoOne": "hace {count} hora",
"relativeHoursAgoOther": "hace {count} horas",
"relativeDaysAgoOne": "hace {count} día",
"relativeDaysAgoOther": "hace {count} días",
"navOverview": "Resumen",
"navBloodPressure": "Presión arterial",
"navWeight": "Peso",
+6 -3
View File
@@ -1147,9 +1147,12 @@
"settingsContextHint": "Blocs optionnels sur lesquels le coach peut s’appuyer. Si tu en désactives un, il sera omis dans chaque réponse."
},
"relativeJustNow": "à l’instant",
"relativeMinutesAgo": "il y a {count} minutes",
"relativeHoursAgo": "il y a {count} heures",
"relativeDaysAgo": "il y a {count} jours",
"relativeMinutesAgoOne": "il y a {count} minute",
"relativeMinutesAgoOther": "il y a {count} minutes",
"relativeHoursAgoOne": "il y a {count} heure",
"relativeHoursAgoOther": "il y a {count} heures",
"relativeDaysAgoOne": "il y a {count} jour",
"relativeDaysAgoOther": "il y a {count} jours",
"navOverview": "Vue d'ensemble",
"navBloodPressure": "Tension artérielle",
"navWeight": "Poids",
+6 -3
View File
@@ -1147,9 +1147,12 @@
"settingsContextHint": "Blocchi opzionali su cui il coach può basarsi. Disattivandone uno, viene omesso da ogni risposta."
},
"relativeJustNow": "proprio ora",
"relativeMinutesAgo": "{count} minuti fa",
"relativeHoursAgo": "{count} ore fa",
"relativeDaysAgo": "{count} giorni fa",
"relativeMinutesAgoOne": "{count} minuto fa",
"relativeMinutesAgoOther": "{count} minuti fa",
"relativeHoursAgoOne": "{count} ora fa",
"relativeHoursAgoOther": "{count} ore fa",
"relativeDaysAgoOne": "{count} giorno fa",
"relativeDaysAgoOther": "{count} giorni fa",
"navOverview": "Panoramica",
"navBloodPressure": "Pressione arteriosa",
"navWeight": "Peso",
+6 -3
View File
@@ -1147,9 +1147,12 @@
"settingsContextHint": "Opcjonalne bloki, na których coach może się opierać. Wyłączenie pominie blok w każdej odpowiedzi."
},
"relativeJustNow": "przed chwilą",
"relativeMinutesAgo": "{count} minut temu",
"relativeHoursAgo": "{count} godzin temu",
"relativeDaysAgo": "{count} dni temu",
"relativeMinutesAgoOne": "{count} minutę temu",
"relativeMinutesAgoOther": "{count} minut temu",
"relativeHoursAgoOne": "{count} godzinę temu",
"relativeHoursAgoOther": "{count} godzin temu",
"relativeDaysAgoOne": "{count} dzień temu",
"relativeDaysAgoOther": "{count} dni temu",
"navOverview": "Przegląd",
"navBloodPressure": "Ciśnienie krwi",
"navWeight": "Waga",
+27 -3
View File
@@ -7,6 +7,11 @@
* lift the body here so a future i18n key change lands once.
*
* Buckets: <1m → just-now, <1h → minutes, <24h → hours, else days.
*
* v1.4.43 H6 — pluralisation. Each bucket now branches on `count === 1`
* to read the *One key (singular) or *Other key (plural), matching the
* dashboard.staleHintWeeksOne / Other pattern already in the bundle.
* Eliminates the "vor 1 Minuten" grammar bug a German user spotted.
*/
export function formatRelativeTime(
iso: string,
@@ -17,9 +22,28 @@ export function formatRelativeTime(
const diffMs = Date.now() - target;
if (diffMs < 60_000) return t("insights.relativeJustNow");
const minutes = Math.floor(diffMs / 60_000);
if (minutes < 60) return t("insights.relativeMinutesAgo", { count: minutes });
if (minutes < 60) {
return t(
minutes === 1
? "insights.relativeMinutesAgoOne"
: "insights.relativeMinutesAgoOther",
{ count: minutes },
);
}
const hours = Math.floor(minutes / 60);
if (hours < 24) return t("insights.relativeHoursAgo", { count: hours });
if (hours < 24) {
return t(
hours === 1
? "insights.relativeHoursAgoOne"
: "insights.relativeHoursAgoOther",
{ count: hours },
);
}
const days = Math.floor(hours / 24);
return t("insights.relativeDaysAgo", { count: days });
return t(
days === 1
? "insights.relativeDaysAgoOne"
: "insights.relativeDaysAgoOther",
{ count: days },
);
}