PluralShapeRule class

A count is interpolated into a sentence that has only one grammatical shape, so some value of that count reads wrong: "{count} people" renders "1 people".

The plural_categories rule is the other half of this: it checks a plural is complete once one exists. Nothing checked that a plural should have existed at all, which is how two English strings shipped saying "1 people". This rule fires on the source only, because that is where the defect is born — once the source is a plural expression, translations are governed by plural_categories, and a fix applied to a translation alone would leave the source wrong for every other locale.

The heuristic. A warning, not an error, and knowingly approximate:

  1. Find placeholders that carry a count. A placeholder qualifies by its declared @key.placeholders.<name>.type (int, num, double, number) or, when nothing is declared, by a conventional name (count, n, num, total, qty, or anything ending in Count).
  2. Drop the ones already governed by a plural / selectordinal expression (IcuMessage.pluralSelectors).
  3. Of what remains, find the noun the count governs (_wordAfter) and fire only when that noun is already plural (_looksPlural) — which is precisely the disagreement: a count that can be 1 in front of a word that means many.
  4. For a regular -s candidate, require the placeholder to be in count position (_inCountPosition) — see below.

Why step 4 exists. An English third-person singular verb ends in -s, so _looksPlural cannot tell "opens" from "people" by shape alone, and "Goal {goal} opens now" read as "1 opens". The rule shipped asserting that its misfires were "a verb, an adjective, or a unit … all singular in form"; that was true of the calibration corpus, which happened to contain no Label {n} verb-s string, and false of English. The disambiguator has to be the text before the placeholder, because "steps" is a plural noun in "{count} steps" and a verb in "Goal {goal} steps" — the word itself carries no answer.

English counts forward: a number that counts either opens its phrase or follows a function word ("and {n} others", "in {n} albums"), while a number that follows a bare noun labels that noun ("Goal 3", "step 7") and the -s word after it is the sentence's verb. Irregular plurals skip the gate: "people" and "children" are never verbs, and that is the case the rule was written for.

Erring quiet here is deliberate. A missed warning costs a re-read; a false one blocks a --strict push over correct copy.

Saying it outright. type: int covers both "how many" and "which one". An author can settle it declaratively with "role": "identifier" (or "ordinal") on the placeholder, which is a fact about the variable rather than a waiver on one wording — it survives a rewrite, where an ack does not. Where the rule still misfires on undeclared copy, the escape hatch is the standard one: dialect check --ack plural_shape:source:<key>, whose fingerprint retires the waiver as soon as the copy is edited.

Inheritance

Constructors

PluralShapeRule()
const

Properties

defaultSeverity → IssueSeverity
Default severity. May be overridden per-issue.
no setteroverride
hashCode → int
The hash code for this object.
no setterinherited
name → String
Stable rule identifier (snake_case). Used by the report formatter to group findings and by users to silence/ack via state file (post-v1.0).
no setteroverride
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited

Methods

noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
run(DialectProject project) → List<Issue>
Inspect project and return findings. May return an empty list. Should NOT throw — turn unexpected conditions into Issues so the report surfaces them rather than crashing.
override
toString() → String
A string representation of this object.
inherited

Operators

operator ==(Object other) → bool
The equality operator.
inherited