views:

263

answers:

7

Say you're making a blog software, and want to show the number of comments an entry got. You might do it this way:

[Entry title]
[Content........]
[ <?php print($numComments;) ?> Comments]

Which might result in:

[Entry title]
[Content........]
5 Comments

But if an entry had only 1 comment, I want the line to say 'Comment' rather than 'Comments'. And in-line if/elses are ugly and repetitive.

What's the best way to deal with this?

+1  A: 

Use a pluralize method like the one found here.

Brandon
+3  A: 

Create a function that takes a number and a word, and returns a string containing both. It will append an "s" (or consult a dictionary that you build) when the number is bigger than 1.

Sinan Taifour
I would make the function add the s if the number is not 1, so that 0 gets the plural form as well.
Fredrik Mörk
What would you call such a function?
Click Upvote
Pluralize seems like the most fitting name.
Brandon
+1 This is the proper way, but can get complicated when you have to deal with the different variations in the English language. (ie. Activity you'd need to make Activities instead of Activity). Having a dictionary that you build is proper, but can be a pain to keep up with.
Dusty
May be the 3rd argument passed to this function can be the plural word? E.g, if you did: `pluralize($num,'Comment');` it would return 'Comments', but if you did: `pluralize(($num,'Activity','Activities');` then it returns Activities rather than Activitys
Click Upvote
@Click Upvote: you have just invented something similar to the ngettext function, please see my answer below :-)
Martin Geisler
This can (and will) get complicated when the software needs to be localized, as some languages handle plural/singular differently from English.
fishlips
Well some languages are even more complicated. Singular, dual and plural each of them related to masculinity and repeated again with numbers like 101, 102, 10X... 201... So. Not all languages are as easy as English when it comes to this problem.
Robert Koritnik
I would also say that not all languages are as *difficult* as English when it comes to this problem :)
Roberto Bonvallet
+2  A: 

Not the most elegant, but the easiest it to output "Comment(s)".

[Entry title]
[Content........]
1 Comment(s)
Dusty
Easy, but doesn't work well in all situations
Click Upvote
As you note, this not very elegant... When I see such output, I always wonder why the computer didn't spend the extra millisecond to figure out if the "s" should be there or not :-)
Martin Geisler
+1 Haha. Agreed.
Dusty
+4  A: 

Why not take the time to humanize things even more....

switch ($numComments)
{
    case 0:
        echo "Be the first to write a comment";
        break;
    case 1:
        echo "Just one comment so far";
        break;
    default:
        echo "There are $numComments comments";

}
Paul Dixon
+1 This method will avoid most localization problems.
fishlips
@fishlips: except when a language has three or more plural forms. As explained below, the ngettext function is the more general solution. It allows a translator to include all his plural forms in the ".po" file and the right one will be selected at runtime.
Martin Geisler
+2  A: 

It surprises me that nobody suggested that yet, but what I usually do is to use the conditional operator:

string commentWord = numComments != 1 ? "Comments" : "Comment";

Note: the string should of course not be hard coded like this at all, but rather loaded from some resource repository, where it is stored with a formatting placeholder for the number, so that you can handle languages where the number should appear last (or in the middle):

// should load "{0} Comments" or "{0} Comment" if we run in an English locale
string comments = string.Format(
        numComments != 1 ? GetResource("Comments") : GetResource("Comment"),
        numComments);
Fredrik Mörk
This is not the right way to do internationalization. Language other than English have their own rules for plural forms; e.g. Lithuanian has three different forms: "1 komentaras", "2 komentarai", "10 komentarų".The GNU gettext framework popular in C programs has a solution for this (the ngettext function). I'm not familiar with Java/C#/PHP so I cannot say what is the canonical correct solution there.
Marius Gedminas
@Marius: thanks for that information. I was not aware of those Lithuanian rules. I have done a bit of international software (event made a translation software of UI texts for a large client) but have never come across such demands. Very interesting! I will not dive into it in detail here though, I just wanted to introduce the concept of picking up the texts from a resource repository, rather than just hard coding them.
Fredrik Mörk
@Fredrik: I was also surpriced the first time I heard of languages with more than two plural forms... I'm Danish and used to just two forms :-) Luckily, the ngettext function can deal with it, please see my answer somewhere else on this page.
Martin Geisler
Plenty of languages have more than two plural forms; for example, IIRC, all Slavic languages do (Russian certainly does).
Pavel Minaev
+6  A: 

Please use the ngettext function for things like this. It allows you to deal correctly with plurals in English and other languages, once and for all. You use it like this:

printf(ngettext("%d Comment", "%d Comments", $numComments), $numComments);

The ngettext function will return the first format string ("%d Comment") if there is just a single comment and the second format string ("%d Comments") if there are more. The printf function will then put the number into the string.

This might seem like a lot of work, but it is very powerful: it works with languages that have more than one plural form(!) -- they actually exist. The PHP manual gives an example for the word "window" which becomes "1 okno", "2 okna" and "5 oken" in some exotic language I don't recognize...

If you are consequent about using ngettext, then your future users from far-away countries will be very grateful to you :-)

Edit: As suggested in the comments, there is a single function to do the above:

function pluralize($num, $singleWord, $pluralWord) {
    return printf(ngettext($singleWord, $pluralWord, $num), $num);
}

By default, xgettext wont recognize this new function, but you can add it with the --keyword flag. Given a file test.php with

echo ngettext("foo", "foos", 1);
echo pluralize(2, "bar", "bars");

you can extract the strings with

xgettext --keyword=pluralize:2,3 test.php

The resulting messages.po file has entries like these:

#: test.php:7
msgid "foo"
msgid_plural "foos"
msgstr[0] ""
msgstr[1] ""

#: test.php:8
msgid "bar"
msgid_plural "bars"
msgstr[0] ""
msgstr[1] ""

The translator will fill in each plural form and with a correctly formed "Plural-Forms" line in the message catalog header, you will be able to support all languages.

Martin Geisler
it would be repetitive to use this with a printf all the time, but perhaps a wrapper function can be written which does the printf and the %d. Perhaps you could write the body of this function and update your answer? `function pluralize($num,$singleWord,$pluralWord='')`
Click Upvote
@Click Upvote: good idea. I made the function with three mandatory arguments -- the plural string is necessary so that xgettext can recognize the function call and treat it correctly.
Martin Geisler
That would be Slovak.
David Rutten
A: 

Check out the Rails inflector module. This provides a nice, centralized and configurable solution to this problem.

ndp