Skip to content

fix(i18n): inject language pack into bootstrap to translate code-split chunks#39357

Merged
rusackas merged 8 commits into
apache:masterfrom
alex-poor:fix/i18n-bootstrap-injection
Jul 6, 2026
Merged

fix(i18n): inject language pack into bootstrap to translate code-split chunks#39357
rusackas merged 8 commits into
apache:masterfrom
alex-poor:fix/i18n-bootstrap-injection

Conversation

@alex-poor

Copy link
Copy Markdown
Contributor

SUMMARY

Fixes #35330. Module-level const X = t('...') calls in code-split chunks evaluate before the async /superset/language_pack/<lang>/ fetch in preamble.ts can resolve, capturing the English msgid forever. #36893 fixed the initial-render race by awaiting initPreamble() before ReactDOM.render, but lazily-loaded chunks (e.g. DashboardContainerChartContextMenuuseDrillDetailMenuItems) still evaluate later and hit an unconfigured translator. Result: strings inside component render bodies translate, but strings captured in module-scope constants stay English, even though bootstrapData.common.locale is set and the language pack endpoint returns a valid pack. Same symptom reported on 5.0.0 / 6.0.0 for pt_BR, ru, zh in #35330 and linked issues.

Fix: inject the full Jed language pack into the HTML bootstrap payload under common.language_pack. An inline <script> in spa.html — which runs before the entry bundle loads — stashes the pack on window.__SUPERSET_LANGUAGE_PACK__. TranslatorSingleton lazily self-configures from that global on first getInstance(), so every chunk-local copy that webpack splitChunks may produce ends up configured identically. preamble.ts now prefers the bootstrap-injected pack and keeps the async fetch as fallback for entries that don't extend spa.html (embedded, legacy).

Net effect: t() returns the translated msgstr on first evaluation everywhere, including module-level constants in lazy chunks.

