Skip to content

Add support for gettext plural forms (msgid_plural / msgstr[N])#3

Merged
himdel merged 1 commit into
himdel:mainfrom
davidpoblador:support-gettext-plural-forms
Mar 1, 2026
Merged

Add support for gettext plural forms (msgid_plural / msgstr[N])#3
himdel merged 1 commit into
himdel:mainfrom
davidpoblador:support-gettext-plural-forms

Conversation

@davidpoblador

Copy link
Copy Markdown
Contributor

The parser currently doesn't handle standard gettext plural forms. When a .po file contains msgid_plural / msgstr[0] / msgstr[1], the parser treats them as unexpected input, gets out of sync, and reports false positives on subsequent entries.

This adds two new states to the parser state machine:

  • State 3: after msgid_plural, expects msgstr[0] or multiline continuation
  • State 4: after msgstr[N], expects blank line, msgstr[N+1], or multiline continuation

Placeholder validation checks each msgstr[N] against the combined set from msgid and msgid_plural.

Tested with:

  • Simple plural entries (2 forms)
  • Languages with 3+ plural forms (e.g. Polish)
  • Multiline continuations on all plural directives
  • Mixed files with both regular and plural entries
  • Plural entries at EOF without trailing blank line
  • Missing placeholders in individual msgstr[N] (correctly caught)

The parser now handles standard gettext plural entries with
msgid_plural and indexed msgstr[N] forms, including languages
with 3+ plural forms and multiline continuations.

Placeholder validation checks each msgstr[N] against the
combined set of placeholders from msgid and msgid_plural.
davidpoblador added a commit to alltuner/vibetuner that referenced this pull request Feb 26, 2026
The upstream lint-po package doesn't handle msgid_plural / msgstr[N],
causing false positives on any .po file with plural entries.

Points to a fork with the fix until the upstream PR lands:
himdel/lint-po#3
davidpoblador added a commit to alltuner/vibetuner that referenced this pull request Feb 26, 2026
The upstream `lint-po` package doesn't handle `msgid_plural` /
`msgstr[N]`, causing false positives on any `.po` file with plural
entries.

This points the pre-commit hook and justfile to a fork with the fix
until the upstream PR lands: himdel/lint-po#3

Closes #1312
@himdel himdel merged commit 00edd41 into himdel:main Mar 1, 2026
@himdel

himdel commented Mar 1, 2026

Copy link
Copy Markdown
Owner

Looks good to me, thanks! :)

Would we need to also add support for ignoring missing placeholders? I think omitting {count} may be a relatively common case for the n=0 and n=1 plural variants?

@davidpoblador

Copy link
Copy Markdown
Contributor Author

Addressed in #4

davidpoblador added a commit to alltuner/vibetuner that referenced this pull request Mar 2, 2026
The gettext plural form support feature has landed upstream
(himdel/lint-po#3), so we no longer need to depend on our fork.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
davidpoblador added a commit to alltuner/vibetuner that referenced this pull request Mar 2, 2026
## Summary
- Switch `lint-po` dependency from our fork
(`davidpoblador/lint-po@support-gettext-plural-forms`) back to upstream
(`himdel/lint-po`)
- The gettext plural form support feature has landed upstream:
himdel/lint-po#3
- Still using `git+` install since it hasn't been released to PyPI yet

## Test plan
- [ ] Verify `lint-po` runs correctly against `.po` files with plural
forms using the upstream repo

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
davidpoblador added a commit to alltuner/vibetuner that referenced this pull request Apr 28, 2026
## Summary
- Closes #1719 — `lint-po` rejects Babel-generated `msgid_plural` /
`msgstr[N]` entries, blocking `just lint` on any locale that uses `{%
trans count=… %}{% pluralize %}{% endtrans %}`.
- Root cause: PyPI `lint-po` is still 0.1.4 (Nov 2022), which predates
the gettext plural-form support merged upstream as `himdel/lint-po#3`
and `#4` (Mar 2026). Switching to PyPI in #1609 silently regressed the
feature.
- Pin `lint-po` to upstream master at
`efadbcaa50f84135ee4f16efa44d99d2662a74b4` via `[tool.uv.sources]` in
the scaffolded `pyproject.toml`.
- Switch the justfile recipe and pre-commit hook from `uvx lint-po` to
`uv run --frozen lint-po` so resolution happens once at `uv sync` time
instead of hitting GitHub on every invocation — also addresses the
transient-failure motivation behind #1609.
- A release-request issue is open upstream at himdel/lint-po#5; once a
new version ships to PyPI we can drop the `[tool.uv.sources]` pin in
favor of a normal version constraint.

## Test plan
- [x] Reproduced #1719 locally with `uvx lint-po` against a
Babel-generated plural `.po` (exit 1, "Unexpected input" warnings).
- [x] Verified the pinned SHA resolves and installs via `uv lock` + `uv
sync --frozen` in a smoke-test project.
- [x] Verified the same plural fixture passes with `uv run --frozen
lint-po` (exit 0).
- [x] Verified placeholder-mismatch detection still flags a
deliberately-broken `msgstr[1]` in a plural entry (exit 1).
- [ ] Smoke `just lint-po` and pre-commit `lint-po` hook on a real
scaffolded project after merge.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants