inflect
A Go library for English word pluralization and singularization, inspired by Rails ActiveRecord's inflector — the kind of smart pluralization that ORM frameworks use for automatic table naming.
Note on Django: Django's built-in auto-pluralization is intentionally primitive — it simply appends "s" to every model name (Person → Persons, Category → Categorys). Developers are expected to override verbose_name_plural manually for anything irregular. This library does what Django doesn't: full English morphology rules so you rarely need to override anything.
Zero dependencies. No reflect. No regexp. Pure stdlib.
Features
| Rule class |
Examples |
| Irregular words |
child → children, man → men, foot → feet |
Latin/Greek -is |
analysis → analyses, thesis → theses |
Latin -um → -a |
datum → data, medium → media, curriculum → curricula |
Greek -on → -a |
criterion → criteria, phenomenon → phenomena |
Latin -us → -i |
alumnus → alumni, cactus → cacti, focus → foci |
Latin -ex/-ix → -ices |
index → indices, matrix → matrices, vertex → vertices |
Sibilants (-s,-x,-z,-ch,-sh) |
status → statuses, box → boxes, church → churches |
Consonant + -y → -ies |
category → categories, query → queries |
Vowel + -y → -s |
day → days, key → keys, monkey → monkeys |
-fe → -ves |
knife → knives, wife → wives |
-f → -ves |
leaf → leaves, wolf → wolves |
-o endings |
tomato → tomatoes, video → videos |
| Uncountables |
sheep, fish, information, equipment (unchanged) |
| Default |
model → models, user → users |
Additional utilities:
- Case preservation —
Category → Categories, STATUS → STATUSES
Singularize — reverse of all rules above
TableName — PascalCase struct name → snake_case plural (GORM/Django convention)
PluralizeIf — count-aware form, mirrors Django's {% pluralize %} tag
IsPlural / IsSingular — heuristic detection
Install
go get codeberg.org/anarproject/inflect
Usage
import "codeberg.org/anarproject/inflect"
// Pluralize
inflect.Pluralize("category") // "categories"
inflect.Pluralize("analysis") // "analyses"
inflect.Pluralize("criterion") // "criteria"
inflect.Pluralize("status") // "statuses"
inflect.Pluralize("person") // "people"
inflect.Pluralize("sheep") // "sheep" (uncountable)
// Case preserved
inflect.Pluralize("Category") // "Categories"
inflect.Pluralize("STATUS") // "STATUSES"
// Count-aware (Django {% pluralize %} equivalent)
inflect.Pluralize("item", 1) // "item"
inflect.Pluralize("item", 0) // "items"
inflect.Pluralize("item", 5) // "items"
// PluralizeIf — same thing, explicit API
fmt.Sprintf("%d %s", n, inflect.PluralizeIf("category", n))
// Singularize
inflect.Singularize("categories") // "category"
inflect.Singularize("analyses") // "analysis"
inflect.Singularize("criteria") // "criterion"
inflect.Singularize("Statuses") // "Status"
// ORM table name: PascalCase → snake_case plural
inflect.TableName("UserProfile") // "user_profiles"
inflect.TableName("BlogPost") // "blog_posts"
inflect.TableName("Category") // "categories"
inflect.TableName("Analysis") // "analyses"
// Detection
inflect.IsPlural("models") // true
inflect.IsSingular("model") // true
inflect.IsPlural("sheep") // true (uncountable = both)
GORM integration
type Category struct {
gorm.Model
Name string
}
func (Category) TableName() string {
return inflect.TableName("Category") // "categories"
}
Rule priority
Pluralize applies rules in this order — first match wins:
- Uncountable → return as-is
- Irregular lookup (O(1) map)
-is → -es
- Sibilant suffixes (
-s,-x,-z,-ch,-sh) → -es
- Consonant+
-y → -ies; vowel+-y → -s
-fe → -ves
-f (non-regular) → -ves
-o endings
- Default →
-s
Singularize applies rules in reverse order with the same irregular table.
Running tests
go test ./... -v
All 100+ sub-tests pass, including:
- Irregular, Latin/Greek, sibilant, consonant-y, -o, -f/-fe, uncountable
- Case preservation (lower / Title / UPPER)
- Full round-trip:
word → plural → singular == word
TableName for 9 PascalCase struct names
PluralizeIf count boundary (0, 1, 2, 5)
- Edge cases (empty string)
Comparison with Django and Rails
| Framework |
Auto-pluralization |
How |
| Django |
Dumb — appends "s" only |
Person → Persons, Category → Categorys |
| Rails ActiveRecord |
Smart — full inflector |
Person → People, Category → Categories |
| inflect (this library) |
Smart — full inflector |
Same as Rails |
Django developers work around this by setting verbose_name_plural manually in every model's Meta class. This library gives Go projects the Rails-style inflector Django never shipped.
License
MIT