Sanitize malformed locale tags in navigator.languages

What does this MR do and why?

navigator.languages is supposed to contain BCP 47 language tags, but some environments inject malformed tags. When one of these reaches Intl.DateTimeFormat (via the globally-loaded time-ago tooltip and other locale/datetime consumers), it throws RangeError: Invalid language tag and the tooltip fails to render.

A previous fix (commit 7bea1ce39d46) stripped HTTP Accept-Language quality values (e.g. en;q=0.9), but Sentry shows the same RangeError continuing with a sibling variant — POSIX locale modifiers such as en-US@posix — which that fix did not cover.

This MR makes getPreferredLocales() (app/assets/javascripts/locale/index.js) robust against any malformed tag, not just the known variants:

  1. Salvage the language[-territory] part of each entry: strip HTTP quality values (;q=0.9), POSIX modifiers (@posix), codeset suffixes (.UTF-8), and stray commas, and convert _ separators to -. A raw POSIX locale like en_US.UTF-8@posix becomes en-US, keeping the user's locale preference instead of discarding it.
  2. Validate each candidate with Intl.getCanonicalLocales() inside a try/catch and drop any entry that still is not a valid BCP 47 tag (e.g. C).

Because invalid entries are filtered out rather than passed through, Intl APIs always receive valid locales and any future malformed variant is handled too.

References

Screenshots or screen recordings

No UI change — this only prevents a RangeError from being thrown; date/time rendering is unchanged for valid locales.

Before After
Intl.DateTimeFormat throws RangeError for malformed navigator.languages entries Malformed entries are salvaged to valid BCP 47 tags where possible, otherwise dropped; dates render with a valid locale

How to set up and validate locally

  1. Run the locale specs:
    yarn jest spec/frontend/locale/index_spec.js
  2. Or verify the core behavior in Node:
    node -e "try { new Intl.DateTimeFormat('en_US.UTF-8@posix'); } catch (e) { console.log('throws:', e.message); }
             console.log('salvaged:', Intl.getCanonicalLocales('en_US.UTF-8@posix'.split(/[;@.,]/)[0].replace(/_/g, '-')));"
    en_US.UTF-8@posix throws, while the salvaged en-US is valid.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Chaoyue Zhao

Merge request reports

Loading
Loading