Skip to content

Fix: choose plural forms by the language's own rules - #167

Merged
SandObserver merged 1 commit into
mainfrom
fix/locale-plural-rules
Aug 29, 2026
Merged

SandObserver merged 1 commit into
mainfrom
fix/locale-plural-rules

Conversation

@SandObserver

Copy link
Copy Markdown
Owner

Summary

  • Store each counted message once per CLDR plural category (key_one, key_other, ...) instead of a singular key plus a ...Plural twin, and rename their {n} placeholder to {count}.
  • Select the form in t() with Intl.PluralRules against the active locale, resolving active category, active other, English category, English other, then the key.
  • Replace the five count === 1 call sites with t(key, { count }) and delete refusedNoticeKey, whose only job was picking between the two keys.
  • Give badge-logic.js the same category resolution over its injected-translator English fallback map, keeping the module import-free.
  • Extend the locale registry with an English name and a source/machine status, and export SOURCE_LANG and isSupported.
  • Resolve direction for an unregistered tag from Intl.Locale text info, keeping the language-subtag set as the fallback for runtimes that carry none.
  • Add two URL-only development locales that are absent from the registry: en-XA accents, pads and brackets every message; cimode loads no catalog so each string renders as its key.
  • Add gates for plural-category completeness per locale, placeholder parity against English, registry-to-file agreement, per-widget catalog locale and key coverage, and pseudolocalisation leaving placeholders and markup intact.
  • Teach the reachability test to compare base names, so a plural category the source never writes out is still accounted for.
  • Rewrite docs/i18n.md around the registry table, the per-key fallback policy, the counted-message workflow, a glossary, the development locales, and the validation command.

Testing
All green: 1080 frontend tests, the backend suite at 94.35% lines, lint, format, both typechecks, the cache-busting check, and the Docker build. Added 15 tests covering plural categories across the six locales, decimal and zero counts, catalog and placeholder parity, the registry, and the pseudolocale transform.

Checklist

  • Stays within the project constraints: one container, no runtime dependencies, no build step (see CONTRIBUTING.md).
  • Tests added or updated for new logic, and the full suite, lint, and typechecks pass.
  • Docs updated if behavior or configuration changed.
  • CHANGELOG.md updated under Unreleased if the change is user-visible.

Counted messages picked between a singular and a plural key on count === 1.
That is an English rule. French and Persian both put zero in the `one`
category, and Chinese has a single form for every count.

A counted message is now stored once per plural category as `key_<category>`,
and `t()` selects with Intl.PluralRules against the active locale, falling back
to `other` for a category a catalog does not carry. Callers pass a numeric
`count`, which also fills `{count}`.

The locale registry gains an English name and a status, marking the five
non-source catalogs as machine translation that no speaker has checked.
Direction for an unlisted tag now comes from the runtime's locale data rather
than a language-subtag set alone.

Two development locales are reachable by URL and are not in the registry:
`en-XA` accents and pads every message, and `cimode` renders each string as its
key.
@SandObserver SandObserver added the bug Something isn't working label Aug 29, 2026
@SandObserver
SandObserver merged commit 3d35eae into main Aug 29, 2026
4 checks passed
@SandObserver
SandObserver deleted the fix/locale-plural-rules branch August 29, 2026 13:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant