fix(i18n): carry the count in One strings where one is more than 1

The plural tier comes from CLDR, and in Russian the one category also
holds 21, 31, 101, while French and Brazilian Portuguese file 0 there.
Thirty One strings in those bundles spelled out the 1, so 21 days read
as one day. They now take {count}, as do two Russian One strings that
spelled the number as a word, and every direct call of such a key passes
the count. A guard finds the locales whose one is wider than 1 from
Intl.PluralRules and holds both ends.
This commit is contained in:
Marc-André Bombeck committed 2026-11-07 01:33:19 +01:00
1 parent 2adea7052b
commit 4921188f22
14 files changed
+270 -111

No files matched your search

@@ -290,7 +290,7 @@ export function InviteTokensSection() {
const days = daysUntil(invite.expiresAt, now);
if (days <= 0) return "—";
return days === 1
? t("admin.invites.expiresInDaysOne")
? t("admin.invites.expiresInDaysOne", { count: days })
: t("admin.invites.expiresInDaysOther", { count: days });
}
+1 -1
View File
@@ -26,7 +26,7 @@ export function TrendHint({ count }: TrendHintProps) {
const description =
result.remaining === 1
? t("trendHints.remainingOne")
? t("trendHints.remainingOne", { count: result.remaining })
: t("trendHints.remainingMany", { count: result.remaining });
return (
@@ -74,7 +74,9 @@ export function IllnessInsightsCard({
<div className="space-y-4 text-sm">
<p className="text-foreground">
{data!.episodeCount === 1
? t("illness.insights.countOne")
? t("illness.insights.countOne", {
count: data!.episodeCount,
})
: t("illness.insights.countOther", {
count: data!.episodeCount,
})}
+1 -1
View File
@@ -77,7 +77,7 @@ function NestedEpisodes({ episodes }: { episodes?: IllnessEpisodeDTO[] }) {
{allFlares ? (
<p className="text-muted-foreground text-xs font-medium">
{count === 1
? t("illness.flares.countOne")
? t("illness.flares.countOne", { count })
: t("illness.flares.countOther", { count })}
</p>
) : null}
+1 -1
View File
@@ -52,7 +52,7 @@ export function EcgCrossLink({ enabled = true, className }: EcgCrossLinkProps) {
const countText =
count === 1
? t("insights.ecg.crossLink.recordingsOne")
? t("insights.ecg.crossLink.recordingsOne", { count })
: t("insights.ecg.crossLink.recordingsMany", { count });
const resultText = resultKey
? t("insights.ecg.crossLink.latestResult", { result: t(resultKey) })
@@ -123,6 +123,7 @@ export function GlucoseAdvancedDisclosure({
{summary.count === 1
? t(
"insights.bloodGlucose.clinical.advanced.byContext.readingsShortOne",
{ count: summary.count },
)
: t(
"insights.bloodGlucose.clinical.advanced.byContext.readingsShort",
@@ -90,14 +90,14 @@ export function GlucoseClinicalPanel() {
const hasAny = clinical.readingCount > 0;
const readingsLabel =
clinical.readingCount === 1
? t("insights.bloodGlucose.clinical.readingsCountOne")
? t("insights.bloodGlucose.clinical.readingsCountOne", { count: 1 })
: t("insights.bloodGlucose.clinical.readingsCount", {
count: clinical.readingCount,
});
const spanDays = Math.max(1, Math.round(clinical.actualSpanDays));
const daysLabel =
spanDays === 1
? t("insights.bloodGlucose.clinical.daysCountOne")
? t("insights.bloodGlucose.clinical.daysCountOne", { count: spanDays })
: t("insights.bloodGlucose.clinical.daysCount", { days: spanDays });
return (
@@ -183,12 +183,12 @@ export function IntradayPulseChart({
: t("insights.intradayPulse.caption");
const coverageNote = coverage
? coverage.readingCount === 1
? t("insights.intradayPulse.coverageOne", { hours: coverage.hours })
: t("insights.intradayPulse.coverageMany", {
count: coverage.readingCount,
hours: coverage.hours,
})
? t(
coverage.readingCount === 1
? "insights.intradayPulse.coverageOne"
: "insights.intradayPulse.coverageMany",
{ count: coverage.readingCount, hours: coverage.hours },
)
: null;
const dayLabel = isToday
@@ -698,7 +698,7 @@ function MedicationTableRowItem({
)}
>
{stock === 1
? t("medications.tableStockDoseOne")
? t("medications.tableStockDoseOne", { count: stock })
: t("medications.tableStockDoses", { count: stock })}
</span>
{runwayDays !== null && (
@@ -82,6 +82,12 @@ interface ScheduleRevisionsResponse {
revisions: ScheduleRevisionRow[];
}
/** The "show earlier eras" toggle, by how many there are. */
const SHOW_ERAS_KEY = {
one: "medications.detail.zeitplan.history.showErasOne",
other: "medications.detail.zeitplan.history.showErasOther",
} as const;
/**
* Dose-time readout of one era: the union of its entries' times,
* deduped + sorted ("07:00 / 19:00"). PRN-only eras have no times —
@@ -284,11 +290,9 @@ export function ScheduleHistoryTimeline({
/>
{expanded
? t("medications.detail.zeitplan.history.hideEras")
: revisions.length === 1
? t("medications.detail.zeitplan.history.showErasOne")
: t("medications.detail.zeitplan.history.showErasOther", {
count: revisions.length,
})}
: t(SHOW_ERAS_KEY[revisions.length === 1 ? "one" : "other"], {
count: revisions.length,
})}
</Button>
)}
<Button