inflect generates grammatically correct English text. pip install inflect. Engine: import inflect; p = inflect.engine(). Plural: p.plural("dog") → “dogs”. p.plural("mouse") → “mice”. Plural noun: p.plural_noun("cactus") → “cacti”. Plural verb: p.plural_verb("is") → “are”. Singular: p.singular_noun("dogs") → “dog”. p.singular_noun("dog") → False (already singular). Article a/an: p.a("apple") → “an apple”. p.a("dog") → “a dog”. Number to words: p.number_to_words(42) → “forty-two”. p.number_to_words(0) → “zero”. Words with threshold: p.number_to_words(1000) → “one thousand”. Ordinal: p.ordinal(1) → “1st”. p.ordinal(42) → “42nd”. Ordinal words: p.number_to_words(42, andword="") + p.ordinal("forty-two") → “forty-second”. join: p.join(["a","b","c"]) → “a, b, and c”. p.join(["a","b"], final_sep="", conj="or") → “a or b”. plural adj: p.plural_adj("red") → “red” (adjectives don’t usually pluralize). inflect noun: p.inflect("2 dog") → “2 dogs”. no: p.no("file", 0) → “no files”. p.no("file", 1) → “1 file”. p.no("file", 2) → “2 files”. present_participle: p.present_participle("run") → “running”. Classical mode: p.classical() — enables “formulae” vs “formulas”. compare: p.compare("dog", "dogs") → “si” (singular of irregular). Claude Code generates inflect UI labels, report summaries, pluralization helpers, and grammar-correct notification templates.
CLAUDE.md for inflect
## inflect Stack
- Version: inflect >= 7.0 | pip install inflect
- Init: p = inflect.engine()
- Plural: p.plural("dog") | p.plural_noun("cactus") | p.plural_verb("is")
- Singular: p.singular_noun("dogs") → "dog" | False if already singular
- Article: p.a("apple") → "an apple" | p.a("user") → "a user"
- Numbers: p.number_to_words(42) | p.ordinal(3) | p.no("file", count)
- Join: p.join(["a","b","c"]) → "a, b, and c"
inflect Grammar Pipeline
# app/grammar.py — inflect pluralization, articles, numbers, ordinals, joins, UI labels
from __future__ import annotations
import inflect
# Single shared engine instance (thread-safe for reads)
_p = inflect.engine()
# ─────────────────────────────────────────────────────────────────────────────
# 1. Pluralization
# ─────────────────────────────────────────────────────────────────────────────
def pluralize(word: str, count: int | None = None) -> str:
"""
Return the plural form of a word.
If count is given, return singular when count==1.
Example:
pluralize("dog") # "dogs"
pluralize("mouse") # "mice"
pluralize("child") # "children"
pluralize("dog", 1) # "dog"
pluralize("dog", 2) # "dogs"
"""
if count is not None and abs(count) == 1:
return word
return _p.plural(word) or word
def pluralize_verb(verb: str, count: int = 2) -> str:
"""
Return the correct verb form for the given count.
Example:
pluralize_verb("is", 1) # "is"
pluralize_verb("is", 2) # "are"
pluralize_verb("has", 3) # "have"
"""
if abs(count) == 1:
return verb
return _p.plural_verb(verb) or verb
def singular(word: str) -> str:
"""
Return the singular form of a word.
Returns the word unchanged if already singular.
Example:
singular("dogs") # "dog"
singular("mice") # "mouse"
singular("children") # "child"
singular("dog") # "dog" (already singular)
"""
result = _p.singular_noun(word)
return result if result else word
def is_plural(word: str) -> bool:
"""Return True if the word appears to be plural."""
return bool(_p.singular_noun(word))
# ─────────────────────────────────────────────────────────────────────────────
# 2. Article selection
# ─────────────────────────────────────────────────────────────────────────────
def with_article(word: str, capitalize: bool = False) -> str:
"""
Prepend 'a' or 'an' to a word based on pronunciation.
Example:
with_article("apple") # "an apple"
with_article("unicorn") # "a unicorn" (hard 'u')
with_article("hour") # "an hour" (silent 'h')
with_article("apple", capitalize=True) # "An apple"
"""
result = _p.a(word) or f"a {word}"
if capitalize:
return result[0].upper() + result[1:]
return result
def article_only(word: str) -> str:
"""Return just 'a' or 'an' for the given word."""
article_phrase = _p.a(word) or "a"
return article_phrase.split()[0]
# ─────────────────────────────────────────────────────────────────────────────
# 3. Number formatting
# ─────────────────────────────────────────────────────────────────────────────
def number_to_words(
n: int,
threshold: int | None = None,
andword: str = "and",
zero: str = "zero",
one: str | None = None,
) -> str:
"""
Convert an integer to English words.
threshold: only convert numbers below this value (return numeral otherwise).
Example:
number_to_words(0) # "zero"
number_to_words(42) # "forty-two"
number_to_words(1000) # "one thousand"
number_to_words(42, threshold=20) # "42" (above threshold)
"""
if threshold is not None and abs(n) >= threshold:
return str(n)
result = _p.number_to_words(n, andword=andword, zero=zero, one=one or "one")
return result
def ordinal_str(n: int, as_words: bool = False) -> str:
"""
Return the ordinal form of a number.
Example:
ordinal_str(1) # "1st"
ordinal_str(42) # "42nd"
ordinal_str(3, as_words=True) # "third"
"""
if as_words:
words = _p.number_to_words(n)
return _p.ordinal(words)
return _p.ordinal(n)
def count_phrase(
count: int,
noun: str,
zero_phrase: str | None = None,
as_words: bool = False,
capitalize: bool = False,
) -> str:
"""
Build a grammatically correct count phrase.
Example:
count_phrase(0, "file") # "no files"
count_phrase(1, "file") # "1 file"
count_phrase(3, "file") # "3 files"
count_phrase(0, "result",
zero_phrase="none") # "none"
count_phrase(42, "item",
as_words=True) # "forty-two items"
"""
if count == 0 and zero_phrase is not None:
return zero_phrase
num_str = number_to_words(count) if as_words else str(count)
noun_form = pluralize(noun, count)
if count == 0:
result = f"no {_p.plural(noun)}"
else:
result = f"{num_str} {noun_form}"
if capitalize:
return result[0].upper() + result[1:]
return result
# ─────────────────────────────────────────────────────────────────────────────
# 4. List joining
# ─────────────────────────────────────────────────────────────────────────────
def join_list(
items: list[str],
conjunction: str = "and",
oxford_comma: bool = True,
) -> str:
"""
Join a list of strings with a grammatically correct conjunction.
Example:
join_list(["Alice"]) # "Alice"
join_list(["Alice", "Bob"]) # "Alice and Bob"
join_list(["Alice", "Bob", "Carol"]) # "Alice, Bob, and Carol"
join_list(["a", "b", "c"], conjunction="or") # "a, b, or c"
join_list(["a", "b", "c"], oxford_comma=False) # "a, b and c"
"""
if not items:
return ""
if len(items) == 1:
return items[0]
if len(items) == 2:
return f"{items[0]} {conjunction} {items[1]}"
sep = ", " + conjunction if oxford_comma else " " + conjunction
return ", ".join(items[:-1]) + sep + " " + items[-1]
def join_nouns(nouns: list[str], counts: list[int], conjunction: str = "and") -> str:
"""
Join count+noun pairs.
Example:
join_nouns(["file", "folder"], [3, 1])
# "3 files and 1 folder"
"""
phrases = [count_phrase(c, n) for n, c in zip(nouns, counts)]
return join_list(phrases, conjunction=conjunction)
# ─────────────────────────────────────────────────────────────────────────────
# 5. Verb forms
# ─────────────────────────────────────────────────────────────────────────────
def present_participle(verb: str) -> str:
"""
Return the present participle (-ing form) of a verb.
Example:
present_participle("run") # "running"
present_participle("write") # "writing"
present_participle("play") # "playing"
"""
return _p.present_participle(verb) or f"{verb}ing"
def subject_verb(subject: str, verb: str, count: int = 1) -> str:
"""
Build a subject-verb phrase with correct agreement.
Example:
subject_verb("user", "has", 1) # "user has"
subject_verb("user", "has", 3) # "users have"
subject_verb("file", "is", 2) # "files are"
"""
noun = pluralize(subject, count)
vb = pluralize_verb(verb, count)
return f"{noun} {vb}"
# ─────────────────────────────────────────────────────────────────────────────
# 6. UI / notification helpers
# ─────────────────────────────────────────────────────────────────────────────
def badge_label(count: int, noun: str) -> str:
"""
Format a UI badge label.
Example:
badge_label(0, "notification") # "No notifications"
badge_label(1, "notification") # "1 notification"
badge_label(5, "notification") # "5 notifications"
"""
if count == 0:
return f"No {pluralize(noun)}"
noun_form = pluralize(noun, count)
return f"{count} {noun_form}"
def summary_sentence(
items: list[str],
verb: str = "found",
noun: str = "result",
) -> str:
"""
Build a result summary sentence.
Example:
summary_sentence([], "found", "result")
# "No results found."
summary_sentence(["a"], "found", "result")
# "1 result found."
summary_sentence(["a","b","c"], "returned", "item")
# "3 items returned."
"""
count = len(items)
if count == 0:
return f"No {pluralize(noun)} {verb}."
noun_form = pluralize(noun, count)
return f"{count} {noun_form} {verb}."
def notification_text(
actor: str,
action: str,
count: int,
item_noun: str,
) -> str:
"""
Build an activity notification string.
Example:
notification_text("Alice", "uploaded", 3, "file")
# "Alice uploaded 3 files."
notification_text("Bob", "deleted", 1, "comment")
# "Bob deleted 1 comment."
"""
item_form = pluralize(item_noun, count)
return f"{actor} {action} {count} {item_form}."
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
print("=== Pluralization ===")
for word in ["dog", "mouse", "child", "cactus", "criterion", "schema"]:
print(f" {word:12s} → {pluralize(word)}")
print("\n=== Singular detection ===")
for word in ["dogs", "mice", "children", "dog", "apple"]:
print(f" {word:10s} → singular: {singular(word):10s} is_plural: {is_plural(word)}")
print("\n=== Articles ===")
for word in ["apple", "banana", "hour", "unicorn", "eulogy", "honest"]:
print(f" {with_article(word)}")
print("\n=== Number to words ===")
for n in [0, 1, 13, 42, 100, 1001, 1_000_000]:
print(f" {n:>10} → {number_to_words(n)}")
print("\n=== Ordinals ===")
for n in [1, 2, 3, 11, 12, 21, 42]:
print(f" {n:3d} → {ordinal_str(n):6s} words: {ordinal_str(n, as_words=True)}")
print("\n=== count_phrase ===")
for n in [0, 1, 2, 5]:
print(f" {n} file → {count_phrase(n, 'file')}")
print(f" 42 items (words) → {count_phrase(42, 'item', as_words=True)}")
print("\n=== join_list ===")
print(f" 0 items: {join_list([])!r}")
print(f" 1 item: {join_list(['Alice'])}")
print(f" 2 items: {join_list(['Alice', 'Bob'])}")
print(f" 3 items: {join_list(['Alice', 'Bob', 'Carol'])}")
print(f" 3 (or): {join_list(['cats', 'dogs', 'fish'], conjunction='or')}")
print("\n=== UI helpers ===")
for n in [0, 1, 3, 99]:
print(f" badge: {badge_label(n, 'notification')}")
print("\n=== Notification texts ===")
print(notification_text("Alice", "uploaded", 3, "file"))
print(notification_text("Bob", "deleted", 1, "comment"))
print(summary_sentence(["a","b"], "returned", "record"))
For the num2words alternative — num2words converts numbers to words in many languages (English, Spanish, French, German, etc.) and supports currencies and years; inflect focuses exclusively on English grammar with pluralization, articles, verb agreement, and ordinals — use num2words when you need multilingual number-to-words conversion, inflect when you need the full English grammar toolkit including plurals, a/an, verb forms, and list joining. For the humanize alternative — humanize formats numbers and dates for human readability (humanize.naturalday(), humanize.filesize()); inflect handles English grammatical inflection (pluralization, articles, verb agreement) and ordinals — use humanize for date/time/size display formatting, inflect for dynamic text generation where grammatical correctness ("1 file" vs "3 files") is required. The Claude Skills 360 bundle includes inflect skill sets covering pluralize()/pluralize_verb()/singular()/is_plural(), with_article()/article_only(), number_to_words()/ordinal_str()/count_phrase(), join_list()/join_nouns(), present_participle()/subject_verb(), badge_label()/summary_sentence()/notification_text() UI helpers. Start with the free tier to try English grammar inflection and dynamic text generation code generation.