I recently wanted a pluralization inflector in a C# project, one similar to the one in Ruby on Rails. Unable to find a satisfactory one, I whipped up my own. Here’s what I’ve got.
namespace MyNamespace
{
public class Formatting
{
private static readonly IList<string> Unpluralizables = new List<string>
{ "equipment", "information", "rice", "money", "species", "series", "fish", "sheep", "deer" };
private static readonly IDictionary<string, string> Pluralizations = new Dictionary<string, string>
{
// Start with the rarest cases, and move to the most common
{ "person", "people" },
{ "ox", "oxen" },
{ "child", "children" },
{ "foot", "feet" },
{ "tooth", "teeth" },
{ "goose", "geese" },
// And now the more standard rules.
{ "(.*)fe?", "$1ves" }, // ie, wolf, wife
{ "(.*)man$", "$1men" },
{ "(.+[aeiou]y)$", "$1s" },
{ "(.+[^aeiou])y$", "$1ies" },
{ "(.+z)$", "$1zes" },
{ "([m|l])ouse$", "$1ice" },
{ "(.+)(e|i)x$", @"$1ices"}, // ie, Matrix, Index
{ "(octop|vir)us$", "$1i"},
{ "(.+(s|x|sh|ch))$", @"$1es"},
{ "(.+)", @"$1s" }
};
public static string Pluralize(int count, string singular)
{
if (count == 1)
return singular;
if (Unpluralizables.Contains(singular))
return singular;
var plural = "";
foreach (var pluralization in Pluralizations)
{
if (Regex.IsMatch(singular, pluralization.Key))
{
plural = Regex.Replace(singular, pluralization.Key, pluralization.Value);
break;
}
}
return plural;
}
}
}
And of course, some NUnit tests.
namespace AutomatedTests
{
[TestFixture]
public class FormattingTests
{
[Test]
public void StandardPluralizationTests()
{
var dictionary = new Dictionary<string, string>();
dictionary.Add("sausage", "sausages"); // Most words - Just add an 's'
dictionary.Add("status", "statuses"); // Words that end in 's' - Add 'es'
dictionary.Add("ax", "axes"); // Words that end in 'x' - Add 'es'
dictionary.Add("octopus", "octopi"); // Some Words that end in 'us' - Replace 'us' with 'i'
dictionary.Add("virus", "viri"); // Some Words that end in 'us' - Replace 'us' with 'i'
dictionary.Add("crush", "crushes"); // Words that end in 'sh' - Add 'es'
dictionary.Add("crutch", "crutches"); // Words that end in 'ch' - Add 'es'
dictionary.Add("matrix", "matrices"); // Words that end in 'ix' - Replace with 'ices'
dictionary.Add("index", "indices"); // Words that end in 'ex' - Replace with 'ices'
dictionary.Add("mouse", "mice"); // Some Words that end in 'ouse' - Replace with 'ice'
dictionary.Add("quiz", "quizzes"); // Words that end in 'z' - Add 'zes'
dictionary.Add("mailman", "mailmen"); // Words that end in 'man' - Replace with 'men'
dictionary.Add("man", "men"); // Words that end in 'man' - Replace with 'men'
dictionary.Add("wolf", "wolves"); // Words that end in 'f' - Replace with 'ves'
dictionary.Add("wife", "wives"); // Words that end in 'fe' - Replace with 'ves'
dictionary.Add("day", "days"); // Words that end in '[vowel]y' - Replace with 'ys'
dictionary.Add("sky", "skies"); // Words that end in '[consonant]y' - Replace with 'ies'
foreach (var singular in dictionary.Keys)
{
var plural = dictionary[singular];
Assert.AreEqual(plural, Formatting.Pluralize(2, singular));
Assert.AreEqual(singular, Formatting.Pluralize(1, singular));
}
}
[Test]
public void IrregularPluralizationTests()
{
var dictionary = new Dictionary<string, string>();
dictionary.Add("person", "people");
dictionary.Add("child", "children");
dictionary.Add("ox", "oxen");
foreach (var singular in dictionary.Keys)
{
var plural = dictionary[singular];
Assert.AreEqual(plural, Formatting.Pluralize(2, singular));
Assert.AreEqual(singular, Formatting.Pluralize(1, singular));
}
}
[Test]
public void NonPluralizingPluralizationTests()
{
var nonPluralizingWords = new List<string> { "equipment", "information", "rice", "money", "species", "series", "fish", "sheep", "deer" };
foreach (var word in nonPluralizingWords)
{
Assert.AreEqual(word, Formatting.Pluralize(2, word));
Assert.AreEqual(word, Formatting.Pluralize(1, word));
}
}
}
}
And finally, usage.
var output = Formatting.Pluralization(2, "item"); // Produces "items" output = Formatting.Pluralization(5, "sheep"); // Produces "sheep" output = Formatting.Pluralization(100, "sausage"); // Produces "sausages" output = Formatting.Pluralization(1, "sausage"); // Produces "sausage"
Now, I’m sure that I’m missing some cases in there. For example, I haven’t found a good way to pluralize “proof.” If any of you wonderful people find another missing case, or if you want to add one, let me know in the comments.



