Skip to content

fix(inflekt): singularize coined -is/-us plurals (apis -> api) - #112

Merged
pyramation merged 1 commit into
mainfrom
fix/inflekt-is-us-singularization
Aug 5, 2026
Merged

fix(inflekt): singularize coined -is/-us plurals (apis -> api)#112
pyramation merged 1 commit into
mainfrom
fix/inflekt-is-us-singularization

Conversation

@pyramation

Copy link
Copy Markdown
Contributor

Summary

singularize treated every -is/-us word as already singular, so a coined plural of a vowel-final stem never lost its s and pluralizing it again doubled up — apisapises, which is how it surfaced (a getGraphQLQueryName('apis') in constructive returned apises).

-const ALREADY_SINGULAR_REGEX = /(?:is|us)$/i;   // "no English plural ends in is/us"
+const ADJECTIVE_OUS_REGEX = /ous$/i;            // anxious, various — no noun stem ends in "ou"

The premise was wrong, and no narrower regex fixes it: apis and iris have the same shape (a vowel plus s), as do menus and radius. Which one a word is, is a dictionary fact, so the real singulars now come from SINGULAR_EXCEPTIONS as self-mappings (status: 'status', iris: 'iris', radius: 'radius', 166 of them, all dictionary-proven by scripts/generate-exceptions.ts), and words no dictionary knows fall through to the generic "drop the s" — which is what identifiers want: apisapi, cpuscpu, urisuri.

Those new self-mappings brought their plurals under the table's existing round-trip test and exposed a gap it had never covered (aegisaegisesaegise, since -ses is only -sis for the Greek nouns), so the generator gained a closure pass for singulars that themselves end in s. Net table change: 747 → 743 entries, with the 212 removed entries (alibis: 'alibi', antis: 'anti', …) now handled by the generic rule instead of being listed.

pluralize is untouched and byte-identical to 0.7.2 on every word checked; only singularize changes, and only for -is/-us.

Testing

  • pnpm test in packages/inflekt: 107 passed, including the new -is/-us cases (coined plurals, dictionary singulars left alone, dictionary vowel-stem plurals still depluralized).
  • scripts/generate-exceptions.ts is idempotent: rerunning reproduces the committed table.
  • pnpm build and pnpm lint clean across the monorepo; pnpm -r test shows no failures caused by this (schema-ts-cli/yamlize-cli fail on "no tests found", which is unrelated and pre-existing).

Link to Devin session: https://app.devin.ai/sessions/c2c27132839a45219a440fd10cbf13a6
Requested by: @pyramation

Every word ending in -is/-us was assumed already singular, so an "apis"
table round-tripped to "apises". No suffix distinguishes the two cases
("apis" and "iris" are both a vowel plus s), so the real singulars now
come from the dictionary-generated exception table instead of a blanket
rule, and unknown words fall through to "drop the s".
@pyramation pyramation self-assigned this Aug 5, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@pyramation
pyramation merged commit d4a5cbd into main Aug 5, 2026
59 checks passed
devin-ai-integration Bot pushed a commit to constructive-io/constructive that referenced this pull request Aug 5, 2026
0.7.2 read every -is/-us word as already singular, so an "apis" table
became "apises" in getGraphQLQueryName and in the generated schema.
constructive-io/dev-utils#112.
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.

1 participant