inflect: correct plurals, ordinals and number words in English
Correctly generate plurals, ordinals, indefinite articles; convert numbers to words
At a glance
- What is it?
- inflect is a Python library that pluralizes nouns, conjugates verbs, selects a or an, writes ordinals and spells numbers out as English words, with separate methods for nouns, verbs and adjectives so the same word can behave differently by part of speech. It suits application code that has to write grammatically correct English sentences, and it is English only.
- Who is it for?
- inflect suits a Python application that writes English sentences from counts, for example notification text, report copy or a user interface string, and it is the right tool when a noun and a verb need different treatment for the same count.
- Can I use it commercially?
- Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One word, three behaviours, decided by part of speech
inflect exists because English pluralisation is not a single operation. The library's central design decision is that plural, singular, verb, adjective and article are separate methods rather than one guess. The README lists the method families: plural, plural_noun, plural_verb, plural_adj, singular_noun, no and num for plurality and singularity; compare, compare_nouns, compare_verbs and compare_adjs for number insensitive comparison; a and an for indefinite articles; present_participle, ordinal and number_to_words; join; and inflect, classical, gender. The distinction shows up immediately in behaviour.
>>> p.plural_noun("I", 2)
'we'
>>> p.plural_verb("saw", 1)
'saw'
>>> p.plural_adj("my", 2)
'our'
>>> p.plural_noun("saw", 2)
'saws'The same input string, saw, is a verb in one call and a noun in another, and the library returns saw and saws accordingly. That distinction is not a detail, it is the difference between a correct sentence and an obviously wrong one, and it is why the API asks the caller to say which job it wants. The adjective handling has the same shape, with my becoming our at a count of two. The comparison family works in the same spirit, comparing two words for numeric equivalence rather than string equality, so compare('person', 'person') returns 'eq' while compare('person', 'people') through compare_nouns returns 's:p' for singular against plural.
Turning a count into a whole sentence
The example in the README that ties everything together is a single sentence built from a count.
>>> count=1
>>> print('There', p.plural_verb('was', count), p.number_to_words(count), p.plural_noun('person', count), 'by the door.')
There was one person by the door.With count set to 243 the same four calls produce the sentence with were, the spelled out count and the plural noun, and the README shows that output explicitly. The point of the example is that each piece of the sentence is derived from the same number, so the caller never has to write an if statement on the count. The no method handles the zero case specifically, since a count of zero is not a plural but an absence, and the no output for one error is ' 1 error' while for two it is ' 2 errors'. The num method lets the caller supply a default count when no count is available, returning an empty string for a count of one and the numeral otherwise, which is how you write a sentence that omits the number when there is only one thing. The join method then handles the list case, turning a tuple of words into a comma separated list with an and before the last item, so the classic 'apple, banana, and carrot' and the two item 'apple, banana' both come out right.
number_to_words has more knobs than you would expect
The number to words conversion is where the library shows the depth of its rules, and the options are worth knowing because they are not in most simple pluralisation libraries. The basic form returns a single string, so 38 becomes thirty-eight and 1234 becomes one thousand, two hundred and thirty-four. Passing wantlist=True returns the same content as a list of parts instead, so you can assemble the phrase yourself. The group parameter changes how digits are chunked, and the examples are specific: group=1 gives one, two, three, four, five for 12345, group=2 gives twelve, thirty-four, five, and group=3 gives one twenty-three, forty-five. The andword parameter controls the conjunction, with an empty value giving one thousand, two hundred thirty-four and the value ', plus' giving one thousand, two hundred, plus thirty-four. The zero and one parameters let you substitute a word for a digit, so grouping 5551202 with one='unity' produces five, five, five, unity, two, zero, two, and the decimal parameter handles the fractional part, with decimal='mark' turning 123.456 into one, two, three, mark, four, five, six. Finally there is a threshold, which is the practical one. Above the threshold the numeral is left as a numeral rather than spelled out, so with a threshold of 10 the value 9 is nine, 10 is ten, 11 is '11' and 1000 is '1,000'. That is the behaviour you want in a sentence that mixes small counts with large totals.
Article selection, ordinals and the classical mode
Indefinite articles are handled by a and an, and the call takes the word you are about to use, so a('thing') returns 'a thing' and a('idea') returns 'an idea'. Ordinals are produced by the ordinal method, with the suffixes following the ordinary English pattern of 1st, 2nd, 3rd and then th, so 347 becomes 347th. The interesting combination is passing an ordinal into number_to_words, which gives one thousand, two hundred and thirty-fourth, so the word form of an ordinal works as well as the numeric form. The gender method selects the gender of singular pronouns, and it is a stateful setting on the engine rather than a per call argument, since p.singular_noun('they') returns it by default and after p.gender('feminine') the same call returns she. The classical method is the other stateful switch, and it exists because the modern rules are not the only rules. Older style forms, for example the plural of certain words, are what classical mode gives you, and the README lists it alongside inflect and gender as the methods that change engine behaviour rather than return a value directly. There is also defnoun, defverb, defadj, defa and defan.
A small dependency list and a careful release process
The packaging is worth a look because it shows how the project is maintained. The library is on PyPI as inflect, the build backend is setuptools with setuptools_scm deriving the version from the repository, and the build requires coherent.licensed. The runtime dependencies are short: more_itertools at 8.5.0 or newer, typeguard at 4.0.1 or newer, and typing_extensions for Python versions below 3.9. The project requires Python 3.10 or newer and is classified as Production/Stable. The optional dependency groups are the interesting part, because they show the testing philosophy. The test group pulls in pytest and pygments, the check group adds pytest-checkdocs and pytest-ruff, the cover group adds pytest-cov, the type group adds pytest-mypy with PyPy excluded on the grounds that a specific mypy issue makes it noisy there, and the enabler group uses pytest-enabler. The documentation extras carry the Sphinx stack along with jaraco.packaging, rst.linker, furo and sphinx-lint, plus the tidelift funding integration. The repository layout matches: a docs/ directory, a tests/ directory, pytest.ini, tox.ini, ruff.toml, mypy.ini, a.coveragerc, towncrier.toml for the changelog fragments, and a NEWS.rst. The changelog fragments file is a sign that changes are recorded per pull request rather than written by hand at release time, and the release list shows v7.3.1 in July 2024, v7.4.0 in September 2024 and v7.5.0 in December 2024, with the last push to the branch on 2026-10-17. The pyproject file records the licence as MIT, and the repository carries a SECURITY.md file.
Where inflect is the wrong tool
The first limit is language. The README states plainly that the functionality is limited to English, which is honest and correct, since English pluralisation and article rules are irregular in ways that other languages are not, but it does mean a multilingual product needs a different approach for the other languages. The second is the weight of the dependency for a simple need. If all you want is person to people, a dozen lines of a pluralisation table or a much smaller library does the job, and pulling in more_itertools and typeguard for that is a trade you may not want. The third is the stateful parts of the API. The gender and classical methods change the engine's behaviour, so a single shared engine object cannot serve two threads with different gender settings without care, and the same is true if you want one part of your output in classical mode and another in modern mode. The fourth is that English correctness has limits the library does not claim to solve. It does not know your sentence, so it cannot resolve whether a noun is an exception to a rule in your specific domain, and a business term that is a proper noun or a product name will be pluralised like any other word unless you handle it yourself. The fifth is simply the maintenance picture. The package metadata classifies the library as Production/Stable, and the newest tagged release listed is v7.5.0 from 2024-12-28, while the last push was on 2026-10-17.
Editorial conclusion
inflect suits a Python application that writes English sentences from counts, for example notification text, report copy or a user interface string, and it is the right tool when a noun and a verb need different treatment for the same count. It does not suit a project that needs languages other than English, since the library states that its functionality is limited to English, and it does not suit code that only needs a simple plural, since the dependency weight and the part of speech distinction are more than a plural function requires. Before you adopt it, read the method list in the README and check that the words you care about are covered, and note that the last push was on 2026-10-17 with the newest release at v7.5.0 from 2024-12-28.
Frequently asked questions
How do I install inflect?
The package is published on PyPI as inflect and requires Python 3.10 or newer. The README points at the PyPI project page for downloads and the project hosts its own documentation on readthedocs.
What is the difference between plural_noun and plural_verb in inflect?
They treat the input as different parts of speech. With the same input, plural_verb('saw', 1) returns 'saw' while plural_noun('saw', 2) returns 'saws', and plural_noun('I', 2) returns 'we' while plural_adj('my', 2) returns 'our'.
How does inflect handle large numbers?
The threshold argument of number_to_words leaves numerals as numerals above the given value, so with a threshold of 10 the value 9 is spelled out as nine, 10 is ten, 11 is '11' and 1000 is '1,000'. The group, andword, zero, one and decimal parameters adjust the wording and digit grouping.
Is inflect limited to English?
Yes. The README states that the functionality of generating plurals, singular nouns, ordinals, indefinite articles and word based representations of numbers is limited to English.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/jaraco-inflect)Was this useful?