Dealing with singular plural phrasing

This is an age old problem and one that’s probably been solved countless times before, but I’m going to write about it anyways.

Say you’re writing code like this:

<p>You have the following themes:</p>
<ul>
@foreach(var theme in Model) {
  <li>@theme.Id</li>
}
</ul>

The natural inclination for the lazy developer is to leave enough alone and stop there. It’s good enough, right? Right?

Sure, when the value of Model.Count is zero or larger than one. But when it is exactly one, the phrase is incorrect English as it should be singular “You have the following theme”.

I must fight my natural inclination here! On the NuGet team, we have a rallying cry intended to inspire us to strive for better, “Apple Polish!”. We tend to blurt it out at random in meetings. I’m thinking of purchasing each member of the team a WWSJD bracelet (What Would Steve Jobs Do?).

To handle this case, I wrote a simple extension method:

public static string CardinalityLabel(this int count, string singular,
    string plural)
{
    return count == 1 ? singular : plural;
}
	

Notice that I didn’t try automatic pluralization. That’s just a pain.

With this method, I can change the markup to say:

<p>You have the following 
  @Model.Count.CardinalityLabel("theme", "themes"):</p>

I’m still just not sure about the name of that method though. What should it be?

Do you have such a method? Or are you fine with the awkward phrasing once in a while?

Tags: ,

What others have said

Requesting Gravatar... Shannon Deminick Aug 16, 2011 3:42 PM
# re: Dealing with singular plural phrasing
In Umbraco v5, we've got a custom Localization framework that takes care of this and much more in the form of languages, most of the details are here:

kuhnel.wordpress.com/...
Requesting Gravatar... Ian Mercer Aug 16, 2011 3:45 PM
# re: Dealing with singular plural phrasing
Take a look at Inflector in Castle Active Record.
Requesting Gravatar... Ken Egozi Aug 16, 2011 4:02 PM
# re: Dealing with singular plural phrasing
I second Ian Mercer.
The Inflector class is awesome, and I've been using it for ages, carrying it around for various projects - thank you Castle Project for choosing Apache License !
Requesting Gravatar... Ryan Aug 16, 2011 4:05 PM
# re: Dealing with singular plural phrasing
The label doesn't change with every cardinality, so perhaps it's just better to use PluralizedLabel, or Pluralizer, or PluralityLabel?
Requesting Gravatar... Pop Catalin Aug 16, 2011 4:06 PM
# re: Dealing with singular plural phrasing
A problem that ain't worth solving, complicates localization (wasted effort) and code (wasted effort), for no real benefit.

From Jeff Atwood's twitter:

"Dear Next Person Who Opens a Pluralization 'Bug', I will personally come to your house and bludgeon you to death with a giant S"
- 3:33 AM Feb 1st, 2009 via web
Requesting Gravatar... Fredrik Kalseth Aug 16, 2011 4:08 PM
# re: Dealing with singular plural phrasing
What about: "you have the following theme(s):". Problem solved! ;-)
Requesting Gravatar... Boris Yankov Aug 16, 2011 4:09 PM
# re: Dealing with singular plural phrasing
You should have the second parameter as optional.
If no second parameter is passed, try the automatic pluralization.
Requesting Gravatar... Shawn Miller Aug 16, 2011 4:10 PM
# re: Dealing with singular plural phrasing
I wrote it this way:


public static string ToPlural(this string @this, int count = 0)
{
return count == 1 ? @this : System.Data.Entity.Design.PluralizationServices.PluralizationService.CreateService(new System.Globalization.CultureInfo("en-US")).Pluralize(@this);
}


Which allows me to do things like:


"goose".ToPlural();
String.Format("{0} {1}", commentCount, "comment".ToPlural(commentCount));
Requesting Gravatar... Yuri Khan Aug 16, 2011 4:10 PM
# re: Dealing with singular plural phrasing
You will run into trouble with this ”singular for exactly 1, plural for all other numbers” approach as soon as you try to localize into any language that has more than two cardinalities, or where the rules for cardinality selection are different.

Consider Russian. Here we have forms for singular, dual and plural.

Singular is used for all numerals that end in ”один” (“one”), that is, for all n that n % 10 == 1 && n % 100 != 11 (because *11 ends in “одиннадцать” (“eleven”) which needs plural).

Similarly, dual is used for numerals ending in “два”, “три” and “четыре” (2, 3 and 4; n % 10 in {2, 3, 4} && n % 100 not in {12, 13, 14}, again because teen-numerals require plural).

