Skip to content

fix(i18n): sanitize browser locales like en-US@posix#10219

Open
Bartok9 wants to merge 5 commits into
marimo-team:mainfrom
Bartok9:top10/fix-sanitize-browser-locale
Open

fix(i18n): sanitize browser locales like en-US@posix#10219
Bartok9 wants to merge 5 commits into
marimo-team:mainfrom
Bartok9:top10/fix-sanitize-browser-locale

Conversation

@Bartok9

@Bartok9 Bartok9 commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

This pull request was AI-assisted; human-reviewed line by line.

Summary

Some Chromium/Playwright environments report navigator.language as en-US@posix. Passing that tag straight into react-aria / Intl throws RangeError and the notebook never renders (including the stock intro tutorial).

Changes

  • Add normalizeBrowserLocale / tighter isValidLocale (modifier strip + supportedLocalesOf)
  • Route all safeLocale fallbacks through normalization with en-US last resort
  • Use the same helper for the data-table default formatting locale
  • Regression tests for @posix, charset suffixes, and junk config locales

Closes #9938

I have read the CLA Document and I hereby sign the CLA

@vercel

vercel Bot commented Jul 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment Jul 24, 2026 6:38am

Request Review

@Bartok9

Bartok9 commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

I have read the CLA Document and I hereby sign the CLA


