diff --git a/ui/__snapshots__/qa.spec.ts.snap b/ui/__snapshots__/qa.spec.ts.snap index a853551a..a2779860 100644 --- a/ui/__snapshots__/qa.spec.ts.snap +++ b/ui/__snapshots__/qa.spec.ts.snap @@ -106,7 +106,7 @@ var closeIconHTML = ''; var closeBtnHTML = \`\`; // helpers/countdown.ts -var formatDeadline = (time) => { +var formatDeadline = (time, locales = [LOCALE, void 0]) => { let unit = "second"; let timeLeft = (time - Date.now()) / 1e3; if (timeLeft >= 60) { @@ -118,14 +118,22 @@ var formatDeadline = (time) => { unit = "hour"; } const isLastMinute = unit === "minute" && timeLeft < 2; - const formattedTimeLeft = new Intl.NumberFormat(LOCALE, { - style: "unit", - unitDisplay: "long", - minimumFractionDigits: isLastMinute ? 1 : 0, - maximumFractionDigits: isLastMinute ? 1 : 0, - unit - }).format(Math.max(0, timeLeft)); - return \`in \${formattedTimeLeft}\`; + const nonNegTimeLeft = Math.max(0, timeLeft); + for (const locale of locales) { + try { + const formattedTimeLeft = new Intl.NumberFormat(locale, { + style: "unit", + unitDisplay: "long", + minimumFractionDigits: isLastMinute ? 1 : 0, + maximumFractionDigits: isLastMinute ? 1 : 0, + unit + }).format(nonNegTimeLeft); + return \`in \${formattedTimeLeft}\`; + } catch (error) { + console.warn(\`Failed to format time using \${locale} locale\`, error); + } + } + return \`in \${nonNegTimeLeft} \${unit}\${nonNegTimeLeft === 1 ? "" : "s"}\`; }; var getCountdownDelay = (deadline) => deadline - Date.now() > 12e4 ? 6e4 : 1e3; var setCountdown = (selector, deadline) => { diff --git a/ui/helpers/countdown.spec.ts b/ui/helpers/countdown.spec.ts index cf0c7dd3..1389793c 100644 --- a/ui/helpers/countdown.spec.ts +++ b/ui/helpers/countdown.spec.ts @@ -46,6 +46,31 @@ describe("Countdown helpers", () => { expect(formatDeadline(Date.now() + offset * 1000)).toMatchSnapshot(); }, ); + + test.each([ + [10000, "10 seconds"], + [60000, "1 minute"], + [-10000, "0 seconds"], + ])(`should handle invalid locales %#`, (offset, label) => { + const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); + expect( + formatDeadline(Date.now() + offset, [ + "invalid_locale_id", + "another_invalid_one", + ]), + ).toBe(`in ${label}`); + expect(warnSpy).toHaveBeenCalledTimes(2); + expect(warnSpy.mock.calls).toEqual([ + [ + "Failed to format time using invalid_locale_id locale", + expect.any(Error), + ], + [ + "Failed to format time using another_invalid_one locale", + expect.any(Error), + ], + ]); + }); }); describe("getCountdownDelay() helper", () => { diff --git a/ui/helpers/countdown.ts b/ui/helpers/countdown.ts index 62d86f77..1903a6a8 100644 --- a/ui/helpers/countdown.ts +++ b/ui/helpers/countdown.ts @@ -1,4 +1,7 @@ -export const formatDeadline = (time: number): string => { +export const formatDeadline = ( + time: number, + locales = [LOCALE, undefined], +): string => { let unit: "second" | "minute" | "hour" = "second"; let timeLeft = (time - Date.now()) / 1000; if (timeLeft >= 60) { @@ -10,14 +13,22 @@ export const formatDeadline = (time: number): string => { unit = "hour"; } const isLastMinute = unit === "minute" && timeLeft < 2; - const formattedTimeLeft = new Intl.NumberFormat(LOCALE, { - style: "unit", - unitDisplay: "long", - minimumFractionDigits: isLastMinute ? 1 : 0, - maximumFractionDigits: isLastMinute ? 1 : 0, - unit, - }).format(Math.max(0, timeLeft)); - return `in ${formattedTimeLeft}`; + const nonNegTimeLeft = Math.max(0, timeLeft); + for (const locale of locales) { + try { + const formattedTimeLeft = new Intl.NumberFormat(locale, { + style: "unit", + unitDisplay: "long", + minimumFractionDigits: isLastMinute ? 1 : 0, + maximumFractionDigits: isLastMinute ? 1 : 0, + unit, + }).format(nonNegTimeLeft); + return `in ${formattedTimeLeft}`; + } catch (error) { + console.warn(`Failed to format time using ${locale} locale`, error); + } + } + return `in ${nonNegTimeLeft} ${unit}${nonNegTimeLeft === 1 ? "" : "s"}`; }; export const getCountdownDelay = (deadline: number): number =>