Files changed:

  • superset/views/base.py — read translations/<lang>/LC_MESSAGES/messages.json into common.language_pack (skipped for en, silently absent if the file isn't present, respects the existing cached_common_bootstrap_data memoization).
  • superset/templates/superset/spa.html — inline script that parses the bootstrap div and sets window.__SUPERSET_LANGUAGE_PACK__ before js_bundle(entry).
  • superset-frontend/packages/superset-core/src/translation/TranslatorSingleton.tsautoConfigureFromWindow() called from getInstance() to self-configure from the global.
  • superset-frontend/src/preamble.ts — prefer the injected pack; keep async fetch as fallback.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Tested on a live deployment with mi (Māori) locale. Before: navbar translated, chart context menu items ("Drill to detail", etc.) and many other module-scope strings stayed English. After: every string with a msgstr renders translated on first load.

TESTING INSTRUCTIONS

  1. Build with BUILD_TRANSLATIONS=true so messages.json files exist on disk.
  2. Log in, switch locale to any non-English language that has translations (e.g. pt_BR, ru, mi).
  3. Open a dashboard, right-click on a chart. Context menu items ("Drill to detail", "Drill to detail by") should render translated.
  4. Check browser console: JSON.parse(document.getElementById('app').dataset.bootstrap).common.language_pack should return the Jed dict, and window.__SUPERSET_LANGUAGE_PACK__ should be the same object.
  5. Switch back to English — language_pack should be null and the async fetch path should not run.

ADDITIONAL INFORMATION

@codecov

codecov Bot commented Apr 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 75.60976% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.71%. Comparing base (a8ab5f9) to head (fdad6e9).

Files with missing lines Patch % Lines
superset-frontend/src/preamble.ts 58.33% 10 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #39357      +/-   ##
==========================================
+ Coverage   64.70%   64.71%   +0.01%     
==========================================
  Files        2686     2686              
  Lines      148625   148651      +26     
  Branches    34298    34309      +11     
==========================================
+ Hits        96164    96206      +42     
+ Misses      50696    50680      -16     
  Partials     1765     1765              
Flag Coverage Δ
hive 39.19% <75.00%> (+0.01%) ⬆️
javascript 69.57% <69.69%> (+<0.01%) ⬆️
mysql 57.85% <100.00%> (+0.03%) ⬆️
postgres 57.91% <100.00%> (+0.03%) ⬆️
presto 40.73% <75.00%> (+0.01%) ⬆️
python 59.30% <100.00%> (+0.03%) ⬆️
sqlite 57.49% <100.00%> (+0.03%) ⬆️
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@alex-poor alex-poor force-pushed the fix/i18n-bootstrap-injection branch 4 times, most recently from d3a4c45 to 254ec95 Compare April 15, 2026 01:37
@alex-poor alex-poor marked this pull request as ready for review April 15, 2026 02:14
@dosubot dosubot Bot added change:frontend Requires changing the frontend i18n:general Related to translations labels Apr 15, 2026
@bito-code-review

bito-code-review Bot commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #e94e97

Actionable Suggestions - 0
Additional Suggestions - 1
  • superset/views/base.py - 1
    • Security - Path Traversal · Line 450-457
      The language parameter is used directly in os.path.join to construct the file path for loading translation packs, which could allow directory traversal if the language string contains path components like '..'. Since locale.language can originate from user-influenced sources like Accept-Language headers or configuration, this poses a security risk for reading arbitrary files. Validate the language string to match expected locale formats before using it in the path.
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • tests/unit_tests/views/test_bootstrap_auth.py - 1
Review Details
  • Files reviewed - 6 · Commit Range: 254ec95..254ec95
    • superset-frontend/packages/superset-core/src/translation/TranslatorSingleton.test.ts
    • superset-frontend/packages/superset-core/src/translation/TranslatorSingleton.ts
    • superset-frontend/src/preamble.ts
    • superset/templates/superset/spa.html
    • superset/views/base.py
    • tests/unit_tests/views/test_bootstrap_auth.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@sadpandajoe sadpandajoe added 🎪 ⚡ showtime-trigger Trigger showtime deployment 🎪 ⚡ showtime-trigger-start Create new ephemeral environment for this PR and removed 🎪 ⚡ showtime-trigger Trigger showtime deployment labels Apr 15, 2026
@github-actions github-actions Bot added 🎪 254ec95 🚦 building 🎪 ⌛ 48h Environment expires after 48 hours (default) and removed 🎪 ⚡ showtime-trigger-start Create new ephemeral environment for this PR labels Apr 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🎪 Showtime is building environment on GHA for 254ec95

@github-actions

Copy link
Copy Markdown
Contributor

🎪 Showtime deployed environment on GHA for 254ec95

Environment: http://34.215.177.143:8080 (admin/admin)
Lifetime: 48h auto-cleanup
Updates: New commits create fresh environments automatically

Comment thread superset/views/base.py Outdated

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

Note

Copilot was unable to run its full agentic suite in this review.

This PR addresses an i18n race where module-scope t('...') calls in lazily loaded/code-split chunks can evaluate before the async language-pack fetch completes, causing some strings to remain in English.

Changes:

  • Adds server-side loading of Jed messages.json into the bootstrap payload as common.language_pack.
  • Exposes the pack early via an inline script in spa.html (window.__SUPERSET_LANGUAGE_PACK__) before the entry bundle loads.
  • Updates the frontend translator singleton to self-configure from the global pack and updates preamble.ts to prefer the injected pack with async fetch as fallback.

Reviewed changes

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

Show a summary per file
File Description
tests/unit_tests/views/test_bootstrap_auth.py Adds unit coverage for _load_language_pack and asserts language pack presence in bootstrap payload
superset/views/base.py Loads messages.json from disk and injects it into the common bootstrap data
superset/templates/superset/spa.html Adds inline script to stash common.language_pack onto window.__SUPERSET_LANGUAGE_PACK__ before bundle load
superset-frontend/src/preamble.ts Prefers bootstrap/global-injected language pack; keeps async fetch as fallback
superset-frontend/packages/superset-core/src/translation/TranslatorSingleton.ts Auto-configures translator from window.__SUPERSET_LANGUAGE_PACK__ on first access to survive chunk duplication
superset-frontend/packages/superset-core/src/translation/TranslatorSingleton.test.ts Adds tests covering the new window-based auto-configuration behavior

Comment thread tests/unit_tests/views/test_bootstrap_auth.py Outdated
Comment thread superset/views/base.py
Comment thread superset/views/base.py Outdated
@bito-code-review

bito-code-review Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #0125ec

Actionable Suggestions - 0
Review Details
  • Files reviewed - 3 · Commit Range: 254ec95..23581c0
    • superset/views/base.py
    • tests/unit_tests/views/test_bootstrap_auth.py
    • tests/unit_tests/views/test_base.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@alex-poor

Copy link
Copy Markdown
Contributor Author

hi @michael-s-molina @mistercrunch ! I have resolved the copilot review issues and will be grateful for any thoughts or advice on this PR. Thank you.

alex-poor and others added 3 commits June 18, 2026 22:44
…t chunks

Module-level `const X = t('...')` calls in code-split chunks evaluate
before the async `/superset/language_pack/<lang>/` fetch in preamble.ts
can resolve, capturing the English msgid forever. apache#36893 fixed the
initial-render race by awaiting initPreamble before ReactDOM.render,
but chunks like DashboardContainer (ChartContextMenu ->
useDrillDetailMenuItems) still evaluate later and hit an unconfigured
translator. Result: strings inside component render bodies translate,
but strings captured at module scope stay English, even though
bootstrapData.common.locale is set and the language pack endpoint
returns a valid pack. Reported for pt_BR, ru, zh and others in apache#35330.

Fix: inject the full Jed language pack into the HTML bootstrap payload
under `common.language_pack`. An inline <script> in spa.html — which
runs before the entry bundle loads — stashes the pack on
`window.__SUPERSET_LANGUAGE_PACK__`. TranslatorSingleton lazily
self-configures from that global on first `getInstance()`, so every
chunk-local copy webpack splitChunks may produce ends up configured
identically. preamble.ts now prefers the bootstrap-injected pack and
keeps the async fetch as a fallback for entries that don't extend
spa.html (e.g. embedded).

Net effect: `t()` returns the translated msgstr on first evaluation
everywhere, including module-level constants in lazy chunks.

Fixes apache#35330
…emoize

Address review feedback on the bootstrap language-pack injection:

* Drop the duplicate `_load_language_pack` reader in views/base.py and
  reuse `superset.translations.utils.get_language_pack`, which already
  has a process-level dict cache keyed by locale and is the existing
  loader used by the async `/superset/language_pack/<lang>/` endpoint.

* Move the pack injection out of `cached_common_bootstrap_data` (which
  is memoized per `(user_id, locale)`) and into `common_bootstrap_payload`
  so the cached entry stays small and the pack itself is shared across
  users for the same locale instead of being duplicated in cache.

* Treat an empty/falsy pack as "no pack" so locales that miss the JSON
  file (the shared utility falls back to an empty English pack) hand
  the frontend `null` and the existing async-fetch fallback fires,
  matching prior behavior.

* Replace the `_load_language_pack` unit tests (function removed) with
  tests against `common_bootstrap_payload`. The new tests don't depend
  on the per-user memoize, removing a flakiness path where a prior test
  populating `(user_id=1, locale=None)` would skip the under-test code.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…payload

`common_bootstrap_payload` now copies the cached dict and adds a
`language_pack` key so the pack isn't duplicated inside the per-user
memoize. Update the existing locale-stringification test to assert on
the relevant fields rather than the full dict, since the wrapper
intentionally returns a superset of the cached payload.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@rusackas rusackas force-pushed the fix/i18n-bootstrap-injection branch from 23581c0 to e553319 Compare June 19, 2026 05:46

@rusackas rusackas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Rebased this onto current master and pushed for you, there were real conflicts to work through: master moved the pre-configure warning into warnPreConfigure (so I had to wire autoConfigureFromWindow() into t()/tn() ahead of the warn check, otherwise it warns even when the window pack is present), and a _get_frontend_config_value helper landed right next to your new code. Translation + bootstrap tests pass locally.

The approach itself looks good to me, synchronous availability before any chunk evaluates is the right fix. A few small inline notes below, none of them blockers.

Comment thread superset/views/base.py
Comment thread superset-frontend/src/preamble.ts
Comment thread superset/templates/superset/spa.html
alex-poor and others added 2 commits June 19, 2026 19:15
get_language_pack returns the empty English Jed pack on a miss (never
falsy), so the trailing 'or None' was dead code. It also clobbered a
language_pack already set via COMMON_BOOTSTRAP_OVERRIDES_FUNC (the
documented apache#35330 workaround). Prefer an existing pack, then fall back to
the shared loader.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bito-code-review

bito-code-review Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #75adde

Actionable Suggestions - 0
Additional Suggestions - 1
  • superset/templates/superset/spa.html - 1
    • Error swallowing without logging · Line 166-166
      The catch block at line 173 silently swallows all exceptions without any logging. While the fallback to async fetch is correct, silently swallowing errors can mask unexpected failures during production debugging. Consider adding minimal logging (e.g., `console.warn('Failed to parse language pack from bootstrap:', e)`).
Review Details
  • Files reviewed - 7 · Commit Range: b365c2b..a719a46
    • superset-frontend/packages/superset-core/src/translation/TranslatorSingleton.test.ts
    • superset-frontend/packages/superset-core/src/translation/TranslatorSingleton.ts
    • superset-frontend/src/preamble.ts
    • superset/templates/superset/spa.html
    • superset/views/base.py
    • tests/unit_tests/views/test_base.py
    • tests/unit_tests/views/test_bootstrap_auth.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@rusackas

rusackas commented Jul 5, 2026

Copy link
Copy Markdown
Member

Everything from my pass is addressed, so LGTM. It's gone conflicting with master since, though. After another rebase, hopefully we can merge.

@rusackas rusackas added the merge-if-green If approved and tests are green, please go ahead and merge it for me label Jul 5, 2026
Resolves the preamble.ts conflict: master renamed the language pack
endpoint from /superset/language_pack/<lang>/ to /language_pack/<lang>/
(route move); keep the bootstrap-pack-first structure from this branch
and adopt the new URL in the async-fetch fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@rusackas

rusackas commented Jul 5, 2026

Copy link
Copy Markdown
Member

Rebased (merged current master) to clear the conflict... master moved the language pack endpoint to /language_pack/<lang>/, so the async-fetch fallback here now uses the new URL. Translator, preamble, and bootstrap view tests all pass locally.

I also dug into the payload question since inlining the pack isn't free. Measured with the French catalog: ~237KB of Jed JSON, which is ~67KB gzipped inside the HTML. That cost only applies to non-English locales (en injects null, so default deployments pay nothing), and only on full page loads, which are rare in an SPA. On a cold cache this is actually faster than master: the fetch approach serializes HTML → JS bundle → language pack round trip before first render, and the inline pack removes that whole hop. On warm loads it's ~67KB of extra HTML where the fetch would have been free from browser cache.

One more point in its favor: send_file serves the pack with our 1-year SEND_FILE_MAX_AGE_DEFAULT on an unversioned URL, so today's cached packs can go stale for a year after an upgrade... the inline pack always matches the server version. If we ever want the best of both worlds, a versioned static pack loaded via a synchronous script tag before the entry bundle would be cacheable AND race-free, but that's a build-pipeline project, not this PR.

LGTM once CI comes back green. 🤞

Comment thread superset/views/base.py
Comment thread superset/views/base.py
Comment thread superset/views/base.py
Comment thread tests/unit_tests/views/test_bootstrap_auth.py
@netlify

netlify Bot commented Jul 5, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit fdad6e9
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a4b255e3de9b80008976d67
😎 Deploy Preview https://deploy-preview-39357--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

TranslatorSingleton.ts declares the typed Window.__SUPERSET_LANGUAGE_PACK__
global, so the test file can use the property directly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@bito-code-review

bito-code-review Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #17330c

Actionable Suggestions - 0
Review Details
  • Files reviewed - 1 · Commit Range: a719a46..9eb5350
    • superset-frontend/packages/superset-core/src/translation/TranslatorSingleton.test.ts
  • Files skipped - 0
  • Tools
    • Eslint (Linter) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment thread superset/views/base.py
@rusackas rusackas merged commit 8382683 into apache:master Jul 6, 2026
66 checks passed
rusackas added a commit that referenced this pull request Jul 6, 2026
…ripts

Follow-up to #39357, which inlined the Jed language pack into the
bootstrap HTML to fix the code-split translation race (#35330). That
made non-English full-page loads carry ~67KB (gzipped) of pack on every
request since HTML is uncacheable.

This keeps the synchronous-configuration guarantee but moves the pack
to a content-addressed script: spa.html emits
<script src="/language_pack/<lang>/<version>/script.js"> before the
entry bundle, where <version> is a short content hash of the pack file.
The endpoint serves window.__SUPERSET_LANGUAGE_PACK__ = {...} with
Cache-Control: public, max-age=31536000, immutable, so browsers fetch
the pack once per translation change instead of once per page load, and
cache-bust automatically on upgrade (fixing the stale-pack window the
unversioned /language_pack/<lang>/ fetch had under the 1-year
SEND_FILE_MAX_AGE_DEFAULT).

Details:
- English emits no tag and pays nothing.
- A stale version in a cached HTML page still gets current content,
  served no-cache so it can't pin under the wrong address.
- Packs provided via COMMON_BOOTSTRAP_OVERRIDES_FUNC still ride the
  bootstrap payload and suppress the script tag (spa.html stashes them
  on window instead), preserving the historical workaround.
- The endpoint is deliberately unauthenticated: catalogs are static
  public repo content with no user data, and must load for anonymous
  principals (login page, embedded).
- preamble.ts fallback chain unchanged: bootstrap pack, window global,
  then async fetch if the script failed to load.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@bito-code-review

Copy link
Copy Markdown
Contributor

Bito Automatic Review Skipped – PR Already Merged

Bito scheduled an automatic review for this pull request, but the review was skipped because this PR was merged before the review could be run.
No action is needed if you didn't intend to review it. To get a review, you can type /review in a comment and save it

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

Labels

change:frontend Requires changing the frontend i18n:general Related to translations merge-if-green If approved and tests are green, please go ahead and merge it for me packages size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[6.0.0rc1] Partial Translations on pt_BR

4 participants