Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clever way to append 's' for plural form in .Net (syntactic sugar)

You may checkout the PluralizationService class which is part of the .NET 4.0 framework:

string lives = "life";
if (player.Lives != 1)
{
    lives = PluralizationService
        .CreateService(new CultureInfo("en-US"))
        .Pluralize(lives);
}
Console.WriteLine("You have {0} {1} left", player.Lives, lives);

It is worth noting that only English is supported for the moment. Warning, this don't work on the Net Framework 4.0 Client Profile!

You could also write an extension method:

public static string Pluralize(this string value, int count)
{
    if (count == 1)
    {
        return value;
    }
    return PluralizationService
        .CreateService(new CultureInfo("en-US"))
        .Pluralize(value);
}

And then:

Console.WriteLine(
    "You have {0} {1} left", player.Lives, "life".Pluralize(player.Lives)
);

You can create a custom formatter that does that:

public class PluralFormatProvider : IFormatProvider, ICustomFormatter {

  public object GetFormat(Type formatType) {
    return this;
  }


  public string Format(string format, object arg, IFormatProvider formatProvider) {
    string[] forms = format.Split(';');
    int value = (int)arg;
    int form = value == 1 ? 0 : 1;
    return value.ToString() + " " + forms[form];
  }

}

The Console.WriteLine method has no overload that takes a custom formatter, so you have to use String.Format:

Console.WriteLine(String.Format(
  new PluralFormatProvider(),
  "You have {0:life;lives} left, {1:apple;apples} and {2:eye;eyes}.",
  1, 0, 2)
);

Output:

You have 1 life left, 0 apples and 2 eyes.

Note: This is the bare minimum to make a formatter work, so it doesn't handle any other formats or data types. Ideally it would detect the format and data type, and pass the formatting on to a default formatter if there is some other formatting or data types in the string.


With the newfangled interpolated strings, I just use something like this:

// n is the number of connection attempts
Console.WriteLine($"Needed {n} attempt{(n!=1 ? "s" : "")} to connect...");

EDIT: just ran across this answer again--since I originally posted this, I've been using an extension method that makes it even easier. This example is ordered by peculiarity:

static class Extensions {
    /// <summary>
    /// Pluralize: takes a word, inserts a number in front, and makes the word plural if the number is not exactly 1.
    /// </summary>
    /// <example>"{n.Pluralize("maid")} a-milking</example>
    /// <param name="word">The word to make plural</param>
    /// <param name="number">The number of objects</param>
    /// <param name="pluralSuffix">An optional suffix; "s" is the default.</param>
    /// <param name="singularSuffix">An optional suffix if the count is 1; "" is the default.</param>
    /// <returns>Formatted string: "number word[suffix]", pluralSuffix (default "s") only added if the number is not 1, otherwise singularSuffix (default "") added</returns>
    internal static string Pluralize(this int number, string word, string pluralSuffix = "s", string singularSuffix = "")
    {
        return $@"{number} {word}{(number != 1 ? pluralSuffix : singularSuffix)}";
    }
}

void Main()
{
    int lords = 0;
    int partridges = 1;
    int geese = 1;
    int ladies = 8;
    Console.WriteLine($@"Have {lords.Pluralize("lord")}, {partridges.Pluralize("partridge")}, {ladies.Pluralize("lad", "ies", "y")}, and {geese.Pluralize("", "geese", "goose")}");
    lords = 1;
    partridges = 2;
    geese = 6;
    ladies = 1;
    Console.WriteLine($@"Have {lords.Pluralize("lord")}, {partridges.Pluralize("partridge")}, {ladies.Pluralize("lad", "ies", "y")}, and {geese.Pluralize("", "geese", "goose")}");
}

(formats are the same). The output is:

Have 0 lords, 1 partridge, 8 ladies, and 1 goose
Have 1 lord, 2 partridges, 1 lady, and 6 geese

using @Darin Dimitrov solution, I would create an extention for string ....

public static Extentions
{
    public static string Pluralize(this string str,int n)
    {
        if ( n != 1 )
            return PluralizationService.CreateService(new CultureInfo("en-US"))
            .Pluralize(str);
        return str;
    }
}

string.format("you have {0} {1} remaining",liveCount,"life".Pluralize());

string message = string.format("You have {0} left.", player.Lives == 1 ? "life" : "lives");

Of course this assumes that you have a finite number of values to pluralize.


I wrote an open-source library called SmartFormat that does exactly that! It's written in C# and is on GitHub: http://github.com/scottrippey/SmartFormat

Although it supports several languages, English "plural rules" are the default. Here's the syntax:

var output = Smart.Format("You have {0} {0:life:lives} left.", player.Lives);

It also supports "zero" quantity, and nested placeholders, so you could do:

var output = Smart.Format("You have {0:no lives:1 life:{0} lives} left.", player.Lives);

See the Inflector class that is part of Castle ActiveRecord. It is licensed under the Apache license.

It has a set of regular expression rules that define how words are pluralized. The version I have used has some errors in these rules though, e.g. it has a 'virus' → 'virii' rule.

I have three extension methods which wrap Inflector, the first of which may be right up your street:

    /// <summary>
    /// Pluralises the singular form word specified.
    /// </summary>
    /// <param name="this">The singular form.</param>
    /// <param name="count">The count.</param>
    /// <returns>The word, pluralised if necessary.</returns>
    public static string Pluralise(this string @this, long count)
    {
        return (count == 1) ? @this :
                              Pluralise(@this);
    }

    /// <summary>
    /// Pluralises the singular form word specified.
    /// </summary>
    /// <param name="this">The singular form word.</param>
    /// <returns>The plural form.</returns>
    public static string Pluralise(this string @this)
    {
        return Inflector.Pluralize(@this);
    }

    /// <summary>
    /// Singularises the plural form word.
    /// </summary>
    /// <param name="this">The plural form word.</param>
    /// <returns>Th singular form.</returns>
    public static string Singularise(this string @this)
    {
        return Inflector.Singularize(@this);
    }