Skip to main content

pluralio

Pluralization and singularization for Python

English · Spanish · Portuguese · Zero dependencies · Type-safe · Extensible

PyPI version Python versions CI Coverage License Code style: ruff Type checker: mypy Downloads


Features

  • Zero dependencies — pure Python standard library, nothing else to install
  • Type-safe — full type hints, py.typed marker included (PEP 561)
  • 100% test coverage — 3,116 tests, every line is verified
  • Extensible at runtime — add irregulars, rules, uncountables, or entire languages without touching source code
  • Case preservation"Library""Libraries", "BOX""BOXES"
  • Count-awarepluralize("item", count=1)"item"
  • Hyphenated words"mother-in-law""mothers-in-law"
  • Python 3.10+ — tested on 3.10, 3.11, 3.12, and 3.13

Table of contents

Installation

pip install pluralio

Or with uv:

uv add pluralio

Quick start

import pluralio

# ── English (default) ──────────────────────────────────────────
pluralio.pluralize("cat")              # "cats"
pluralio.pluralize("box")              # "boxes"
pluralio.pluralize("child")            # "children"
pluralio.pluralize("city")             # "cities"
pluralio.singularize("cities")         # "city"
pluralio.singularize("mice")           # "mouse"
pluralio.singularize("children")       # "child"

# ── Spanish ────────────────────────────────────────────────────
pluralio.pluralize("gato", lang="es")         # "gatos"
pluralio.pluralize("lápiz", lang="es")        # "lápices"
pluralio.pluralize("examen", lang="es")       # "exámenes"
pluralio.singularize("lápices", lang="es")    # "lápiz"
pluralio.singularize("alemanes", lang="es")   # "alemán"

# ── Portuguese ────────────────────────────────────────────────
pluralio.pluralize("casa", lang="pt")         # "casas"
pluralio.pluralize("coração", lang="pt")      # "corações"
pluralio.pluralize("papel", lang="pt")        # "papéis"
pluralio.pluralize("flor", lang="pt")         # "flores"
pluralio.singularize("corações", lang="pt")   # "coração"
pluralio.singularize("papéis", lang="pt")     # "papel"

# ── Count-aware ────────────────────────────────────────────────
pluralio.pluralize("item", count=1)    # "item"  (singular)
pluralio.pluralize("item", count=0)    # "items" (plural)
pluralio.pluralize("item", count=5)    # "items" (plural)

# ── Case preservation ─────────────────────────────────────────
pluralio.pluralize("Library")          # "Libraries"
pluralio.pluralize("LIBRARY")          # "LIBRARIES"
pluralio.singularize("Libraries")      # "Library"

# ── Hyphenated words ──────────────────────────────────────────
pluralio.pluralize("mother-in-law")    # "mothers-in-law"
pluralio.singularize("mothers-in-law") # "mother-in-law"

# ── List supported languages ──────────────────────────────────
pluralio.supported_languages()         # ["en", "es", "pt"]

# ── Inspect word form ─────────────────────────────────────────
pluralio.is_plural("cats")             # True
pluralio.is_singular("cat")           # True
pluralio.is_plural("sheep")            # True   (uncountable — valid as both)
pluralio.is_singular("sheep")          # True   (uncountable — valid as both)

How it works

Every word goes through a three-step priority chain:

┌─────────────────────────────────────────────────────────────┐
│                    Input word (stripped)                     │
└───────────────────────────┬─────────────────────────────────┘
                            ▼
              ┌─────────────────────────┐
              │  1. Uncountable check   │  ← highest priority
              │     word in set?        │
              └────────────┬────────────┘
                           │ no
                           ▼
              ┌─────────────────────────┐
              │  2. Irregular lookup    │
              │     word in dict?       │
              └────────────┬────────────┘
                           │ no
                           ▼
              ┌─────────────────────────┐
              │  3. Regex rules         │  ← first match wins
              │     (ordered list)      │
              └────────────┬────────────┘
                           │ no match
                           ▼
              ┌─────────────────────────┐
              │  Return word unchanged  │
              └─────────────────────────┘
  • Uncountable words (e.g. "sheep", "information") are returned as-is.
  • Irregular words (e.g. "man""men") are looked up in a dictionary.
  • Regex rules are applied in order — the first matching pattern wins.
  • The casing of the input is always preserved in the output.

