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