Although, if the extension method is not defined in the model but somewhere near the localization, it can be redefined appropriately for each language.
Requesting Gravatar... Karel Vandenhove Aug 16, 2011 4:13 PM
# re: Dealing with singular plural phrasing
You should add a switch for count == 0 => "no themes"
Requesting Gravatar... Ken Egozi Aug 16, 2011 4:16 PM
# re: Dealing with singular plural phrasing
@Yuri's point is spot on - one more reason for keeping rendering concerns in the presentation layer
Requesting Gravatar... Remy Aug 16, 2011 4:52 PM
# re: Dealing with singular plural phrasing
We actually store the whole sentence in the resx files.E.g. A sentence for zero, 1 and more results. If you mingle individual words like in your example it gets difficult to translate afterwards. And yes, don't forget the 0 case as Karel pointed out. Maybe we need something where you can add as many different plural phrasing as you want, so it works for all languages.
Requesting Gravatar... Neil Barnwell Aug 16, 2011 5:06 PM
# re: Dealing with singular plural phrasing
I've done the exact same thing, but for the sake of I18n, I tend to use string.Format() with the usual placeholders:


public static string Pluralise(object value, string singularPattern, string pluralPattern)
{
int number;

if (int.TryParse(value.ToString(), out number))
{
if (number == 1)
return string.Format(singularPattern, value);

return string.Format(pluralPattern, value);
}
throw new ArgumentException("Value must be numeric", "value");
}
Requesting Gravatar... Marcin Floryan Aug 16, 2011 5:12 PM
# re: Dealing with singular plural phrasing
Perhaps consider approach long used by the gettext project, mostly the ngettext method as described here: www.gnu.org/.../gettext.html#Plural-forms

If you want to pluralise and localise you open a world of very interesting rules in various languages.
Requesting Gravatar... John Aug 16, 2011 5:54 PM
# re: Dealing with singular plural phrasing
Whether you need to fix this depends on a whole raft of factors and trying to apply it to all software is just a recipe for failure. Consider the following scenarios:

1) A customer-facing website, available in a limited number of languages, for a company that wants to showcase its professionalism.

2) A command-line utility written in my spare time for use by power users, administrators and developers.

3) An open-source web-based project, early in its development, with a limited development team and available in as many languages as possible.

4) A troubleshooting message intended to appear in a log somewhere.

In only one of those examples can I really say that fixing this kind of thing with a pluralisation method is really worth it - can you guess which one it is?

(Yeah, using the "theme(s)" fix might be acceptable in the others).
Requesting Gravatar... Andy Norman Aug 16, 2011 5:57 PM
# re: Dealing with singular plural phrasing
I just looked at the Inflector class. It doesn't correctly pluralise virus to viruses opting instead for viri, which doesn't bode well.
Requesting Gravatar... Bertrand Le Roy Aug 16, 2011 6:26 PM
# re: Dealing with singular plural phrasing
What they said. This just won't work with localization. As you suspected, this is a solved problem: gettext is the standard solution.
Requesting Gravatar... Neil A Aug 16, 2011 6:33 PM
# re: Dealing with singular plural phrasing
What about something more re-usable; two generic extension methods like this?


T1 ValueChoice<T0,T1>(Func<T> predicate, T1 valueIfTrue, T1 valueIfFalse)


and


T1 ActionChoice<T0,T1>(Func<T> predicate, T1 actionIfTrue, T1 actionIfFalse)


So then you could do...


Model.ValueChoice(m=>m.Count==1,"theme","themes")
Requesting Gravatar... Neil A Aug 16, 2011 6:43 PM
# re: Dealing with singular plural phrasing
D'oh - spotted some dumb errors striaght after posting, but hopefully you got the idea! This might work out better?...

T1 ValueChoice<T0,T1>(T0 this target, Func<T0,bool> predicate, T1 valueIfTrue, T1 valueIfFalse)
Requesting Gravatar... Tim Iles Aug 16, 2011 6:53 PM
# re: Dealing with singular plural phrasing
Further to Andy's comment, Inflector also goes for "octopi". It also singularizes "loves" to "lofe" (but has explicit fix for the more common exception "moves"). Looks like this code would be fine for internal usage, e.g. convention-based pluralised table-name mapping from singular entity-name, but I wouldn't use it generically on a public facing front-end.
Requesting Gravatar... RayH Aug 16, 2011 8:06 PM
# Great solution already on NuGet
# Install-Package Pluralizer