export function safeLocale(locale: string | null | undefined): string {
if (locale && isValidLocale(locale)) {
return locale;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isValidLocale normalizes underscores only for validation, but this returns the raw value. A configured en_US passes the check and still reaches I18nProvider as an invalid locale; should this return normalizeBrowserLocale(locale)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in the latest commit.

isValidLocale was rewriting _- only inside the check, then safeLocale returned the original string, so a configured en_US could pass validation and still reach I18nProvider raw.

Valid config locales now go through normalizeBrowserLocale before handoff (same path as browser tags). Added a regression test: safeLocale("en_US") === "en-US".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in 0a4f6aa.

safeLocale now always runs valid config through normalizeBrowserLocale, so underscore forms like en_US are canonicalized to en-US before they reach I18nProvider. Added a unit test for that path.

Bartok9 added a commit to Bartok9/marimo that referenced this pull request Jul 24, 2026
isValidLocale accepts underscore forms (en_US) for validation only.
safeLocale now always returns a BCP 47 tag so I18nProvider never gets
raw underscore locales. Addresses review on marimo-team#10219.
@Bartok9

Bartok9 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @Sanjays2402 — addressed your review:

  • safeLocale now returns normalizeBrowserLocale(locale) for valid configured locales (so en_US becomes en-US before I18nProvider)
  • Regression test added for the underscore-config case

Ready for another look.

@Bartok9

Bartok9 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Addressed @Sanjays2402's review: safeLocale now returns normalizeBrowserLocale(locale) for valid config (so en_USen-US for I18nProvider). Tests green (locale-provider.test.tsx, 14 passing).

@kirangadhave

Copy link
Copy Markdown
Member

@Light2Dark since you attempted a fix previously can you please evaluate this PR?

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a frontend crash caused by non‑BCP47 browser locale tags (e.g. en-US@posix) being passed into Intl/react-aria, which can throw RangeError and prevent notebooks (including the intro tutorial) from rendering.

Changes:

  • Add locale tag normalization/validation and route LocaleProvider locale selection through it with an en-US fallback.
  • Add regression tests covering Playwright/Chromium-style locale modifiers and other malformed tags.
  • Apply the same normalization to the data-table column-formatting default locale.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
frontend/src/core/i18n/locale-provider.tsx Introduces locale normalization/validation helpers and uses them to provide a safe locale to I18nProvider.
frontend/src/core/i18n/tests/locale-provider.test.tsx Adds/updates tests to cover normalized browser locales and fallback behavior.
frontend/src/components/data-table/column-formatting/feature.ts Uses the normalization helper for the data-table’s default formatting locale.
Comments suppressed due to low confidence (1)

frontend/src/core/i18n/locale-provider.tsx:72

  • safeLocale validates the raw configured locale before normalization, so config values like en-US@posix or en_US.UTF-8 (both mentioned as inputs to normalize) are treated as invalid and ignored. Normalize/strip common modifiers for the configured value before deciding to fall back to navigator.language.
  if (locale && isValidLocale(locale)) {
    return normalizeBrowserLocale(locale);
  }

Comment on lines 57 to 61
if (Intl.NumberFormat.supportedLocalesOf([normalized]).length === 0) {
return false;
}
new Intl.Locale(normalized);
return true;
Comment on lines 12 to 14
import type { DataType } from "@/core/kernel/messages";
import { normalizeBrowserLocale } from "@/core/i18n/locale-provider";
import { logNever } from "@/utils/assertNever";
Bartok9 and others added 4 commits July 24, 2026 02:35
Chromium/Playwright can report navigator.language as en-US@posix, which
throws RangeError in Intl/react-aria and crashes the app before render.

Normalize browser tags (strip @/charset modifiers), validate with
supportedLocalesOf, and fall back to en-US. Reuse normalization for the
data table default locale.

Closes marimo-team#9938
isValidLocale accepts underscore forms (en_US) for validation only.
safeLocale now always returns a BCP 47 tag so I18nProvider never gets
raw underscore locales. Addresses review on marimo-team#10219.
Address review feedback while Light2Dark evaluates:

- Move normalize/isValid/safeLocale into core/i18n/locale.ts so the
  data-table feature does not import the React LocaleProvider module
- Guard missing Intl.Locale (do not force en-US when only NumberFormat
  is available)
- Normalize config locales with @posix / .UTF-8 before navigator fallback
- Prefer vi.stubGlobal for navigator mocks (no leaked navigator object)
- Tests: 17 passing including de-DE@posix and config-modifier cases

Refs marimo-team#9938
@Bartok9
Bartok9 force-pushed the top10/fix-sanitize-browser-locale branch from 0a4f6aa to ff3c6da Compare July 24, 2026 06:36
@Bartok9

Bartok9 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Evaluation notes for @Light2Dark (and anyone reviewing)

@kirangadhave asked for your eyes since you previously attempted this in #9945 — thank you. Here's a concise self-eval of this PR vs that attempt, plus what I just pushed to make review easier.

Problem (issue #9938)

Chromium/Playwright can report navigator.language as en-US@posix. Passing that into Intl / react-aria throws RangeError: Incorrect locale information provided and the notebook never renders (including the stock intro tutorial).

How this differs from closed #9945

#9945 (yours) #10219 (this)
Approach browser-locale.ts + often omit locale prop so react-aria picks default Always hand I18nProvider a safe BCP 47 tag via safeLocale
@posix Ended at strip-or-fallback (after review) Strip @… / .charset, underscore→hyphen, re-validate, language-only fallback, then en-US
Config path Validated user config; browser path separate Config and browser go through the same normalizer (incl. en_USen-US)
Data table DEFAULT_LOCALE constant normalizeBrowserLocale(navigator.language) so tables match browser when valid
Settings UI filter Filtered navigator.languages in the locale dropdown Not touched (happy to add if you want parity)

Neither approach is wrong; this one is a bit more aggressive about always supplying a concrete locale so we never depend on react-aria’s internal default path for the crash case.

Latest push (ff3c6dad9) — review feedback folded in

While you evaluate, I addressed open nits so you don’t have to chase them:

  1. Pure module frontend/src/core/i18n/locale.ts — helpers no longer live only in the React locale-provider.tsx, so column-formatting/feature.ts doesn’t pull jotai/react-aria (Copilot).
  2. Intl.Locale optional — if Intl.Locale is missing, we don’t treat every locale as invalid (Copilot).
  3. Config with modifierssafeLocale("en-US@posix") / en_US.UTF-8 recover the base tag instead of discarding config and jumping to navigator (Copilot low-confidence note).
  4. Testsvi.stubGlobal + unstubAllGlobals (no leaked navigator). 17/17 vitest green locally on locale-provider.test.tsx.
  5. Rebased onto current main (0 behind).

What I’d love from the evaluation

  • Does always-passing a locale (vs omitting for system default) match marimo’s preferred i18n model?
  • Want the settings dropdown filter from fix: guard invalid navigator.language locale tags #9945 folded in here?
  • Any preference to rename FALLBACK_LOCALEDEFAULT_LOCALE for consistency with your earlier naming?

Happy to adjust immediately off your notes. Thanks again for looking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Frontend crashes when navigator.language is en-US@posix

4 participants