Skip to content

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

Open
alex-poor wants to merge 5 commits into
apache:masterfrom
alex-poor:fix/i18n-bootstrap-injection
Open

fix(i18n): inject language pack into bootstrap to translate code-split chunks#39357
alex-poor wants to merge 5 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.37%. Comparing base (79cfe4d) to head (a719a46).

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.36%   64.37%   +0.01%     
==========================================
  Files        2651     2651              
  Lines      144812   144838      +26     
  Branches    33417    33428      +11     
==========================================
+ Hits        93208    93245      +37     
+ Misses      49935    49922      -13     
- Partials     1669     1671       +2     
Flag Coverage Δ
hive 39.33% <75.00%> (+0.01%) ⬆️
javascript 68.55% <69.69%> (+<0.01%) ⬆️
mysql 58.08% <100.00%> (+0.03%) ⬆️
postgres 58.15% <100.00%> (+0.03%) ⬆️
presto 40.92% <75.00%> (+0.01%) ⬆️
python 59.60% <100.00%> (+0.03%) ⬆️
sqlite 57.81% <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 Environment 254ec95 status: building 🎪 254ec95 📅 2026-04-15T09-53 Environment 254ec95 created at 2026-04-15T09-53 🎪 254ec95 🤡 sadpandajoe Environment 254ec95 requested by sadpandajoe 🎪 ⌛ 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 github-actions Bot added 🎪 254ec95 🚦 deploying Environment 254ec95 status: deploying 🎪 254ec95 🚦 running Environment 254ec95 status: running 🎪 🎯 254ec95 Active environment pointer - 254ec95 is receiving traffic 🎪 254ec95 🌐 34.215.177.143:8080 Environment 254ec95 URL: http://34.215.177.143:8080 (click to visit) and removed 🎪 254ec95 🚦 building Environment 254ec95 status: building 🎪 254ec95 🚦 deploying Environment 254ec95 status: deploying 🎪 254ec95 🚦 running Environment 254ec95 status: running 🎪 🎯 254ec95 Active environment pointer - 254ec95 is receiving traffic labels Apr 15, 2026
@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

@github-actions github-actions Bot removed 🎪 ⚡ showtime-trigger-stop 🎪 254ec95 🚦 running Environment 254ec95 status: running 🎪 254ec95 📅 2026-04-15T09-53 Environment 254ec95 created at 2026-04-15T09-53 🎪 ⌛ 48h Environment expires after 48 hours (default) 🎪 254ec95 🌐 34.215.177.143:8080 Environment 254ec95 URL: http://34.215.177.143:8080 (click to visit) 🎪 254ec95 🤡 sadpandajoe Environment 254ec95 requested by sadpandajoe labels Apr 15, 2026
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
dayjs.locale('en');
} finally {
window.clearTimeout(timeoutId);
} else {

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.

Worth flagging as a known limitation: embedded doesn't extend spa.html, so it never gets the injected pack and lands here on the async fetch, which still has the same code-split chunk race this PR is fixing. So embedded dashboards would stay partially translated. Fine to leave for a follow-up, just might be worth a note so it's not assumed covered.

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.

As far as I can tell embedded is actually covered (I think!).

  • EmbeddedView.embedded renders superset/spa.html with entry="embedded" and calls common_bootstrap_payload() (embedded/view.py#L97-L109), so the injected common.language_pack and the inline <script> that sets window.__SUPERSET_LANGUAGE_PACK__ (before js_bundle(entry)) both apply.
  • The embedded webpack entry is addPreamble('src/embedded/index.tsx'), which prepends preamble.ts — so the preferred injected-pack path here runs for embedded too, and TranslatorSingleton.autoConfigureFromWindow() self-configures from the global on the first t()/tn() regardless.
  • spa.html is the only template carrying data-bootstrap/js_bundle(entry), so every entry goes through this injection.

whether an embedded load is actually translated still depends on the resolved locale (?lang=/session/Accept-Language) being non-English — if it resolves to en, the pack is null and there's nothing to inject, same as the main app.

Let me know if you're seeing it hit the async fetch path in practice, but as far as I can trace it embedded shouldn't.

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

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 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