Allows very nice conventions such as (I use an extension method "Pluralize" to shorten even more):

var str = "There {is} {_} {person}.";

var single = Pluralizer.Instance.Pluralize(str, 1);
Assert.AreEqual("There is 1 person.", single);

var plural = Pluralizer.Instance.Pluralize(str, 67);
Assert.AreEqual("There are 67 people.", plural);

Following these simple rules:

1) start by writing the sentence in its singular form.
2) wrap any words that you want “pluralized” in curly braces
3) nouns are handled automatically by the built-in PluralizationService class in Entity Framework for .NET 4
4) only the most simple verbs and pronouns are automatically handled. You’ll probably have to handle those by yourself, separated by the pipe character.
5) the number itself is displayed using the special underscore placeholder (i.e. write this to get the number itself printed: {_})
6) manually define the singular and plural words by separating them with a pipe character (e.g. {singular|plural}).
Requesting Gravatar... Justin Aug 16, 2011 8:42 PM
# re: Dealing with singular plural phrasing
I have to second Boris Yankov's point. Just use theme(s) and move on with things that matter. Just my opinion. Haven't had a complaint about this from a client yet in nearly 10 years.
Requesting Gravatar... Tim Murphy Aug 16, 2011 8:45 PM
# re: Dealing with singular plural phrasing
My method name is SingleOrPlural.
Requesting Gravatar... Kamran Aug 16, 2011 9:53 PM
# re: Dealing with singular plural phrasing
I recently created a Plural Razor helper to do this for me. Mine does an extra step and will treat your inputs as format strings so if you want, you can embed the number. Sometimes I do, sometimes I don't (i.e. wrap the number in a tag).


@helper Plural(int count, string singular, string plural) {
@String.Format(count == 1 ? singular : plural, count)
}


Works very nicely! I like Neil's function in that you can pass any kind of numeric (decimal, double, etc.) but I haven't run into that yet. Keeping it simple!

For localization, since I haven't dealt with that yet, I like Yuri's suggestion of having separate implementations. So you can do:


Culture.Plural(count, singular, plural)


Assuming you've implemented an architecture that will support that. That should work if you have a defined set of locales you support. Or like others have suggested, perhaps an existing library takes care of that.
Requesting Gravatar... Eric Malamisura Aug 16, 2011 10:10 PM
# re: Dealing with singular plural phrasing
I think you are right on here, this is quality. Anyone who says this is a waste of time and effort has no freaking clue what polish is or what quality is for that matter. We have reached an evolution in software design that begs for a high degree of polish, its no longer acceptable for our software to "do" what its suppose to do to get the job done. Consumers, and users alike assume it will "do", and want polish/shine. Their needs and wants have shifted, for the better IMO!
Requesting Gravatar... Sam Aug 16, 2011 10:22 PM
# re: Dealing with singular plural phrasing
@pop catalin: 'no real benefit' - this is why most developers shouldn't be making UX decisions. I'm fairly sure you believe this is true, but if you're building a product where customer experience is important, generating grammatically incorrect output is not a good look (or, in my view, correct code).

Yes, localization is hard. Accept the fact you're going to have to invest a fair bit of additional development if you want the reap the rewards of a larger market. Don't use it as an excuse for mediocrity.
Requesting Gravatar... MisterJames Aug 16, 2011 10:23 PM
# re: Dealing with singular plural phrasing
+1 for RayH. Pluralizer also has an Mvc package with helpers called Pluralizer.Mvc.

I deal with Français and English and this library suits me fine.
Requesting Gravatar... Quentin Aug 17, 2011 12:03 AM
# re: Dealing with singular plural phrasing
A while ago I ran into a promising string formatting library for .Net, SmartFormat.

https://github.com/scottrippey/SmartFormat/wiki
(originally www.codeproject.com/.../Custom_Formatting.aspx)

It has functionality built in for pluralizing, or rather conditionally formatting based on a count.

It also tests as one of the fastest string formatting libraries available for .Net.
Requesting Gravatar... Pop Catalin Aug 17, 2011 12:44 AM
# re: Dealing with singular plural phrasing
@Sam,
Obviously UX decisions are rather subjective and depend heavily on the type of the project. Having pluralization may or may not add any benefit at all.
What I'm arguing is the value obtained relative to (overall) effort, and in my opinion this effort can be used elsewhere with more added value.
Having worked on some heavily localized projects, I can say that ignoring this issue can bring more benefit (even more polish in other areas) than trying to solve it.




