Skip to content

fix: sanitize non-BCP-47 browser locales that crash the frontend - #10318

Merged
mscolnick merged 1 commit into
mainfrom
fix/sanitize-browser-locale-9938
Jul 24, 2026
Merged

mscolnick merged 1 commit into
mainfrom
fix/sanitize-browser-locale-9938

Conversation

@Light2Dark

@Light2Dark Light2Dark commented Jul 24, 2026 •

Copy link
Copy Markdown
Member

📝 Summary

Closes #9938

Some environments — notably Chromium under Playwright/Docker — report navigator.language as en-US@posix. That value is a POSIX locale string, not a valid BCP 47 tag, so passing it into Intl (via react-aria's I18nProvider, which constructs Intl.Locale) throws RangeError: Incorrect locale information provided and the frontend crashes before any notebook renders — including the stock intro.py tutorial.

The previous safeLocale validated the configured locale but returned the raw navigator.language on both fallback paths, so with the default (unset) config the crash tag flowed straight through.

Changes

  • Add a pure frontend/src/core/i18n/locale.ts module:
    • normalizeLocale(tag) strips non-BCP-47 modifiers (@posix, .UTF-8), converts _→-, falls back to the language subtag, and returns null when nothing usable remains.
    • safeLocale(config) becomes a clean precedence chain: normalizeLocale(config) ?? browserLocale() ?? FALLBACK_LOCALE.
    • Validation uses new Intl.Locale(tag) — the exact constructor that throws — rather than supportedLocalesOf, which would additionally remap legitimate-but-uncommon locales to en-US and is runtime/CLDR-dependent.
  • Route LocaleProvider through the module (config and browser tags now share one normalizer).
  • Sanitize the data-table default formatting locale (column-formatting/feature.ts), the other site that fed raw navigator.language into Intl.*.

Why this approach

Downstream formatting reads its locale from react-aria's useLocale(), which flows from I18nProvider — so fixing the provider fixes those consumers automatically. Keeping the helpers in a pure module avoids pulling React/jotai into the data-table feature, and validating with Intl.Locale targets the crash precisely without silently changing valid user locales.

Acknowledgement

Thanks to @Bartok9, whose earlier (now-closed) #10219 explored the same overall approach — a pure locale.ts module, normalizeLocale (strip @posix/.UTF-8, _→-, language-subtag fallback) plus a safeLocale precedence chain, Intl.Locale validation over supportedLocalesOf, and sanitizing the column-formatting/feature.ts default locale.

📋 Pre-Review Checklist

  • For large changes, or changes that affect the public API: this change was discussed or approved through an issue — see Frontend crashes when navigator.language is en-US@posix #9938.
  • Any AI generated code has been reviewed line-by-line by the human PR author, who stands by it.
  • Video or media evidence is provided for any visual changes (optional).

✅ Merge Checklist

  • I have read the contributor guidelines.
  • Documentation has been updated where applicable, including docstrings for API changes.
  • Tests have been added for the changes made.

Made with Cursor

Some environments (e.g. Chromium under Playwright/Docker) report
`navigator.language` as `en-US@posix`, which `Intl` rejects with
`RangeError: Incorrect locale information provided`, crashing the app
before any notebook renders.

Add a pure `core/i18n/locale.ts` module that normalizes browser/config
locale tags (strip `@modifier`/`.charset`, `_`->`-`, language-subtag
fallback) and validate via `Intl.Locale`. Route the `LocaleProvider` and
the data-table default formatting locale through it.

Closes #9938

Co-authored-by: Cursor <cursoragent@cursor.com>
@vercel

vercel Bot commented Jul 24, 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 8:34am

Request Review

@Bartok9

Bartok9 commented Jul 24, 2026

Copy link
Copy Markdown

Thanks for driving this to the finish, @Light2Dark — happy to see #9938 getting a solid fix, and I like the extra edge cases you caught.

One small, humble ask on attribution: this lands very close to the approach in my (now-closed) #10219 — the pure frontend/src/core/i18n/locale.ts module, the normalizeLocale (strip @posix/.UTF-8, _→-, language-subtag fallback) plus safeLocale precedence chain, validating with new Intl.Locale(tag) rather than supportedLocalesOf, and sanitizing column-formatting/feature.ts as the second Intl.* site. Would you be open to adding a Co-authored-by: Bartok9 <...> line (or a quick @Bartok9 mention) so the shared design work is credited? Totally defer to you and the maintainers on the merge — just want the contribution acknowledged. Thanks again!

@Bartok9

Bartok9 commented Jul 24, 2026

Copy link
Copy Markdown

Thanks @Light2Dark for picking this up and shipping a cleaner version of the #9938 fix — especially the pure locale.ts module and the tighter Intl.Locale validation path. Happy to see the crash case get a maintainer-owned landing.

If it’s easy, a short note that #10219 / @Bartok9 explored the same approach (normalize @posix/charset modifiers, _→-, pure helper, data-table default locale) would be appreciated for attribution. Either way, no blockers from us — glad this is moving.

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‑BCP‑47 navigator.language values (e.g. en-US@posix) being passed into Intl via react-aria’s I18nProvider, by normalizing/validating both configured and browser locales before use.

Changes:

  • Added a dedicated, pure i18n locale helper module that normalizes POSIX/ICU/charset/underscore locale strings and validates via Intl.Locale.
  • Updated LocaleProvider to use the shared safeLocale() resolution chain instead of passing through raw navigator.language.
  • Sanitized the data-table column formatting default locale and added focused unit tests for the new normalization logic and provider behavior.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
frontend/src/core/i18n/locale.ts Introduces locale normalization + Intl.Locale validation with safe fallbacks.
frontend/src/core/i18n/locale-provider.tsx Routes react-aria I18nProvider locale through safeLocale() to prevent crashes.
frontend/src/core/i18n/tests/locale.test.ts Adds unit tests for validation/normalization + browser/config fallback behavior.
frontend/src/core/i18n/tests/locale-provider.test.tsx Ensures LocaleProvider always supplies a safe, concrete locale (incl. POSIX cases).
frontend/src/components/data-table/column-formatting/feature.ts Uses browserLocale() instead of raw navigator.language for formatting defaults.

@cubic-dev-ai cubic-dev-ai Bot 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.

No issues found across 5 files

Architecture diagram
sequenceDiagram
    participant Browser as Browser (Navigator)
    participant State as User Config (Jotai)
    participant Provider as LocaleProvider
    participant Logic as i18n/locale.ts
    participant Aria as I18nProvider (React Aria)
    participant Table as DataTable Feature

    Note over Provider, Logic: App Initialization Flow
    Provider->>State: Read localeAtom
    State-->>Provider: Configured locale (e.g., "en_US")
    
    Provider->>Logic: NEW: safeLocale(configValue)
    Logic->>Logic: NEW: normalizeLocale(configValue)
    Logic->>Logic: Validate via new Intl.Locale()
    
    alt Config is invalid or null
        Logic->>Browser: NEW: browserLocale()
        Browser-->>Logic: navigator.language (e.g., "en-US@posix")
        Logic->>Logic: NEW: normalizeLocale(browserValue)
        Note right of Logic: Strips @posix, .UTF-8, etc.
    end
    
    Logic-->>Provider: Valid BCP-47 string (e.g., "en-US")
    Provider->>Aria: CHANGED: Mount with sanitized locale
    Aria-->>Provider: Provide i18n context to tree

    Note over Table, Logic: Data Table Initialization
    Table->>Logic: NEW: browserLocale()
    Logic->>Browser: Access navigator.language
    Browser-->>Logic: "en-US@posix"
    Logic->>Logic: normalizeLocale()
    Logic-->>Table: "en-US"
    Note over Table: Safe for Intl.NumberFormat values
Loading

Re-trigger cubic

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report for ./frontend

Status Category Percentage Covered / Total
🔵 Lines 77% 77177 / 100229
🔵 Statements 77% 77177 / 100229
🔵 Functions 71.17% 652 / 916
🔵 Branches 79.62% 4489 / 5638
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
frontend/src/components/data-table/column-formatting/feature.ts 90.14% 70.73% 100% 90.14% 162, 164, 182, 205, 214, 220-221, 231-233, 235-241, 243-245, 279
frontend/src/core/i18n/locale-provider.tsx 100% 100% 100% 100%
frontend/src/core/i18n/locale.ts 100% 100% 100% 100%
Generated in workflow #19907 for commit 4ef98a7 by the Vitest Coverage Report Action

@Light2Dark

Light2Dark commented Jul 24, 2026 •

Copy link
Copy Markdown
Member Author

Got it @Bartok9 , adding in the PR. Thank you.

@Light2Dark
Light2Dark requested a review from kirangadhave July 24, 2026 08:47
@Light2Dark
Light2Dark marked this pull request as ready for review July 24, 2026 08:48
@Bartok9

Bartok9 commented Jul 24, 2026

Copy link
Copy Markdown

Appreciate it, @Light2Dark — saw the Acknowledgement land on the PR body. Thanks for the credit and for shipping the extra edge cases.

@mscolnick
mscolnick merged commit 7fd4f02 into main Jul 24, 2026
38 of 40 checks passed
@mscolnick
mscolnick deleted the fix/sanitize-browser-locale-9938 branch July 24, 2026 13:12
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Development release published. You may be able to view the changes at https://marimo.app?v=0.23.16-dev3

williambdean pushed a commit to williambdean/marimo that referenced this pull request Sep 6, 2026
…imo-team#10318)

## 📝 Summary

Closes marimo-team#9938

Some environments — notably Chromium under Playwright/Docker — report
`navigator.language` as `en-US@posix`. That value is a POSIX locale
string, not a valid [BCP 47](https://www.rfc-editor.org/info/rfc5646)
tag, so passing it into `Intl` (via react-aria's `I18nProvider`, which
constructs `Intl.Locale`) throws `RangeError: Incorrect locale
information provided` and the frontend crashes before any notebook
renders — including the stock `intro.py` tutorial.

The previous `safeLocale` validated the *configured* locale but returned
the raw `navigator.language` on both fallback paths, so with the default
(unset) config the crash tag flowed straight through.

### Changes

- Add a pure `frontend/src/core/i18n/locale.ts` module:
- `normalizeLocale(tag)` strips non-BCP-47 modifiers (`@posix`,
`.UTF-8`), converts `_`→`-`, falls back to the language subtag, and
returns `null` when nothing usable remains.
- `safeLocale(config)` becomes a clean precedence chain:
`normalizeLocale(config) ?? browserLocale() ?? FALLBACK_LOCALE`.
- Validation uses `new Intl.Locale(tag)` — the exact constructor that
throws — rather than `supportedLocalesOf`, which would additionally
remap legitimate-but-uncommon locales to `en-US` and is
runtime/CLDR-dependent.
- Route `LocaleProvider` through the module (config and browser tags now
share one normalizer).
- Sanitize the data-table default formatting locale
(`column-formatting/feature.ts`), the other site that fed raw
`navigator.language` into `Intl.*`.

### Why this approach

Downstream formatting reads its locale from react-aria's `useLocale()`,
which flows from `I18nProvider` — so fixing the provider fixes those
consumers automatically. Keeping the helpers in a pure module avoids
pulling React/jotai into the data-table feature, and validating with
`Intl.Locale` targets the crash precisely without silently changing
valid user locales.

### Acknowledgement

Thanks to @Bartok9, whose earlier (now-closed) marimo-team#10219 explored the same
overall approach — a pure `locale.ts` module, `normalizeLocale` (strip
`@posix`/`.UTF-8`, `_`→`-`, language-subtag fallback) plus a
`safeLocale` precedence chain, `Intl.Locale` validation over
`supportedLocalesOf`, and sanitizing the `column-formatting/feature.ts`
default locale.

## 📋 Pre-Review Checklist

- [x] For large changes, or changes that affect the public API: this
change was discussed or approved through an issue — see marimo-team#9938.
- [x] Any AI generated code has been reviewed line-by-line by the human
PR author, who stands by it.
- [ ] Video or media evidence is provided for any visual changes
(optional).

## ✅ Merge Checklist

- [x] I have read the [contributor
guidelines](https://github.com/marimo-team/marimo/blob/main/CONTRIBUTING.md).
- [x] Documentation has been updated where applicable, including
docstrings for API changes.
- [x] Tests have been added for the changes made.

Made with [Cursor](https://cursor.com)

Co-authored-by: Cursor <cursoragent@cursor.com>

This branch was successfully deployed

1 active deployment
Preview — 4ef98a73 Deployed Jul 24, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

4 participants