fix(l10n): disambiguate duration "m" label from millions abbreviation with msgctxt - #124246
fix(l10n): disambiguate duration "m" label from millions abbreviation with msgctxt#124246parh0m2007 wants to merge 3 commits into
Conversation
… with msgctxt
The single-character msgid "m" was shared by three different meanings:
`small_count()` millions, the extra-short month label, and the
extra-short minute label in `getDuration()`. Since gettext maps one
msgid to one msgstr per locale, Russian durations under a month
rendered as «5млн» ("5 millions") instead of «5м» ("5 min").
Route the extra-short month/minute labels through a new `tp()`
(pgettext) helper with a "duration" translation context, and thread
msgctxt support through the i18n pipeline:
- `locale.tsx`: add `tp` (pgettext wrapper) alongside `t`/`tn`
- `getDuration.tsx`: use `tp('duration', 'm')` for the extra-short
month/minute labels
- `ts-extract-gettext.ts`: register `tp` so contexts are extracted
- `po-catalog-loader.ts`: emit context-specific entries in the
`msgctxt\u0004msgid` key format Jed looks up, instead of dropping
them
- `bin/merge-catalogs`: look up messages with their context so
context-specific msgids stay separate from context-less ones
- en/ru catalogs: add the new `msgctxt "duration"` entry (ru: «м»)
Fixes getsentry#124240
|
🚨 Warning: This pull request contains Frontend and Backend changes! It's discouraged to make changes to Sentry's Frontend and Backend in a single pull request. The Frontend and Backend are not atomically deployed. If the changes are interdependent of each other, they must be separated into two pull requests and be made forward or backwards compatible, such that the Backend or Frontend can be safely deployed independently. Have questions? Please ask in the |
merge_message() assigned from frontend_msg.usr_comments instead of user_comments, which would raise an AttributeError whenever a frontend message carried translator comments. Spotted by Sentry's bug-prediction bot on getsentry#124246.
|
The |
d51a7ae to
e0dc82c
Compare
Problem
The single-character msgid
"m"was shared by three different meanings:small_count()insrc/sentry/templatetags/sentry_helpers.py(_("m"))DURATION_LABELSinstatic/app/utils/duration/getDuration.tsxDURATION_LABELSinstatic/app/utils/duration/getDuration.tsxBecause gettext maps one msgid to one msgstr per locale, the Russian catalog carries the millions translation («млн») for all three. With
unitStyle="extraShort"(TimeSince→getRelativeDate→getDuration), durations under a month rendered as «5млн» ("5 millions") instead of «5м» ("5 min").Fixes #124240
Solution
Route the extra-short month/minute labels through gettext translation contexts (msgctxt), keeping
small_count()'s millions meaning as the context-less"m". Jed already supportspgettextlookups at runtime (msgctxt\u0004msgidkeys), and the extraction script already knows thepgettextsignature — the pipeline pieces just weren't wired up for frontend use.Changes
static/app/locale.tsx: addtp(apgettextwrapper) alongsidet/tn, so ambiguous single-letter msgids can be disambiguated with a context.static/app/utils/duration/getDuration.tsx: the extra-short month/minute labels now usetp('duration', 'm').build-utils/ts-extract-gettext.ts: registertp: ['msgctxt', 'msgid']so context-bearing calls are extracted with theirmsgctxt.build-utils/po-catalog-loader.ts: previously only readtranslations[''], silently dropping all context-specific entries. Now iterates all contexts and emits entries in themsgctxt\u0004msgidkey format Jed looks up.bin/merge-catalogs:catalog.get(frontend_msg.id)matched context-specific msgids against the context-less entry (the source of the collision). Now looks up withcatalog.get(id, context); the context travels with theMessagewhen written back.en/rucatalogs: surgically add the newmsgctxt "duration"entry (ru: «м»; other locales fill in via Transifex). The nextmake merge-locale-catalogs+ Transifex sync will refresh references across the remaining locales.small_count()and its «млн» translation are untouched.Verification
pnpm run typecheck— cleanpnpm run lint:js(oxlint) on changed files — 0 errorsgetDuration(122, 0, false, true)→'2м'and months →'3м'(not «млн»)pnpm run build-js-poextracts themsgctxt "duration"entry;bin/merge-catalogsverified to keep plain"m"→ «млн» and create the separate context entry;po-catalog-loaderoutput verified to resolve viaJed.pgettext('duration', 'm')