Requesting Gravatar... Adam Ralph Aug 17, 2011 1:46 AM
# re: Dealing with singular plural phrasing
Whilst I have a deep love of polishing my UI's (and code), my vote leans towards the 'not worth the effort' side on this one. As various comments above have noted, this gets tricky when it comes to localization and it seems to be a can of worms that's just not worth opening.

"You have the following theme(s):"
sits just fine with me and I don't consider it 'mediocre' or 'unpolished' ;-)
Requesting Gravatar... Adam Ralph Aug 17, 2011 1:48 AM
# re: Dealing with singular plural phrasing
Of course, I may have emphasised my point more by getting my blockquote correct ;-)
Requesting Gravatar... Jarrett Aug 17, 2011 2:13 AM
# re: Dealing with singular plural phrasing
Please "borrow" the name from RoR core: pluralize is a great method name, and I wouldn't have to learn a new method name.
Requesting Gravatar... Nicholas Carey Aug 17, 2011 2:31 AM
# re: Dealing with singular plural phrasing
As noted earlier, different languages have quite a variance in their representation of grammatical:

+ Singular/Plural. The base form is singular; the plural form is inflected.
+ Singulative/Plural. Inverse of above: the base form is plural; the singulative form is inflected. To complicate thing, in Russian and Arabic, the singulative form always has feminine gender regardless of the base word's gender.
+ Singular/Dual/Plural. Some languages differentiate between singular (1), dual(2) and collective (>2, many).
+ Singular/Dual/Trial/Plural. And, just to spice it up a bit, some languages toss in a trial (3) form for pronouns (but not nouns), to boot.

It gets further complicated because in some languages, the inflected form will change not only based on the count, but on gender and case as well.

Requesting Gravatar... Dmytrii Nagirniak Aug 17, 2011 8:39 AM
# re: Dealing with singular plural phrasing
I like how Rails does it with pluralize method that optinally allow you to pass plural and do its magic otherwise.

So ideally I would prefer usage similar to:


Model.Count.PluralizeFor("theme", "themes");
Model.Count.PluralizeFor("theme");


With the method definition of:

public static string PluralizeFor(this int number, string singular, string plural = null) {
if (number == 1)
return singular;
return plural ?? Inflector.Pluralize(singular);
}

To me this seem pretty clear approach.
Requesting Gravatar... Josh Aug 17, 2011 12:13 PM
# re: Dealing with singular plural phrasing
I found this c# port of the Ruby pluralize method a while back, and have been using it fairly extensively ever since then.

Super light weight and easy to turn into an extension method.

It only works with English, but my guess is the percentage of people who actually code for internationalization in their apps is small.

Old School Nuget: Ctrl + C, Ctrl + V
Requesting Gravatar... Mike Aug 21, 2011 11:42 PM
# re: Dealing with singular plural phrasing
Sean M. Burke and Jordan Lachler once wrote a humorous piece on the subject: http://bit.ly/oQglzN
Requesting Gravatar... James Barrow Aug 22, 2011 10:54 PM
# re: Dealing with singular plural phrasing
Haven't read all the comments. Did see a similar thing in CodeIgniter, which has an Inflector helper [ codeigniter.com/.../inflector_helper.html ]. Inflection seems to be the right term, and also seems quite complicated [ http://en.wikipedia.org/wiki/Inflection ].
Requesting Gravatar... Scott Hanselmnan Aug 23, 2011 1:39 PM
# re: Dealing with singular plural phrasing
What about the Pluralizer built into EF that we already ship with .NET4? www.hanselman.com/...
Requesting Gravatar... Michael Fokken Sep 09, 2011 1:53 AM
# re: Dealing with singular plural phrasing
I totally agree. I hate going to sites or using programs that say "1 days". How long would it take to program what you did? 10 seconds? I think it's really interesting the major websites don't take stuff like this into consideration.
-
http://www.michaelfokken.com/
Requesting Gravatar... Anonymous Sep 11, 2011 6:30 AM
# re: Dealing with singular plural phrasing
If you used proper Internet English, problem would be none:

All your theme:


<ul>
@foreach(var theme in Model) {
<li>@theme.Id</li>
}
</ul>

What do you have to say?

(will show your gravatar)
Please add 1 and 2 and type the answer here: