Chrome’s extension message format has no plural support. There is no plural field, no ICU MessageFormat, no select. You get a string and some $placeholders$.

So every extension does the same thing, and Latr did too:

count === 1 ? t('reminderOne') : t('reminderOther')

That is correct English. It is correct German, Dutch, French and Portuguese too, near enough. It is wrong in Russian and Ukrainian, and it is wrong often - a single non-singular form misdeclines roughly a third of all counts.

What the two-form assumption misses

Russian has three plural categories in CLDR terms, not two:

  • one - 1, 21, 31, 41, 101…
  • few - 2, 3, 4, 22, 23, 24…
  • many - 5 through 20, 25 through 30, 0…

So “2 reminders”, “3 reminders” and “4 reminders” take a different form from “5 reminders”, and count !== 1 collapses both into whichever one the translator happened to write. About a third of the numbers a queue actually displays land in the wrong bucket.

Note the second row of that list, because it is the part that surprises people who have only ever thought about this in English: “one” is not “1”. Russian’s one category also covers 21 and 31. Which means a message in the one slot may need to print the number, where the English equivalent hardcodes the word “one” and never shows a digit at all.

If your One string is literally "One reminder", it is broken in Russian at 21.

What we do instead

One function, because there is exactly one place this decision should live:

plural(count, stem, subs)

It asks Intl.PluralRules for the CLDR category in the current UI language, then looks up <stem>One, <stem>Few, <stem>Many or <stem>Other in the catalogue.

Intl.PluralRules is in every browser that can run a Manifest V3 extension, it knows the rules for every locale anyone will ever ship, and it is the same data CLDR gives every other platform. There is no reason to encode plural rules by hand and several reasons not to.

Only the categories a locale actually uses need to exist in that locale’s catalogue. English needs One and Other. Russian needs One, Few and Many. Other is the fallback and every locale has one, so a missing category degrades to something grammatical rather than to a blank.

The part that is not about plurals

This bug was invisible for as long as the product shipped in English only, and it stayed invisible after eight more languages were added, because the person reading the screen was reading English.

That is the same shape as another bug we wrote up - a repeating reminder that showed the English word daily in all nine translations. Both are cases where the test suite was strong and the failure was in a dimension the suite did not have an opinion about.

The suite now holds every locale to English’s shape: no missing key, no key English lacks, placeholders that match exactly. It also parses each locale’s own date-input placeholder with that locale’s parser, because the placeholder is a promise about what the field accepts and a translated example the parser cannot read advertises input that silently fails.

What it cannot do is tell you that your two-branch conditional is a grammar assumption. For that you have to know the grammar exists.

The short version

If you are branching on count === 1 anywhere in a product that ships in more than one language, you have a bug. It is Intl.PluralRules and about fifteen lines to fix, it needs no dependency, and the alternative is being subtly, confidently wrong in front of a third of the numbers you display.