Skip to content

fix(i18n): restore extraction of plural strings with i18next v24+ - #964

Merged
KaiVandivier merged 2 commits into
masterfrom
fix/i18n-plural-extraction
Oct 12, 2026
Merged

KaiVandivier merged 2 commits into
masterfrom
fix/i18n-plural-extraction

Conversation

@HendrikThePendric

@HendrikThePendric HendrikThePendric commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

d2-app-scripts i18n extract silently drops every i18n.t() call that passes count. Those strings never reach en.pot or Transifex, and at runtime English always shows the singular form. There is no error or warning.

This is a regression

Plural extraction was fixed in July 2024 in #861 (LIBS-641) by moving to i18next-scanner v3, verified manually at the time. It broke again without any change in this repo:

  • i18next-scanner v3 depends on "i18next": "*" and builds its plural suffixes from the rules of whichever i18next it resolves. This only works with compatibilityJSON: 'v3'.
  • i18next v24.0.0 (22 November 2024) removed compatibilityJSON: 'v3'. Its plural rule is now a plain Intl.PluralRules with no .numbers, so the scanner computes pluralSuffixes.en = [] and writes no key at all for strings with count.
  • Any app whose lockfile resolved i18next@* after that date gets v24+ and is affected. Apps with an older lockfile entry keep working, which is why this looks intermittent.

Lockfile bump

No automated test covered plural extraction (#861 only changed dependencies, and the existing extract tests mock the scanner). Even with one, it would have passed here: our yarn.lock still resolved i18next@* to 21.8.10. This PR adds that test and updates the lockfile entry to 26.4.2, the version a fresh install of an app gets today, so the test runs against the same i18next that apps do. Only the scanner uses this entry; the runtime (@dhis2/d2-i18n) has its own i18next@^10.3 entry.

The fix

After creating the parser in cli/src/lib/i18n/extract.js, set the English suffixes by hand:

parser.pluralSuffixes.en = ['', '_plural']

These are exactly the values scanner v3 computed for English before i18next v24, so the output is the same as before the regression:

msgid "And {{count}} other"
msgid_plural "And {{count}} other"
msgstr[0] "And {{count}} other"
msgstr[1] "And {{count}} others"

Only en is scanned. Other languages get their plural forms from the Plural-Forms header of their .po files during i18n generate, so they are not affected.

Alternatives considered

  • Upgrade to i18next-scanner v4. It extracts plurals in the newer i18next format (_one/_other), which the runtime in apps (@dhis2/d2-i18n, on i18next 10) cannot read. Every app would need changes, plus a migration of existing plural translations. Out of scope for a regression fix.
  • Pin i18next in cli's dependencies. Package managers don't make the scanner use that copy: with yarn 1, a fresh install still gives the scanner the latest i18next, so it would keep breaking. Forcing it would take resolutions/overrides in every app. The problem is here, in a repo we don't own...

Testing

  • New test in cli/src/lib/i18n/extract.test.js runs the real scanner and i18nextToPot on i18n.t('And {{count}} other', { count: n, defaultValue_plural: 'And {{count}} others' }) and checks for a msgid_plural entry with the plural text in msgstr[1]. With i18next 26 it fails without the fix.
  • The existing extract tests mock the Parser, so that mock now includes a pluralSuffixes field.
  • yarn test in cli: 44/44 pass on Node 22 and on Node 18 (the version CI uses).

Apps affected by this need to run i18n extract again after upgrading, so the missing plural strings reach en.pot and Transifex.

🤖 Generated with Claude Code

HendrikThePendric and others added 2 commits October 6, 2026 17:20
i18next-scanner v3 depends on `"i18next": "*"`, and our lockfile still
resolved that to 21.8.10 from 2022. Published packages ship without
their lockfile, so apps resolve `*` themselves and a fresh install gets
the latest major. Since i18next v24 (22 November 2024) that breaks the
scanner's plural handling, which this repo could not see.

Resolve it to what a fresh install gets today, so tests and CI run
against the same i18next that apps do. Only the scanner uses this entry:
the runtime (@dhis2/d2-i18n) has its own `i18next@^10.3` entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
i18next-scanner v3 derives its plural suffixes from whichever i18next it
resolves (it depends on `i18next: *`). i18next v24 removed
`compatibilityJSON: 'v3'`, so the plural rule no longer exposes
`.numbers`, the scanner computes no suffixes for `en`, and every
i18n.t() call that passes `count` was silently left out of en.pot.

Set the English suffixes to ['', '_plural'] after creating the parser,
the same values scanner v3 computed before i18next v24. Other languages
take their plural forms from the .po Plural-Forms header in generate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@HendrikThePendric
HendrikThePendric force-pushed the fix/i18n-plural-extraction branch from 440bd49 to 72f26e2 Compare October 6, 2026 15:24
@sonarqubecloud

sonarqubecloud Bot commented Oct 6, 2026

Copy link
Copy Markdown

@kabaros kabaros left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested on the data-entry app - Before the fix, the plural translation resource didn't end up in the pot file. And they do afterwards.

@KaiVandivier
KaiVandivier merged commit 1b60da5 into master Oct 12, 2026
20 checks passed
@KaiVandivier
KaiVandivier deleted the fix/i18n-plural-extraction branch October 12, 2026 12:39
dhis2-bot added a commit that referenced this pull request Oct 12, 2026
## [12.11.7](v12.11.6...v12.11.7) (2026-10-12)

### Bug Fixes

* **i18n:** restore extraction of plural strings with i18next v24+ ([#964](#964)) ([1b60da5](1b60da5))
@dhis2-bot

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 12.11.7 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants