i18n: alert/report type is interpolated untranslated into localized toast messages
- Dominant language
- Python
- Stars
- 74.8k
- Forks
- 18.3k
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 685
Description
### Bug description
The two toast messages fired when a report or alert is triggered manually interpolate the schedule's type into an otherwise translated sentence, but the type itself is never translated. In any non-English locale the result is an English noun sitting in the middle of a localized sentence.
In `superset-frontend/src/pages/AlertReportList/index.tsx`:
```js
// line 197
t('%(alertType)s "%(alertName)s" triggered successfully', {
alertType: alert.type,
alertName: alert.name,
})
// line 205
t('Failed to trigger %(alertType)s "%(alertName)s": %(error)s', {
alertType: alert.type,
alertName: alert.name,
error,
})
```
`alert.type` is the raw model value. It is backed by `ReportScheduleType` in `superset/reports/models.py`:
```python
class ReportScheduleType(StrEnum):
ALERT = "Alert"
REPORT = "Report"
```
so it is always the literal `"Alert"` or `"Report"`, and it never goes through `t()`.
**Actual result**, French locale, triggering an alert named "Ventes J-1":
> Alert « Ventes J-1 » déclenché avec succès
**Expected result:**
> L'alerte « Ventes J-1 » a été déclenchée avec succès
This affects every locale, not just French. It cannot be fixed in the `.po` catalogues — the untranslated value is injected at runtime, so no wording choice by a translator can reach it.
### Why the obvious fix isn't sufficient
Wrapping the value — `alertType: t(alert.type)` — is necessary but doesn't finish the job. `Alert` and `Report` are already in the catalogues (`fr` renders them "Alerte" and "Rapport"), so the substitution would work, but it then exposes two grammatical problems that the sentence template cannot express:
1. **Agreement.** "Alerte" is feminine and "Rapport" is masculine. The past participle in the success message has to agree with whichever one is substituted — *déclenchée* vs *déclenché* — and a single template string can only carry one of them. The same applies to any language with grammatical gender.
2. **Elision and contraction.** The failure message reads `Échec du déclenchement de %(alertType)s`, which yields "de Alerte" where French requires "de l'alerte". Other languages have the equivalent problem with case endings.
The standard gettext answer is to not compose the sentence from an interpolated noun, and instead emit two complete strings:
```js
alert.type === 'Alert'
? t('Alert "%(name)s" triggered successfully', { name: alert.name })
: t('Report "%(name)s" triggered successfully', { name: alert.name })
```
(`ReportScheduleType` in `superset-frontend/src/features/reports/types.ts` is a
type alias `'Alert' | 'Report'` rather than an enum, so the comparison is against
the literal.)
That gives each locale a whole sentence to translate, and the agreement and elision problems disappear because the translator is writing the sentence rather than assembling it. It costs two extra msgids per message.
I'm happy to open a PR along those lines if the approach looks right — I'd rather check the direction first, since it changes the source strings and invalidates the existing translations for those two entries in every locale.
### How this was found
While reviewing the French catalogue in #42577. [@rusackas](https://github.com/rusackas) flagged the agreement problem on the translated strings; tracing where the placeholder gets its value turned up the untranslated interpolation underneath it.
### Screenshots/recordings
None. See the note below.
### Superset version
`master` / latest
### Python version
Not applicable
### Node version
Not applicable
### Browser
Not applicable
### Additional context
This is a source read, not a runtime reproduction — I don't have a Superset instance running, so the French output quoted above is derived from the catalogue entry and the enum, not observed in a browser. The three facts it rests on are each checkable in the tree: the two `t()` call sites above, the `ReportScheduleType` enum, and the French msgstr `"%(alertType)s « %(alertName)s » déclenché avec succès"`.
No Python stacktrace — this is a display defect, nothing is raised.
### Checklist
- [x] I have searched Superset docs and Slack and didn't find a solution to my problem.
- [x] I have searched the GitHub issue tracker and didn't find a similar bug report.
- [x] I have checked Superset's logs for errors and if I found a relevant Python stacktrace, I included it here as text in the "additional context" section.
Contributor guide
Assessment
This issue has not been assessed yet.