My response to Phil Haack’s post on singular and plural phrasing.
public static class Pluralization
{
private static readonly ConcurrentDictionary dictionary;
private static Func<string, string> defaultPluralizationRule = (s) => s.EndsWith("s") ? "es" : "s";
static Pluralization()
{
dictionary = new ConcurrentDictionary();
}
public static void Add(string singular, string plural)
{
if (singular.IsBlank())
throw new ArgumentException("singular string is not allowed to be null or empty");
if (plural.IsBlank())
throw new ArgumentException("plural string is not allowed to be null or empty");
dictionary[singular] = plural;
}
public static void Add(IEnumerable<KeyValuePair<string, string>> keyValuePairs)
{
keyValuePairs.ForEach(kvp => Add(kvp.Key, kvp.Value));
}
public static void Clear()
{
dictionary.Clear();
}
public static void SetDefaultPluralizationRule(Func<string, string> rule)
{
defaultPluralizationRule = rule;
}
public static string Pluralize(this int count, string singular, string plural = null)
{
if (count < 0)
throw new ArgumentOutOfRangeException("count", "count may not be less than 0");
if (plural.IsBlank() == false)
Add(singular, plural);
if (count == 1)
return OutputString(count, singular);
if (dictionary.TryGetValue(singular, out plural))
return OutputString(count, plural);
return OutputString(count, singular + defaultPluralizationRule(singular));
}
private static string OutputString(int i, string s)
{
return string.Format("{0} {1}", i, s);
}
}
A gist is available here, with unit tests. Benefits of my approach…
- Pluralized forms are cached, so this can be something that is built during application startup.
- The default action is a function. If the word ends in an “s”, “es” is added to the end. Otherwise, just an “s” is added to the end of the word. This policy works for a lot of words in the English language.
What do you think?