views:

185

answers:

6

I want to be able to type something like:

Console.WriteLine("You have {0:life/lives} left.", player.Lives);

instead of

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

so that for player.Lives == 1 the output would be: You have 1 life left.
for player.Lives != 1 : You have 5 lives left.

or

Console.WriteLine("{0:day[s]} till doomsday.", tillDoomsdayTimeSpan);

Some systems have that built-in. How close can I get to that notation in C#?

EDIT: Yes, I am specifically looking for syntactic sugar, and not a method to determine what singular/plural forms are.

+14  A: 

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.

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)
);
Darin Dimitrov
+1 Nice, way better than my solution.
Tim
+1 Wow. var moose = ps.Pluralize("mouse");
Noel Abrahams
I do believe your format parameters are reversed. Otherwise, +1.
Paul Ruane
+1 Nice to know. I didn't make it clear in my question. I am after syntactic sugar not a method to determine the forms themselves.
@Paul, good point. I updated my answer to fix this.
Darin Dimitrov
@user93422, you could add an [extension method](http://msdn.microsoft.com/en-us/library/bb383977.aspx) to the string class that pluralizes it given a number. It will be sugar and honey :-)
Darin Dimitrov
A nitpicky note: the plural form is usually used with n != 1. That is, 0 *lives* left or 2 *lives* left, but just one *life* left
Michael Haren
@Michael, my English is bad. Need to adapt the extension method then.
Darin Dimitrov
@Noel - Do you mean var mice? Looking at reflector looks like the plural for zoon is zoa... who knew :P +1 for learning something new
SwDevMan81
@SwDevMan81, moose are more interesting than mice, no? :D
Noel Abrahams
@Noel - haha, both are pretty lame
SwDevMan81
+1  A: 
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.

Tim
A: 

I'm thinking the easiest way to do it is to create an Interface IPlural which has an method .ToString(int quantity) which returns the singular form when quantity == 1 an the plural form all other times.

Brad
A: 

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);
    }
Paul Ruane
a direct link: http://cid-net.googlecode.com/svn/trunk/src/Cid.Mvc/Inflector.cs
Frank Schwieterman
@Frank Schwieterman: excellent, thanks.
Paul Ruane
+1  A: 

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());
Muad'Dib
IMO this is the most correct solution as it is provided inherent to .NET I would recommend `Inflector` however if you deploy with the Client framework as the pluralization service I believe is only in the full framework.
Chris Marisic
+2  A: 

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.

Guffa
with extension method it could be reduced to: `"you have {0:eye;eyes}".With(2)` which is what I was looking for.