API reference

Core functions

Function Description
pluralize(word, lang="en", count=None) Convert a word to its plural form.
singularize(word, lang="en") Convert a word to its singular form.
is_plural(word, lang="en") Check if a word is in plural form.
is_singular(word, lang="en") Check if a word is in singular form.
supported_languages() Return a sorted list of registered language codes.

Extensibility functions

Function Description
add_irregular(singular, plural, lang="en") Add an irregular pair (both directions).
add_plural(singular, plural, lang="en") Add only the singular → plural direction.
add_singular(plural, singular, lang="en") Add only the plural → singular direction.
add_uncountable(word, lang="en") Mark a word as invariable.
add_plural_rule(pattern, replacement, lang="en") Insert a pluralization regex rule (top priority).
add_singular_rule(pattern, replacement, lang="en") Insert a singularization regex rule (top priority).
register_language(lang, *, plural_rules=..., ...) Register a completely new language.

Low-level registry

Function Description
LanguageRules Dataclass holding all rules for a language.
register(rules) Register a LanguageRules instance.
get_rules(lang) Retrieve rules for a language (raises ValueError if not found).

Extending rules

pluralio is designed to be extended at runtime — no need to modify the source code.

Add an irregular word

Registers both pluralize and singularize directions:

pluralio.add_irregular("person", "people")

pluralio.pluralize("person")      # "people"
pluralio.singularize("people")    # "person"

Add only plural or singular direction

When you need just one direction (e.g. Spanish accent restoration):

# Only pluralize direction
pluralio.add_plural("joven", "jóvenes", lang="es")
pluralio.pluralize("joven", lang="es")    # "jóvenes"

# Only singularize direction (accent restoration)
pluralio.add_singular("alemanes", "alemán", lang="es")
pluralio.singularize("alemanes", lang="es")  # "alemán"

Add an uncountable / invariable word

pluralio.add_uncountable("data")

pluralio.pluralize("data")      # "data"
pluralio.singularize("data")    # "data"

Add a regex rule

Custom rules are inserted at the top of the rule list (highest priority — first match wins):

pluralio.add_plural_rule(r"us$", "i")
pluralio.pluralize("cactus")    # "cacti"

pluralio.add_singular_rule(r"i$", "us")
pluralio.singularize("cacti")   # "cactus"

Register a new language

pluralio.register_language(
    "fr",
    plural_rules=[(r"$", "s")],
    singular_rules=[(r"s$", "")],
    irregular_plurals={"cheval": "chevaux"},
    uncountable={"information"},
)

pluralio.pluralize("chat", lang="fr")        # "chats"
pluralio.pluralize("cheval", lang="fr")      # "chevaux"
pluralio.singularize("chevaux", lang="fr")   # "cheval"
pluralio.pluralize("information", lang="fr") # "information"

Comparison

How does pluralio compare to other Python inflection libraries?

Feature pluralio inflect pluralsingular pluralizer
Spanish pluralization ✅ 100% ❌ 39% ⚠️ 74% ❌ 40%
Spanish singularization ✅ 100% ❌ 29% ⚠️ 87% ❌ N/A
Singularize
Count-aware (count=1)
Case preservation
Hyphenated words
Idempotency (ES)
Uncountables (ES) ✅ ~87
Accent restoration (ES) ⚠️ Partial
Runtime extensibility
Add custom languages
Zero dependencies
Type hints (py.typed)
Python 3.10+
Test coverage 100% ~95% ~60% ~70%

