Extensibility API¶
pluralio — Pluralization and singularization for Python.
- Public API:
pluralize(): Convert a word to plural form.singularize(): Convert a word to singular form.supported_languages(): List registered language codes.add_irregular(): Add an irregular word pair (both directions).add_plural(): Add only the singular→plural direction.add_singular(): Add only the plural→singular direction.add_uncountable(): Mark a word as invariable.add_plural_rule(): Insert a pluralization regex rule.add_singular_rule(): Insert a singularization regex rule.register_language(): Register a new language with its rules.is_plural(): Check if a word is in plural form.is_singular(): Check if a word is in singular form.join(): Join words into a natural-language list.ordinal(): Convert a number to its ordinal string (1 → “1st”).template(): Interpolate pluralize/singularize into string templates.LanguageRules: Dataclass for custom language rules.register(): Low-level registry registration.get_rules(): Low-level registry lookup.
- Supported languages (built-in):
English (
en)Spanish (
es)Portuguese (
pt)French (
fr)Italian (
it)Esperanto (
eo)
Note
Language rules are registered as a side effect of importing this
package. Always use import pluralio (or
from pluralio import pluralize) rather than importing from
submodules directly, e.g. avoid from pluralio.core import pluralize,
because the latter bypasses registration and will raise
ValueError for any non-default language.
Example
>>> import pluralio
>>> pluralio.pluralize("cat")
'cats'
>>> pluralio.pluralize("gato", lang="es")
'gatos'
>>> pluralio.singularize("cities")
'city'
>>> pluralio.pluralize("libro", lang="eo")
'libroj'
- class pluralio.LanguageRules(code: str, irregular_plurals: dict[str, str] = <factory>, irregular_singles: dict[str, str] = <factory>, plural_rules: list[tuple[~re.Pattern[str], str]] = <factory>, singular_rules: list[tuple[~re.Pattern[str], str]] = <factory>, uncountable: set[str] = <factory>)[source]
Bases:
objectContainer for all pluralization/singularization rules of a language.
- code
ISO 639-1 language code (e.g.
"en","es","fr").- Type:
- irregular_plurals
Mapping of singular → plural for words that do not follow regex rules. Keys and values are lowercase. Checked before regex rules during pluralization.
- irregular_singles
Mapping of plural → singular for words that do not follow regex rules. Keys and values are lowercase. Checked before regex rules during singularization. Typically the inverse of
irregular_plurals, but may include extra entries (e.g. Spanish accent restoration).
- plural_rules
Ordered list of
(compiled_regex, replacement)tuples applied during pluralization. First match wins.- Type:
list[tuple[re.Pattern[str], str]]
- singular_rules
Ordered list of
(compiled_regex, replacement)tuples applied during singularization. First match wins.- Type:
list[tuple[re.Pattern[str], str]]
- uncountable
Set of lowercase words that are invariable — both
pluralizeandsingularizereturn them unchanged. Checked first, before irregulars and regex.
Warning
frozen=Trueprevents reassigning attributes (rules.code = "fr"raisesFrozenInstanceError), but the mutable containers (dict,list,set) can still be modified in place. This is by design — the extensibility API (pluralio.add_irregular(),pluralio.add_plural(), etc.) mutates the contents of already-registered instances. Always use those functions instead of mutating containers directly.- code: str
- pluralio.add_irregular(singular: str, plural: str, lang: str = 'en') None[source]
Add an irregular word pair for both pluralize and singularize.
This registers the word in both
irregular_plurals(singular → plural) andirregular_singles(plural → singular), so that bothpluralize()andsingularize()use the mapping. The entries are stored in lowercase for case-insensitive matching.- Parameters:
singular – The singular form of the word.
plural – The plural form of the word.
lang – Language code to register the pair in. Defaults to
"en".
Example
>>> add_irregular("person", "people") >>> pluralize("person") 'people' >>> singularize("people") 'person'
- pluralio.add_plural(singular: str, plural: str, lang: str = 'en') None[source]
Add only the singular→plural direction for an irregular word.
Unlike
add_irregular(), this only affectspluralize().singularize()will not know about the inverse mapping.This is useful when the plural→singular direction follows a different rule or needs a separate entry (e.g. Spanish accent restoration).
- Parameters:
singular – The singular form of the word.
plural – The plural form of the word.
lang – Language code. Defaults to
"en".
Example
>>> add_plural("joven", "jóvenes", lang="es") >>> pluralize("joven", lang="es") 'jóvenes'
- pluralio.add_plural_rule(pattern: str, replacement: str, lang: str = 'en') None[source]
Insert a pluralization regex rule at the top (highest priority).
Rules are checked in order, and the first match wins. By inserting at position 0, the new rule takes priority over all existing rules.
- Parameters:
pattern – Regular expression pattern to match (string, will be compiled internally).
replacement – Replacement string for
re.sub.lang – Language code. Defaults to
"en".
Example
>>> add_plural_rule(r"us$", "i") >>> pluralize("cactus") 'cacti'
- pluralio.add_singular(plural: str, singular: str, lang: str = 'en') None[source]
Add only the plural→singular direction for an irregular word.
Unlike
add_irregular(), this only affectssingularize().pluralize()will not know about the forward mapping.This is useful for Spanish accent restoration where the plural form has a predictable ending but the singular requires an accent that the regex rules cannot infer.
- Parameters:
plural – The plural form of the word.
singular – The singular form of the word.
lang – Language code. Defaults to
"en".
Example
>>> add_singular("alemanes", "alemán", lang="es") >>> singularize("alemanes", lang="es") 'alemán'
- pluralio.add_singular_rule(pattern: str, replacement: str, lang: str = 'en') None[source]
Insert a singularization regex rule at the top (highest priority).
Rules are checked in order, and the first match wins. By inserting at position 0, the new rule takes priority over all existing rules.
- Parameters:
pattern – Regular expression pattern to match (string, will be compiled internally).
replacement – Replacement string for
re.sub.lang – Language code. Defaults to
"en".
Example
>>> add_singular_rule(r"i$", "us") >>> singularize("cacti") 'cactus'
- pluralio.add_uncountable(word: str, lang: str = 'en') None[source]
Mark a word as uncountable (invariable).
Uncountable words are returned unchanged by both
pluralize()andsingularize(). They are checked before irregulars and regex rules, so they take highest priority.- Parameters:
word – The word to mark as uncountable.
lang – Language code. Defaults to
"en".
Example
>>> add_uncountable("data") >>> pluralize("data") 'data' >>> singularize("data") 'data'
- pluralio.get_rules(lang: str) LanguageRules[source]
Retrieve the rules for a given language code.
- Parameters:
lang – ISO 639-1 language code (e.g.
"en","es").- Returns:
The
LanguageRulesinstance for the requested language.- Raises:
ValueError – If
langis not registered. The error message includes the list of supported languages.
Example
>>> from pluralio.registry import get_rules >>> rules = get_rules("en") >>> rules.code 'en'
- pluralio.is_plural(word: str, lang: str = 'en') bool[source]
Check if a word is in its plural form.
A word is considered plural if singularizing it produces a different word. Uncountable (invariable) words are valid as both singular and plural, so they return
True.- Parameters:
word – The word to check.
lang – ISO 639-1 language code. Defaults to
"en".
- Returns:
Trueif the word is in plural form (or is uncountable),Falseotherwise.- Raises:
TypeError – If
wordis not a string.ValueError – If
langis not a registered language.
Example
>>> is_plural("cats") True >>> is_plural("cat") False >>> is_plural("sheep") True >>> is_plural("gatos", lang="es") True
- pluralio.is_singular(word: str, lang: str = 'en') bool[source]
Check if a word is in its singular form.
A word is considered singular if pluralizing it produces a different word. Uncountable (invariable) words are valid as both singular and plural, so they return
True.- Parameters:
word – The word to check.
lang – ISO 639-1 language code. Defaults to
"en".
- Returns:
Trueif the word is in singular form (or is uncountable),Falseotherwise.- Raises:
TypeError – If
wordis not a string.ValueError – If
langis not a registered language.
Example
>>> is_singular("cat") True >>> is_singular("cats") False >>> is_singular("sheep") True >>> is_singular("gato", lang="es") True
- pluralio.join(words: Iterable[str], *, conjunction: str = 'and', final_sep: str = ', ', sep: str = ', ') str[source]
Join words into a natural-language list.
- Parameters:
words – Iterable of strings to join.
conjunction – Conjunction word for the last item. Defaults to
"and".final_sep – Separator before the conjunction. Defaults to
", ". Pass""for no comma before the conjunction (Oxford comma removal).sep – Separator between items (except before the conjunction). Defaults to
", ".
- Returns:
A natural-language string joining all words.
Example
>>> join(["apple"]) 'apple' >>> join(["apple", "banana"]) 'apple and banana' >>> join(["apple", "banana", "carrot"]) 'apple, banana, and carrot' >>> join(["apple", "banana", "carrot"], final_sep="") 'apple, banana and carrot' >>> join(["apple", "banana", "carrot"], conjunction="or") 'apple, banana, or carrot'
- pluralio.ordinal(number: int | str) str[source]
Convert a number to its ordinal string representation.
- Parameters:
number – An integer or a string representation of an integer.
- Returns:
The ordinal string (e.g.
"1st","2nd","3rd","11th").- Raises:
ValueError – If
numbercannot be converted to an integer.
Example
>>> ordinal(1) '1st' >>> ordinal(2) '2nd' >>> ordinal(3) '3rd' >>> ordinal(11) '11th' >>> ordinal(21) '21st' >>> ordinal("101") '101st' >>> ordinal(0) '0th'
- pluralio.pluralize(word: str, lang: str = 'en', count: int | None = None) str[source]
Convert a word to its plural form.
The function follows the three-step priority chain (uncountable → irregular → regex) and preserves the input casing in the output.
- Parameters:
word – The singular word to pluralize.
lang – ISO 639-1 language code. Defaults to
"en".count – Optional integer count. When
count == 1the word is returned unchanged (singular form). Any other value (including0, negative numbers, andNone) produces the plural form.
- Returns:
The plural form of
word, with original casing preserved.- Raises:
TypeError – If
wordis not a string.ValueError – If
langis not a registered language.
Example
>>> pluralize("cat") 'cats' >>> pluralize("box") 'boxes' >>> pluralize("child") 'children' >>> pluralize("gato", lang="es") 'gatos' >>> pluralize("item", count=1) 'item' >>> pluralize("Library") 'Libraries' >>> pluralize("mother-in-law") 'mothers-in-law'
- pluralio.register(rules: LanguageRules) None[source]
Register a language’s rules in the global registry.
If a language with the same
codealready exists, it is overwritten. The regex application cache is cleared to prevent stale results.- Parameters:
rules – The
LanguageRulesinstance to register.- Raises:
ValueError – If
rules.codeis empty.
Example
>>> from pluralio.registry import LanguageRules, register >>> register(LanguageRules(code="xx"))
- pluralio.register_language(lang: str, *, plural_rules: list[tuple[str, str]] | None = None, singular_rules: list[tuple[str, str]] | None = None, irregular_plurals: dict[str, str] | None = None, irregular_singles: dict[str, str] | None = None, uncountable: set[str] | None = None) None[source]
Register a new language with its pluralization rules.
This is the high-level API for adding a completely new language. It compiles the regex patterns, normalizes keys to lowercase, auto-generates inverse irregular mappings, and registers the resulting
LanguageRulesin the global registry.If
plural_rulesis omitted, a default rule of(r"$", "s")is used (appends). Ifsingular_rulesis omitted, a default rule of(r"s$", "")is used (strip trailings).For every entry in
irregular_plurals, the inverse is automatically added toirregular_singlesunless an explicit entry already exists.- Parameters:
lang – ISO 639-1 language code for the new language.
plural_rules – List of
(pattern, replacement)string tuples for pluralization. Checked in order; first match wins.singular_rules – List of
(pattern, replacement)string tuples for singularization. Checked in order; first match wins.irregular_plurals – Mapping of singular → plural for irregular words. Keys and values are normalized to lowercase.
irregular_singles – Mapping of plural → singular for irregular words. Keys and values are normalized to lowercase. Auto-populated from
irregular_pluralswhere possible.uncountable – Set of invariable words. Normalized to lowercase.
Example
>>> register_language( ... "fr", ... plural_rules=[(r"$", "s")], ... singular_rules=[(r"s$", "")], ... irregular_plurals={"cheval": "chevaux"}, ... uncountable={"information"}, ... ) >>> pluralize("chat", lang="fr") 'chats' >>> pluralize("cheval", lang="fr") 'chevaux' >>> singularize("chevaux", lang="fr") 'cheval' >>> pluralize("information", lang="fr") 'information'
- pluralio.singularize(word: str, lang: str = 'en') str[source]
Convert a word to its singular form.
The function follows the three-step priority chain (uncountable → irregular → regex) and preserves the input casing in the output.
- Parameters:
word – The plural word to singularize.
lang – ISO 639-1 language code. Defaults to
"en".
- Returns:
The singular form of
word, with original casing preserved.- Raises:
TypeError – If
wordis not a string.ValueError – If
langis not a registered language.
Example
>>> singularize("cities") 'city' >>> singularize("mice") 'mouse' >>> singularize("lápices", lang="es") 'lápiz' >>> singularize("Libraries") 'Library' >>> singularize("mothers-in-law") 'mother-in-law'
- pluralio.supported_languages() list[str][source]
Return a sorted list of all registered language codes.
- Returns:
Sorted list of ISO 639-1 codes (e.g.
["en", "eo", "es", "fr", "it", "pt"]).
Example
>>> from pluralio.registry import supported_languages >>> supported_languages() ['en', 'eo', 'es', 'fr', 'it', 'pt']
- pluralio.template(text: str, **kwargs: object) str[source]
Interpolate pluralize/singularize into a string template.
Placeholders use the syntax
{name},{name:pluralize}, or{name:singularize}. When:pluralizeis used, the function looks for acountkey inkwargs(or a custom count variable specified as{name:pluralize:count_var}) to enable count-aware pluralization.- Parameters:
text – Template string with placeholders.
**kwargs – Values for placeholder substitution. If a
countkey is present, it is used for count-aware pluralization. A custom count variable can be specified in the placeholder syntax (e.g.{word:pluralize:n}useskwargs["n"]as the count).
- Returns:
The interpolated string.
- Raises:
KeyError – If a placeholder name is not found in
kwargs.
Example
>>> template("I have {count} {word:pluralize}", count=5, word="cat") 'I have 5 cats' >>> template("I have {count} {word:pluralize}", count=1, word="cat") 'I have 1 cat' >>> template("The {word:singularize} is here", word="mice") 'The mouse is here' >>> template("{n} {word:pluralize} arrived", n=3, word="box") '3 boxes arrived'
Examples¶
Adding an irregular word pair:
from pluralio import add_irregular, pluralize, singularize
add_irregular("person", "people")
pluralize("person") # "people"
singularize("people") # "person"
Adding a plural-only mapping:
from pluralio import add_plural, pluralize
add_plural("joven", "jóvenes", lang="es")
pluralize("joven", lang="es") # "jóvenes"
Adding a singular-only mapping (accent restoration):
from pluralio import add_singular, singularize
add_singular("alemanes", "alemán", lang="es")
singularize("alemanes", lang="es") # "alemán"
Marking a word as uncountable:
from pluralio import add_uncountable, pluralize, singularize
add_uncountable("data")
pluralize("data") # "data"
singularize("data") # "data"
Adding a custom regex rule:
from pluralio import add_plural_rule, pluralize
add_plural_rule(r"us$", "i")
pluralize("cactus") # "cacti"
Registering a new language:
from pluralio import register_language, pluralize, singularize
register_language(
"xx",
plural_rules=[(r"$", "s")],
singular_rules=[(r"s$", "")],
irregular_plurals={"foo": "foos"},
uncountable={"bar"},
)
pluralize("word", lang="xx") # "words"
pluralize("foo", lang="xx") # "foos"
pluralize("bar", lang="xx") # "bar"
Checking plural / singular:
from pluralio import is_plural, is_singular
is_plural("cats") # True
is_singular("cat") # True
is_plural("gatos", lang="es") # True
is_singular("gato", lang="es") # True