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 <singhakhil69@gmail.com>

* 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 <singhakhil69@gmail.com>

* style: run prettier to fix linting issues

- Format code according to project prettier configuration
- Fixes failing Lint Frontend check

Signed-off-by: Akhil Singh <singhakhil69@gmail.com>

---------

Signed-off-by: Akhil Singh <singhakhil69@gmail.com>
This commit is contained in:
Akhil Singh
2025-09-11 15:37:17 -04:00
committed by GitHub
parent 9a54243f09
commit 113d61c027
3 changed files with 17 additions and 3 deletions
@@ -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;
+5 -1
View File
@@ -11,7 +11,11 @@ function clearMemoizedCache(fn: Memoized<AnyFn>) {
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) => {
@@ -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) => {