From e30cc43d6c353f945dc25675a41c375520edfcc5 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Wed, 11 Mar 2026 22:02:57 +0000 Subject: [PATCH] fix: normalize "original" locale to "en" for datetime formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When users select "Untranslated" mode, the locale is set to "original". This is not a valid BCP 47 locale tag, so browser Intl APIs fall back to the OS language, causing mixed-language strings like "Revealed el próximo mes". Add a normalizeIntlLocale() utility that maps "original" to "en" and apply it consistently across all components that pass locale to intlFormat, intlFormatDistance, or the element. Fixes #4122 Co-authored-by: leonardthethird --- .../(home)/components/research_and_updates.tsx | 4 +++- .../components/consumer_post_card/upcoming_cp.tsx | 3 ++- .../components/cp_reveal_time/cp_reveal_time.tsx | 3 ++- front_end/src/components/ui/local_daytime.tsx | 11 ++--------- front_end/src/utils/formatters/date.ts | 15 ++++++++++++--- 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/front_end/src/app/(main)/(home)/components/research_and_updates.tsx b/front_end/src/app/(main)/(home)/components/research_and_updates.tsx index 551137dd80..a04e10f288 100644 --- a/front_end/src/app/(main)/(home)/components/research_and_updates.tsx +++ b/front_end/src/app/(main)/(home)/components/research_and_updates.tsx @@ -5,6 +5,8 @@ import Link from "next/link"; import { getLocale, getTranslations } from "next-intl/server"; import { FC } from "react"; +import { normalizeIntlLocale } from "@/utils/formatters/date"; + import WithServerComponentErrorBoundary from "@/components/server_component_error_boundary"; import Button from "@/components/ui/button"; import { NotebookPost } from "@/types/post"; @@ -26,7 +28,7 @@ type Props = { const ResearchAndUpdates: FC = async ({ posts, className }) => { const t = await getTranslations(); - const locale = await getLocale(); + const locale = normalizeIntlLocale(await getLocale()); return (
diff --git a/front_end/src/components/consumer_post_card/upcoming_cp.tsx b/front_end/src/components/consumer_post_card/upcoming_cp.tsx index 42ea74ecaa..d5292f0ff4 100644 --- a/front_end/src/components/consumer_post_card/upcoming_cp.tsx +++ b/front_end/src/components/consumer_post_card/upcoming_cp.tsx @@ -4,6 +4,7 @@ import { FC } from "react"; import RelativeTime from "@/components/ui/relative_time"; import cn from "@/utils/core/cn"; +import { normalizeIntlLocale } from "@/utils/formatters/date"; type Props = { cpRevealsOn: string; @@ -12,7 +13,7 @@ type Props = { const UpcomingCP: FC = ({ cpRevealsOn, className }) => { const t = useTranslations(); - const locale = useLocale(); + const locale = normalizeIntlLocale(useLocale()); return (
{t("cpRevealed")} diff --git a/front_end/src/components/cp_reveal_time/cp_reveal_time.tsx b/front_end/src/components/cp_reveal_time/cp_reveal_time.tsx index ff1a4d0c35..f142413af0 100644 --- a/front_end/src/components/cp_reveal_time/cp_reveal_time.tsx +++ b/front_end/src/components/cp_reveal_time/cp_reveal_time.tsx @@ -3,6 +3,7 @@ import { useLocale, useTranslations } from "next-intl"; import React, { FC } from "react"; import RelativeTime from "@/components/ui/relative_time"; +import { normalizeIntlLocale } from "@/utils/formatters/date"; type Props = { cpRevealTime: string; @@ -16,7 +17,7 @@ const CPRevealTime: FC = ({ className = "", }) => { const t = useTranslations(); - const locale = useLocale(); + const locale = normalizeIntlLocale(useLocale()); if (hiddenUntilView) { const revealDate = new Date(cpRevealTime); diff --git a/front_end/src/components/ui/local_daytime.tsx b/front_end/src/components/ui/local_daytime.tsx index 4c8df72b13..c6c81ae535 100644 --- a/front_end/src/components/ui/local_daytime.tsx +++ b/front_end/src/components/ui/local_daytime.tsx @@ -2,21 +2,14 @@ import { useLocale } from "next-intl"; import { FC } from "react"; import RelativeTime from "@/components/ui/relative_time"; -import { formatDate } from "@/utils/formatters/date"; +import { formatDate, normalizeIntlLocale } from "@/utils/formatters/date"; type Props = { date?: string; }; const LocalDaytime: FC = ({ date }) => { - let locale = useLocale(); - if (locale === "original") { - // For some reason, when the locale is "original" (Untranslated), the the server and client - // endup with different values for the localValue variable. This is a workaround to - // make sure the dates render correctly on both and default to English locale when - // in Untranslated mode - locale = "en"; - } + const locale = normalizeIntlLocale(useLocale()); const localValue = date ? formatDate(locale, new Date(date)) : ""; return ( diff --git a/front_end/src/utils/formatters/date.ts b/front_end/src/utils/formatters/date.ts index 46f7d09ce7..907c330458 100644 --- a/front_end/src/utils/formatters/date.ts +++ b/front_end/src/utils/formatters/date.ts @@ -92,7 +92,7 @@ export function formatDate(locale: string, date: Date) { return intlFormat( new Date(date), { year: "numeric", month: "short", day: "numeric" }, - { locale } + { locale: normalizeIntlLocale(locale) } ); } @@ -106,7 +106,7 @@ export function formatDatetime(locale: string, date: Date) { hour: "numeric", minute: "numeric", }, - { locale } + { locale: normalizeIntlLocale(locale) } ); } @@ -133,7 +133,7 @@ export function formatRelativeDate( let dateStr: string = ""; if (Math.abs(delta) < relCutoff) { dateStr = intlFormatDistance(date, now, { - locale, + locale: normalizeIntlLocale(locale), numeric: "always", style: short ? "short" : "long", }); @@ -189,6 +189,15 @@ export function formatDurationToShortStr(duration: Duration): string { return str; } +/** + * Normalizes the app locale for use with the browser's Intl API. + * The "original" locale (Untranslated mode) is not a valid BCP 47 tag, + * so it falls back to the OS language. This maps it to "en" instead. + */ +export function normalizeIntlLocale(locale: string): string { + return locale === "original" ? "en" : locale; +} + export const getDateFnsLocale = (locale: string) => { switch (locale) { case "es":