From 113d61c02761f0f5913449e0d771336971e00a32 Mon Sep 17 00:00:00 2001 From: Akhil Singh <35478226+akhilsingh-git@users.noreply.github.com> Date: Thu, 11 Sep 2025 12:37:17 -0700 Subject: [PATCH] I18n: Prevent Intl.DateTimeFormat crash with invalid locales (#110522) * fix: prevent Intl.DateTimeFormat crash with invalid locales like 'c' - Add locale validation utilities to prevent crashes when LANG=c is set - Filter out invalid locales from navigator.languages before creating DateTimeFormat - Add fallback handling to use browser defaults when all locales are invalid - Fixes issue #110494 where Grafana crashes with 'RangeError: Incorrect locale information provided' - Maintains backward compatibility for valid locales Signed-off-by: Akhil Singh * refactor: simplify locale fix to use direct try-catch approach - Remove locale-utils.ts and locale-utils.test.ts files - Use simple try-catch in dates.ts files: try locale, fallback to 'en-US' - Use Laura's suggested approach in formats.ts with explicit variable declaration - Remove unused utility exports from index.ts - Maintains same functionality with cleaner, simpler code - Avoids adding to public API while still preventing crashes with invalid locales like 'c' Signed-off-by: Akhil Singh * style: run prettier to fix linting issues - Format code according to project prettier configuration - Fixes failing Lint Frontend check Signed-off-by: Akhil Singh --------- Signed-off-by: Akhil Singh --- packages/grafana-data/src/datetime/formats.ts | 8 +++++++- packages/grafana-i18n/src/dates.ts | 6 +++++- public/app/core/internationalization/dates.ts | 6 +++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/grafana-data/src/datetime/formats.ts b/packages/grafana-data/src/datetime/formats.ts index 92b9a99db62..67d0e4e6948 100644 --- a/packages/grafana-data/src/datetime/formats.ts +++ b/packages/grafana-data/src/datetime/formats.ts @@ -105,7 +105,13 @@ export function localTimeFormat( } // https://momentjs.com/docs/#/displaying/format/ - const dateTimeFormat = new Intl.DateTimeFormat(locale || undefined, options); + let dateTimeFormat: Intl.DateTimeFormat; + + try { + dateTimeFormat = new Intl.DateTimeFormat(locale || undefined, options); + } catch { + dateTimeFormat = new Intl.DateTimeFormat('en-US', options); + } const parts = dateTimeFormat.formatToParts(new Date()); const hour12 = dateTimeFormat.resolvedOptions().hour12; diff --git a/packages/grafana-i18n/src/dates.ts b/packages/grafana-i18n/src/dates.ts index c75cd2c86f4..c942226dfba 100644 --- a/packages/grafana-i18n/src/dates.ts +++ b/packages/grafana-i18n/src/dates.ts @@ -11,7 +11,11 @@ function clearMemoizedCache(fn: Memoized) { let regionalFormat: string | undefined; const createDateTimeFormatter = deepMemoize((locale: string | undefined, options: Intl.DateTimeFormatOptions) => { - return new Intl.DateTimeFormat(locale, options); + try { + return new Intl.DateTimeFormat(locale, options); + } catch { + return new Intl.DateTimeFormat('en-US', options); + } }); const createDurationFormatter = deepMemoize((locale: string | undefined, options: Intl.DurationFormatOptions) => { diff --git a/public/app/core/internationalization/dates.ts b/public/app/core/internationalization/dates.ts index 8d1e7467a93..9ef7bbbdb82 100644 --- a/public/app/core/internationalization/dates.ts +++ b/public/app/core/internationalization/dates.ts @@ -9,7 +9,11 @@ const deepMemoize: typeof memoize = (fn) => memoize(fn, { isEqual: deepEqual }); const isLocaleEnabled = config.featureToggles.localeFormatPreference; const createDateTimeFormatter = deepMemoize((locale: string, options: Intl.DateTimeFormatOptions) => { - return new Intl.DateTimeFormat(locale, options); + try { + return new Intl.DateTimeFormat(locale, options); + } catch { + return new Intl.DateTimeFormat('en-US', options); + } }); const createDurationFormatter = deepMemoize((locale: string, options: Intl.DurationFormatOptions) => {