Hey all,

Kicking off a new vote on an old SIP, that’s been largely implemented. Happy to 
see it moving again, and would be stoked to see it merged as Superset is 
*almost* fully translated nowadays! Let’s dive in...

SIP-161 ("Translating Superset asset metadata"): 
https://github.com/apache/superset/issues/32854

Quick background:
This SIP got extensive [DISCUSS] treatment on this list and in the issue back 
in April 2025, and Max and I converged with the community on a minimal-core 
direction. Namely, a deployment-configurable `TRANSLATION_HOOK` as the only 
integration point, with Superset core never storing translations itself.

The original proposer had to step away for funding reasons before we closed the 
loop with a vote, and the issue sat until @alex-poor (GH handle) picked it back 
up with a PR that implements this converged direction:

https://github.com/apache/superset/pull/40679

*************** DETAILS ***************


What new things can be translated?

• Chart names and dashboard titles, automatically — no author action needed.
• Home-page "Recents" item titles.
• Anything an author explicitly wraps in a new `{{ i18n('...') }}` Jinja macro 
inside a templated field (axis labels, native filter names, etc.)
    • This is opt-in per field, not automatic.
• Explicitly not covered: the actual chart data/values and localized 
number/date formatting, which are the same exclusions the original SIP had.


How are translations maintained and loaded?

• Superset core stores none of it, that's the design shift from the original 
proposal.
• There's one hook:
    • TRANSLATION_HOOK(text, locale, **context) -> str | None
    • You implement it in config, and back it with whatever you want — a static 
dict, an external MT API, gettext `.po` catalogs, or a database table, whatever
    • A `TRANSLATION_BATCH_HOOK` variant exists for anything where each lookup 
is a query or network call. Superset prefetches through it wherever it already 
holds a full collection (a dashboard's charts, the Recents list). That means a 
30-chart dashboard costs 1 hook call, not 30, and results are memoized. The one 
place that still resolves per-row is the list views, since FAB serializes each 
row independently. The docs tell you to cache your hook if that matters.
    • There's no export/import built into core; that's on whatever you build 
behind the hook.


Effect on our `.pot`/`.po` effort?

• None on the existing pipeline — UI chrome strings are unaffected, nothing new 
gets added to Superset's own translatable-string extraction.
• One of the four reference hooks in the docs *does* show how a deployment 
could reuse the gettext toolchain (Poedit/Transifex/Weblate) for asset content. 
But  that’s in a separate catalog domain (e.g. `assets`) so it can't collide 
with or pollute our existing UI catalogs. Opt-in per deployment, zero impact on 
Superset’s core.


Breaking? Nope… fully additive.

• Feature flag + a multi-language `LANGUAGES` config are both required to 
activate anything, so virtually every existing single-language install is 
untouched.
• New optional config hooks (no-op if undefined), new dump-only REST fields 
(`localized_name`/`localized_title`, nothing removed or renamed)
• One new Jinja macro
• No DB migration.
• Editing a name always shows/saves the canonical value, the edit workflow 
doesn't change.


Summary of what you'd be voting on:


• A `TRANSLATION_HOOK(text, locale, **context) -> str | None` config hook (plus 
an optional batch variant for query/API-backed stores), fully pluggable — 
static map, external MT service, `.po` catalogs, a DB table, whatever a 
deployment wants.
• A `{{ i18n('...') }}` Jinja macro for opt-in translation of templated fields.
• - Gated behind `ENABLE_I18N_ASSET_TRANSLATIONS` + a multi-language 
`LANGUAGES` config, so single-language deployments (the default) pay zero cost.
• No new core dependencies, no DB migration, no translations stored in Superset 
itself. Fully additive REST fields, editing always operates on the canonical 
name.
• Automatically covers chart names and dashboard titles; anything else (axis 
labels, native filter names, etc.) is opt-in via the macro. Search/filtering on 
translated text is not addressed. Both are known, named gaps relative to the 
original SIP's ambitions, not oversights.


Worth flagging from prior discussion / alternatives:

• The original motivation explicitly called out *axis and metric labels* as 
things to translate. This PR only auto-covers `slice_name`/`dashboard_title`. 
Everything else needs manual `{{ i18n() }}` wrapping per field, per org, so 
it’s a narrower scope
• "Search/filter by translated text" was an open question in the original SIP 
and remains unaddressed — list search still matches canonical text only.
• The SIP's "markup vs. auto-wrap" contention is resolved as a hybrid option 
(auto for the two title fields, manual macro for everything else)
• It was doubted that batch retrieval was feasible *through Jinja* but this PR 
sidesteps that by prefetching outside the macro (dashboard-charts endpoint, 
Recents), while the macro itself still resolves one string per call site, 
consistent with rather than disproving the doubt.


The PR itself has already had thorough review already — worth an additional 
pass if you want the engineering details

*************** /END WALL OF DETAILS ***************


Please vote:

 [ ] +1 approve
 [ ] +0 no opinion
 [ ] -1 disapprove (please say why)

Vote will stay open for at least 72 hours.

Evan Rusackas
Preset, Inc
PMC, Apache Superset
Member, ASF

Reply via email to