Supported languages

Language Code Regex rules Irregulars Uncountables Status
English en 7 684 219 ✅ Complete
Spanish es 9 352 + 81 extra singles 92 ✅ Complete
Portuguese pt 8 + 13 388 + 88 extra singles 88 ✅ Complete
French fr 🔜 Planned
Italian it 🔜 Planned

Roadmap

Version Goal Status
1.0.0 English + Spanish, core engine, extensibility API ✅ Released
1.1.0 English classical plurals, uncountables, suffix rules ✅ Released
1.2.0 English -che words, compound prefixes, whitespace ✅ Released
1.3.0 English expansion (Latin/Greek, proper nouns, -o words) ✅ Released
1.4.0 Spanish expansion (loanwords, accent restoration, uncountables, idempotency fix) ✅ Released
1.5.0 Portuguese (pt) ✅ Released
1.6.0 French (fr) 🔜 Planned
1.7.0 Italian (it) 🔜 Planned

Contributing

Contributions are welcome! Whether it's a bug report, a new language, or a feature suggestion — please read our guides first:

Quick start for contributors

git clone https://github.com/MathiasPaulenko/pluralio.git
cd pluralio
pip install -e ".[dev]"

# Run checks
ruff check pluralio/ tests/
mypy pluralio/
pytest

Security

If you discover a security vulnerability, please see our Security Policy for responsible disclosure instructions. Do not open a public issue.

License

MIT — Copyright (c) 2025 Mathias Paulenko

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pluralio-1.5.5.tar.gz (81.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pluralio-1.5.5-py3-none-any.whl (39.9 kB view details)

Uploaded Python 3

File details

Details for the file pluralio-1.5.5.tar.gz.

File metadata

  • Download URL: pluralio-1.5.5.tar.gz
  • Upload date:
  • Size: 81.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pluralio-1.5.5.tar.gz
Algorithm Hash digest
SHA256 5b723d807d9144cbc55982fe4c83375151cba77d9afdfebbe8ee4f27e7de8645
MD5 0217ecb8063b943481020ebb6d398d84
BLAKE2b-256 cf48bee85cb0c78bf165ae584fc6a61ffac1d39b5c2b7eb864b07bac9dc3915b

See more details on using hashes here.

Provenance

The following attestation bundles were made for pluralio-1.5.5.tar.gz:

Publisher: release.yml on MathiasPaulenko/pluralio

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pluralio-1.5.5-py3-none-any.whl.

File metadata

  • Download URL: pluralio-1.5.5-py3-none-any.whl
  • Upload date:
  • Size: 39.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pluralio-1.5.5-py3-none-any.whl
Algorithm Hash digest
SHA256 108f09363384197e4c8b6f019c18806c4726842ed77d2307d8e8f1c62d19aeed
MD5 f72beebb4ef0f2198dd27f0f40a03e18
BLAKE2b-256 6b4557469ac348c7908e39a65fa79838a6a72801abd4190a85d179cfcc7ca753

See more details on using hashes here.

Provenance

The following attestation bundles were made for pluralio-1.5.5-py3-none-any.whl:

Publisher: release.yml on MathiasPaulenko/pluralio

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

2.2.0

2 files

2.1.2

2 files

2.1.1

2 files

2.1.0

2 files

2.0.0

2 files

1.8.3

2 files

1.8.2

2 files

1.8.1

2 files

1.8.0

2 files

1.7.2

2 files

1.7.1

2 files

1.7.0

2 files

1.6.2

2 files

1.6.1

2 files

1.6.0

2 files

This release

1.5.5 This release

2 files

1.5.4

2 files

1.5.3

2 files

1.5.2

2 files

1.5.1

2 files

1.5.0

2 files

1.4.2

2 files

1.4.1

2 files

1.4.0

2 files

1.3.0

2 files

1.2.0

2 files

1.1.0

2 files

1.0.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page