107

Many times, when generating messages to show to the user, the message will contain a number of something that I want to inform the customer about.

I'll give an example: The customer has selected a number of items from 1 and up, and has clicked delete. Now I want to give a confirmation message to the customer, and I want to mention the number of items he has selected to minimize the chance of him making a mistake by selecting a bunch of items and clicking delete when he only wants to delete one of them.

One way is to make the generic message like this:

int noofitemsselected = SomeFunction();
string message = "You have selected " + noofitemsselected + " item(s). Are you sure you want to delete it/them?";

The "problem" here is the case where noofitemselected is 1, and we have to write item and it instead of items and them.

My normal solution will be something like this

int noofitemsselected = SomeFunction();
string message = "You have selected " + noofitemsselected + " " + (noofitemsselected==1?"item" : "items") + ". Are you sure you want to delete " + (noofitemsselected==1?"it" : "them") + "?";

This gets quite long and quite nasty really fast if there are many references to the numbers plurality inside the code, and the actual message gets hard to read.

So my questions is simply. Are there any better ways of generating messages like this?

EDIT

I see a lot of persons has got very hung up in the case that I mentioned that the message should be displayed inside a message box, and has simply given an answer of how to avoid using the message box at all, and that is all good.

But remember that the problem of pluralization also apply to texts other places in the program in addition to message boxes. For example, a label alongside a grid displaying the number of lines selected in the grid will have the same problem regarding pluralization.

So this basically apply to most text that is outputted in some way from programs, and then the solution is not as simple as to just change the program to not output text anymore :)

Øyvind Bråthen
  • 59,338
  • 27
  • 124
  • 151
  • 6
    @0xA3: I don't really know if every language has a pluralization that is as easily expressed as "item(s)". – Jens Nov 23 '10 at 09:02
  • 5
    You should probably be thinking about localisation, too. This issue can get a whole lot worse. – Alex Brown Nov 23 '10 at 11:42
  • 4
    @Jens: They usually don't, in least convenient ways possible. Some languages have e.g. different pluralizations for 2-4 than for 5-infinity, all that depending on gender. "Natural Languages: Localization Madness! Coming soon to a computer near you!" – Piskvor left the building Nov 23 '10 at 12:59
  • 29
    IMO, nothing makes programmers look lazier to users than bad plurality. – Greg Nov 23 '10 at 13:39
  • If you're localizing your code, GNU gettext has techniques to handle plurals naturally and get it right in many languages. – Ken Bloom Nov 24 '10 at 01:49
  • 1
    @Greg WRONG! nothing makes programmers look lazier then not putting a neon sign near the button that only they are useing:"What could they make it stand out a little more? whats the problem?" – none Nov 24 '10 at 06:21
  • 3
    @none: What? What button? None of that made any sense. – Cody Gray - on strike Nov 24 '10 at 13:05
  • 4
    @Øyvind: +1 for your Edit. It seems many of the "answerers" here are more intent on proving that your question is wrong, rather than offering a solution that's right. Anti-solutions, if you will. – mwolfe02 Nov 24 '10 at 15:16

25 Answers25

94

You can avoid all of this messy plurality by just deleting the items without any message and giving the user a really good Undo facility. Users never read anything. You should build a good Undo facility as part of your program anyway.

You actually get 2 benefits when you createe a comprehensive Undo facility. The first benefit makes the user's life easier by allowing him/her to reverse mistakes and minimise reading. The second benefit is that your app is reflecting real life by allowing the reversal of non-trivial workflow (not just mistakes).

I once wrote an app without using a single dialog or confirmation message. It took some serious thinking and was significantly harder to implement than using confirmation-type messages. But the end result was rather nice to use according to its end-users.

HTTP 410
  • 17,300
  • 12
  • 76
  • 127
  • 12
    For some reason, I really feel like I agree with this and should upvote it. – Cody Gray - on strike Nov 23 '10 at 09:14
  • @Cody - I can't understand why :D – Øyvind Bråthen Nov 23 '10 at 10:48
  • @Cody: at least we used different links :-) – HTTP 410 Nov 23 '10 at 11:48
  • I wonder how practical this in web applications? nevertheless I love it ! – Illuminati Nov 23 '10 at 16:47
  • As UIs go, having a confirmation message on delete is expected in almost all situations. – webbiedave Nov 23 '10 at 17:42
  • 4
    @Øyvind, Here's why: Messages asking the user for verification are expensive to the user, especially when they are presented in the form of a modal dialog box that *stops everything* until the user answers the dialog. This annoys many users, to the point where they begin just clicking any button to get past the dialog box (how many installers have you just clicked *next, next, next?*). Consequently, it is safer, and there is much less user friction, if you just do a soft delete, and provide an undo capability. Gmail uses this technique to great effect. – Robert Harvey Nov 23 '10 at 17:43
  • 1
    @Robert- my comment was directed at Cody, since this answer was basically a copy of his. Regarding your point, I partially agree. You shouldn't spam the user with modal dialog boxes, but when performing a delete of critical content, I feel that displaying a dialog is by far the best, and most polite, way to treat the user. – Øyvind Bråthen Nov 23 '10 at 17:56
  • 2
    @webbiedave - on the contrary, if the program has an undo on delete that is obviously shown after delete, having a confirmation message simply gets in the way and is often *not* done. – justkt Nov 23 '10 at 18:53
  • @justk: How dare you contradict me! :) I only meant that it is much more common to see a confirm delete than an undo delete. I guess it depends on *what* is being deleted and how its deletion (whether temporary or not) affects other parts of the application. – webbiedave Nov 23 '10 at 19:19
  • 5
    Debateable, but in any case, the same issue will come up in cases other than a confirmation message on a delete, so this answer is really tangential to the question. – Jay Nov 23 '10 at 22:24
  • 50
    i upvoted this without reading it. i can always undo it later – Steven A. Lowe Nov 23 '10 at 22:28
  • 1
    The only problem is knowing how long/when/where to show the undo message. The "undo" idea is very great in principle, but is notoriously difficult to implement well. What if the user navigates away from the page after deleting something and still expects to be able to undo their delete? Gmail does it quite well, but keep in mind that nothing is actually "deleted" because it just goes to the trash folder, so the user can still get their email back even if they lose the "undo" message. – Lotus Notes Nov 23 '10 at 22:42
  • 1
    @Lotus: Exactly. There is a trash can you can go to to retrieve the deletion, any time you want to. BTW your user name is ironic; Worst UI Ever. – Robert Harvey Nov 23 '10 at 23:47
  • @Lotus: I usually find myself needing the undo feature immediately after the action. I very rarely continue with my tasks and then several minutes later suddenly think "Oh, I didn't mean to do that!" YMMV of course, but I only expect to be able to undo the last action. Of course, if the site provides multiple level undo history, that's even better. – Duncan Nov 24 '10 at 02:35
  • 6
    @Robert Harvey: Probably apocryphal, but many years back I read that Lotus had shipped 40,000 units of Lotus Notes for the Mac and 60,000 were returned. The interface was so bad that even the people who had pirated it sent it back. – Duncan Nov 24 '10 at 02:38
  • 2
    for some processes you cannot create undo, for legal reasons. – none Nov 24 '10 at 06:23
  • 1
    @none: Or for complexity reasons. What if a transaction has side effects, or can interact with other transactions? To take a simple example: Enter a deposit to the customer's bank account. Then the system processes a withdrawal. Can you now undo the deposit? What if without the deposit, there are insufficient funds for the withdrawal? – Jay Nov 24 '10 at 14:36
  • @Jay: Then you reverse the withdrawal as well. I work in banking, and this happens regularly. Of course, implementing UNDO thoroughly can make your business workflow significantly more complex. That's the price you pay if you wish to reflect the real world and make the end-user's life nicer. – HTTP 410 Nov 24 '10 at 17:55
  • @RoadWarrior: How do you reverse a withdrawal? Do you run after the customer and take the money back? – Jay Nov 24 '10 at 20:27
  • @Jay: I'm in investment banking rather than retail, so we don't usually have that problem (withdrawal goes to another bank or a company rather than an individual). But yes, in a retail context you ask the customer for *your* money back. If that doesn't work, you hit the customer's credit record with a debt. And so on up the scale. – HTTP 410 Nov 24 '10 at 21:03
  • 1
    The question is **not** about how to handle the selection/deletion scenario. It is about how to handle the message. It could have been some other, more unavoidable message in there. Although 'undo' is good advice, when left alone, it isn't a proper answer for this question. – edgerunner May 07 '11 at 19:56
  • @edgerunner: I'm saying that a good way of handling this type of question is not to ask it. In 99.9% of cases, that's the right approach. Admittedly my answer doesn't deal with the tiny percentage of cases where it really is necessary to ask the question. – HTTP 410 May 10 '11 at 18:38
  • @RoadWarrior, undo functionality cannot always compensate for poor copywriting. What Øyvind is trying to do is use proper language for his text. Undo may be good advice for his example case, but there are loads of other cases where undo is pointless but a message in proper language would just do it. – edgerunner May 10 '11 at 21:02
  • @edgerunner, you say "loads" and I say "tiny". Do you have some good examples? – HTTP 410 May 11 '11 at 11:34
  • sure, right on this page. " asked 5 months ago, viewed 2,615 times, active 4 months ago ". Please notice that I'm not talking about "delete" cases but any case where a number is presented. – edgerunner May 11 '11 at 12:26
  • @edgerunner, the original question was in the context of message dialogs, which is what I answered. It was only later that the question was edited to refer to all text shown on the screen. It's hard to edit every answer to keep up with all question edits. – HTTP 410 May 12 '11 at 07:54
  • @RoadWarrior, Sorry that I voted down your answer, but I could not keep up with all of the comments to realize early enough that your answer was for an earlier version of the question. – migu Jan 18 '12 at 09:34
53

If there is ever any chance, no matter how small, that this app will need to be translated to other languages then both are wrong. The correct way of doing this is:

string message = ( noofitemsselected==1 ?
  "You have selected " + noofitemsselected + " item. Are you sure you want to delete it?":
  "You have selected " + noofitemsselected + " items. Are you sure you want to delete them?"
);

This is because different languages handle plurality differently. Some like Malay don't even have syntactic plurals so the strings would generally be identical. Separating the two strings makes it easier to support other languages later on.

Otherwise if this app is meant to be consumed by the general public and is supposed to be user friendly then the second method is preferable. Sorry but I don't really know a shorter way of doing this.

If this app is meant to be consumed only internally by your company then do the shortcut "item(s)" thing. You don't really have to impress anybody when writing enterprisy code. But I'd advise against doing this for publicly consumed app because this gives the impression that the programmer is lazy and thus lower their opinion of the quality of the app. Trust me, small things like this matter.

slebetman
  • 109,858
  • 19
  • 140
  • 171
  • 35
    While I agree with the premise, You are ignoring languages that have more than two degrees of plurality. (Take Russian for example. three different ways of saying depending on if its 1, <5, or >= 5 and even that depends on what exactly you are talking about). Basically I'm saying you need a stronger conditional statement and not just a ternary operator. – crasic Nov 23 '10 at 09:20
  • I have to agree with Cody here. A really good answer. +1, but I'll wait some time to make it the accepted answer in case any other answers show up here. – Øyvind Bråthen Nov 23 '10 at 10:18
  • 4
    You can even optimize away the `noofitemsselected` in this example for the first option; since you know it is 1. – default Nov 23 '10 at 13:31
  • 16
    While we're at it, Hebrew can have different pluralities for 1, 2, 3-10, and 11+. – Joel Spolsky Nov 23 '10 at 14:38
  • 2
    If translation is an issue, your example code would also be suboptimal. The ordering you assume about numbers " number + item " may not hold in a different language. The solution would be to use placeholders with string.Format, as suggested by @Dan Puzey – hayalci Nov 23 '10 at 15:14
  • Actually I don't know C# at all so I don't know about string.Format. Personally I use gettext or msgcat (which is gettext compatible) to do this and most implementation of gettext has placeholders in the form of `%1 %2` etc. But I don't know how gettext works in C# so I can't give example code. Sorry.. – slebetman Nov 23 '10 at 16:34
  • In Romanian, there are different pluralities for <20 and and for >=20. – True Soft Nov 23 '10 at 18:09
  • 3
    @Joel, Hebrew can have that many different pluralities? לא ידעתי (I didn't know)! Modern Hebrew is like English as far as plurals go (though *ancient* Hebrew also had a dual form.) – Ken Bloom Nov 24 '10 at 01:52
  • @Ken, modern Hebrew uses the dual form for things that go in pairs (shoes, ears, etc). It also distinguishes between <=10 and >10 for measurements (asara sheqalim vs. esrim sheqel). – Joel Spolsky Nov 24 '10 at 02:11
  • 3
    In English, zero gets treated like a plural (0 items, 2 items); what happens with zero in Hebrew, Russian, Romanian? Is there a simple way to identify the ranges? I'm guessing that the information has to be determined per language, so any systems based on message files has to deal with varying amounts of plurality in a set of messages. Ouch! Setting up the translations would be tricky too - different numbers of messages are needed for different languages. – Jonathan Leffler Nov 24 '10 at 06:00
  • @Joel, that's right. In Hebrew you can say **zero items** and **two items** but you can't say **one items**: you must say **one item**. – Zippo Dec 02 '10 at 18:55
  • A catch to giving completely separate singular and plural versions of the message is the same problem you always have when you write something twice: twice as much room for errors, twice as hard to test, and twice as hard to maintain. What if you mis-spell a word in one version? Testers may miss it because they only tested the other version. What if the message changes? The person making the change must be sure to make the change twice. Etc. And sure, for one message, the extra work is not a big deal. But odds are you have many such messages in your app. – Jay Oct 06 '11 at 13:56
  • @Jay: On the other hand, considering all the language issues above it really is the only reasonable way to do it. Unless you can come up with a better system. Unfortunate side effect of how languages work in the real world. – slebetman Oct 06 '11 at 19:53
  • regardless of language/implementation, there are well documented approaches to handling the plural rules of languages. [e.g. Gettext](http://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html). One flaw is that the _source_ language can only have two forms, although the target language (translation) can have many and is worked out by an equation based on the quantity. [List of equations here](http://docs.translatehouse.org/projects/localization-guide/en/latest/l10n/pluralforms.html?id=l10n/pluralforms) – Tim May 14 '13 at 15:34
39

How about just:

string message = "Are you sure you want to delete " + noofitemsselected + " item(s)?"

That way, you eliminate the number agreement difficulties, and end up with an even shorter, more to-the-point error message for the user as a bonus. We all know users don't read error messages anyway. The shorter they are, the more likely they are to at least glance at the text.

Or, armed with this knowledge that users don't read error messages, you could approach this a different way. Skip the confirmation message altogether, and just provide an undo feature that Just Works, regardless of what was deleted. Most users are already accustomed to undoing an operation when they notice it was not what they wanted, and are likely to find this behavior more natural than having to deal with another annoying pop-up.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • 1
    Good answer. However, it was requested by the customer in this case to provide error messages for many many cases because he felt it was the best approach. But changing the text can at least minimize the problem and make it less messy. – Øyvind Bråthen Nov 23 '10 at 09:44
  • @Øyvind: Fair enough. Since you have to follow the customer, I'd either take the first approach I proposed or use a Resources file with a handler function. I just thought it was worth pointing out an alternative because it's so easily forgotten. Heck, I didn't even think of it until after I submitted my first response. – Cody Gray - on strike Nov 23 '10 at 09:49
  • Alternatives are most welcome. That's why I gave it a +1 also :) – Øyvind Bråthen Nov 23 '10 at 09:53
  • 1
    +1 for not trying to solve an unsolvable problem. And remember Steve Krug's law: Remove half the words. Then do it again. I use: "Delete {x} item(s)?" – Serge Wautier Sep 07 '11 at 07:52
20

What about what Java has had for years: java.text.MessageFormat and ChoiceFormat? See http://download.oracle.com/javase/1.4.2/docs/api/java/text/MessageFormat.html for more information.

MessageFormat form = new MessageFormat("The disk \"{1}\" contains {0}.");
form.applyPattern(
   "There {0,choice,0#are no files|1#is one file|1<are {0,number,integer} files}.");

Object[] testArgs = {new Long(12373), "MyDisk"};

System.out.println(form.format(testArgs));

// output, with different testArgs
output: The disk "MyDisk" are no files.
output: The disk "MyDisk" is one file.
output: The disk "MyDisk" are 1,273 files.

In your case you want something somewhat simpler:

MessageFormat form = new MessageFormat("Are you sure you want to delete {0,choice,1#one item,1<{0,number.integer} files}?");

The advantage of this approach is that it works well with the i18n bundles, and you can provide translations properly for languages (like Japanese) that have no concept of plural or singular words.

Berin Loritsch
  • 11,400
  • 4
  • 30
  • 57
  • 1
    This approach works well for handling plurality but does not handle gender, sadly. You need a different string for each kind of thing that can be deleted. – Mr. Shiny and New 安宇 Nov 23 '10 at 17:50
  • If you know the noun, you can fashion the sentences to work with the proper adjective. But yes, having studied classical Greek, you have a different form of the word based on masculine, feminine, or neuter nouns (adjective takes on the form of the noun it modifies), and the form differs based on its function in the sentence (subject, object, etc.). We can always come up with cases where one solution works and another doesn't. This is the way the Java folks attempted to solve the problem. You still have to be careful with the way you form the message formats. – Berin Loritsch Nov 23 '10 at 20:43
  • You can always feed the Java solution two integers, where the second one indicates gender. (But in reality, gender agreement is usually less of a concern simply because the use case of fitting different objects into a message is less frequent than fitting different numbers of the same object into a message) – Ken Bloom Nov 24 '10 at 01:55
12

I'd go with not hardcoding the message, but providing two messages in an seperate Resource file. Like

string DELETE_SINGLE = "You have selected {0} item. Are you sure you want to delete it?";
string DELETE_MULTI = "You have selected {0} items. Are you sure you want to delete them?";

and then feeding them into String.Format like

if(noofitemsselected == 1)
    messageTemplate = MessageResources.DELETE_SINGLE;
else
    messageTemplate = MessageResources.DELETE_MULTI;

string message = String.Format(messageTemplate, noofitemsselected)

I think that this approach is easier to localize and maintain. All UI messages would be at a single locaion.

Jens
  • 25,229
  • 9
  • 75
  • 117
  • +1, I like this approach. Although, you would need to keep track of the resource name equivalents. – default Nov 23 '10 at 13:36
11

You can sidestep the issue entirely by phrasing the message differently.

string message = "The number of selected items is " + noofitemsselected + ". Are you sure you want to delete everything in this selection?";
gdejohn
  • 7,451
  • 1
  • 33
  • 49
10

The first thing I'd suggest is: use string.Format. That allows you to do something like this:

int numOfItems = GetNumOfItems();
string msgTemplate;
msgTemplate = numOfItems == 1 ? "You selected only {0} item." : "Wow, you selected {0} items!";
string msg = string.Format(msgTemplate, numOfItems);

Further, in WPF apps, I've seen systems where a resource string would be pipe-delimited to have two messages: a singular and a plural message (or a zero/single/many message, even). A custom converter could then be used to parse this resource and use the relevant (formatted) string, so your Xaml is something like this:

<TextBlock Text="{Binding numOfItems, Converter={StaticResource c:NumericMessageFormatter}, ConverterParameter={StaticResource s:SuitableMessageTemplate}}" />
Dan Puzey
  • 33,626
  • 4
  • 73
  • 96
7

For English, plenty of answers above. For other languages it is more difficult, as plurals depend on the gender of the noun and the word ending. Some examples in French:

Regular masculine:

Vous avez choisi 1 compte. Voulez-vous vraiment le supprimer.
Vous avez choisi 2 comptes. Voulez-vous vraiment les supprimer.

Regular feminine

Vous avez choisi 1 table. Voulez-vous vraiment la supprimer.
Vous avez choisi 2 tables. Voulez-vous vraiment les supprimer.

Irregular masculine (finishes with 's')

Vous avez choisi 1 pays. Voulez-vous vraiment le supprimer.
Vous avez choisi 2 pays. Voulez-vous vraiment les supprimer?

The same problem exists in most Latin languages and gets worse in German or Russian, where there are 3 genders (maculine, feminine and neuter).

You'll need to take care if your objective is to handle more than just English.

smirkingman
  • 6,167
  • 4
  • 34
  • 47
  • At least in my case, this is not really a problem. For each text I want to insert the number, I will know what the noun is when generating the string. But to make a more general solution I agree with you, that to make it work for "all" languages might be a very hard, if not impossible task. – Øyvind Bråthen Nov 23 '10 at 12:50
  • 1
    Or take Polish, where the plural form of the noun used is different depending on the number! :/ – UpTheCreek Jan 11 '11 at 10:36
5

To be able to have pluralized messages which will be possible to localize properly, my opinion is that it would be wise to first create a layer of indirection between the number and a message.

For example, use a constant of some sort to specify which message you want to display. Fetch the message using some function that will hide the implementation details.

get_message(DELETE_WARNING, quantity)

Next, create a dictionary that holds the possible messages and variations, and make variations know when they should be used.

DELETE_WARNING = {
   1: 'Are you sure you want to delete %s item',
   >1: 'Are you sure you want to delete %s items'
   >5: 'My language has special plural above five, do you wish to delete it?'
}

Now you could simply find the key that corresponds to the quantity and interpolate the value of the quantity with that message.

This oversimplified and naive example, but I don't really see any other sane way to do this and be able to provide good support for L10N and I18N.

Davor Lucic
  • 28,970
  • 8
  • 66
  • 76
5

You'll have to translate the function below from VBA to C#, but your usage would change to:

int noofitemsselected = SomeFunction();
string message = Pluralize("You have selected # item[s]. Are you sure you want to delete [it/them]?", noofitemsselected);

I have a VBA function that I use in MS Access to do exactly what you are talking about. I know I'll get hacked to pieces for posting VBA, but here goes anyway. The algorithm should be apparent from the comments:

'---------------------------------------------------------------------------------------'
' Procedure : Pluralize'
' Purpose   : Formats an English phrase to make verbs agree in number.'
' Usage     : Msg = "There [is/are] # record[s].  [It/They] consist[s/] of # part[y/ies] each."'
'             Pluralize(Msg, 1) --> "There is 1 record.  It consists of 1 party each."'
'             Pluralize(Msg, 6) --> "There are 6 records.  They consist of 6 parties each."'
'---------------------------------------------------------------------------------------'
''
Function Pluralize(Text As String, Num As Variant, Optional NumToken As String = "#")
Const OpeningBracket = "\["
Const ClosingBracket = "\]"
Const DividingSlash = "/"
Const CharGroup = "([^\]]*)"  'Group of 0 or more characters not equal to closing bracket'
Dim IsPlural As Boolean, Msg As String, Pattern As String

    On Error GoTo Err_Pluralize

    If IsNumeric(Num) Then
        IsPlural = (Num <> 1)
    End If

    Msg = Text

    'Replace the number token with the actual number'
    Msg = Replace(Msg, NumToken, Num)

    'Replace [y/ies] style references'
    Pattern = OpeningBracket & CharGroup & DividingSlash & CharGroup & ClosingBracket
    Msg = RegExReplace(Pattern, Msg, "$" & IIf(IsPlural, 2, 1))

    'Replace [s] style references'
    Pattern = OpeningBracket & CharGroup & ClosingBracket
    Msg = RegExReplace(Pattern, Msg, IIf(IsPlural, "$1", ""))

    'Return the modified message'    
    Pluralize = Msg
End Function

Function RegExReplace(SearchPattern As String, _
                      TextToSearch As String, _
                      ReplacePattern As String) As String
Dim RE As Object

    Set RE = CreateObject("vbscript.regexp")
    With RE
        .MultiLine = False
        .Global = True
        .IgnoreCase = False
        .Pattern = SearchPattern
    End With

    RegExReplace = RE.Replace(TextToSearch, ReplacePattern)
End Function

The usage got cut off a bit in the code comments above, so I'll repeat it here:

Msg = "There [is/are] # record[s]. [It/They] consist[s/] of # part[y/ies] each."

Pluralize(Msg, 1) --> "There is 1 record.  It consists of 1 party each."
Pluralize(Msg, 6) --> "There are 6 records.  They consist of 6 parties each."

Yes, this solution ignores languages that are not English. Whether that matters depends on your requirements.

mwolfe02
  • 23,787
  • 9
  • 91
  • 161
4

You could generate the plural automatically, see eg. plural generator.

For plural generating rules see wikipedia

string msg = "Do you want to delete " + numItems + GetPlural(" item", numItems) + "?";
thumbmunkeys
  • 20,606
  • 8
  • 62
  • 110
  • I like using this method if the application is not internationalised. Puralising results in a more polished application. – cspolton Nov 23 '10 at 08:54
  • 1
    Its really hard to internationalise this, and it often fails in English e.g. "You have orders + numMice + GetPlural(" mouse",numMice)" – James Anderson Nov 23 '10 at 09:08
  • @James Anderson: Now, that's not the best example grammatically is it ;-) Don't fall into the trap of thinking that of using a GetPural method is the *only* method that you're allowed to use. – cspolton Nov 23 '10 at 10:21
  • coupled with a few more functions to handle a few more corner cases, I think this is a very good solution, not shorter though. – lalli Nov 23 '10 at 11:26
4

How about a more generic way. Avoid pluralization in the second sentence:

Number of selected items to be deleted: noofitemsselected.
Are you sure?

I find out that doing it this way puts the number at the end of the line which is really easy to spot. This solution would work with the same logic in any language.

Danosaure
  • 3,578
  • 4
  • 26
  • 41
  • "Selected items to be deleted:" doesn't sound right, the sentence indicates a list of names, but a number is printed. Even if changed to "Number of items to be deleted:", still "items" is awkward. – lalli Nov 23 '10 at 11:22
  • @lalli: Yes, the wording is not perfect, and none of provided solutions sound perfect. I'm only suggesting to put it in a form like label and value. You are right about adding `Number` (i was answering while rocking my baby to sleep). – Danosaure Nov 23 '10 at 16:02
4

My general approach is to write a "single/plural function", like this:

public static string noun(int n, string single, string plural)
{
  if (n==1)
    return single;
  else
    return plural;
}

Then in the body of the message I call this function:

string message="Congratulations! You have won "+n+" "+noun(n, "foobar", "foobars")+"!";

This isn't a whole lot better, but at least it, (a) puts the decision in a function and so unclutters the code a little, and (b) is flexible enough to handle irregular plurals. i.e. it's easy enough to say noun(n, "child", "children") and the like.

Of course this only works for English, but the concept is readily extensible to languages with more complex endings.

It occurs to me that you could make the last parameter optional for the easy case:

public static string noun(int n, string single, string plural=null)
{
  if (n==1)
    return single;
  else if (plural==null)
    return single+"s";
  else
    return plural;
}
Jay
  • 26,876
  • 10
  • 61
  • 112
  • 1
    I like your last part where you use a default value to "unclutterize" the code in case of the default operation of adding an *s* to pluralize the noun. – Øyvind Bråthen Nov 23 '10 at 23:21
4

Internationalization

I assume you want internationalization support, in which case different languages have different patterns for plurals (e.g. a special plural form for 2 of something, or more complicated languages like Polish), and you can't rely on applying some simple pattern to your string to fix it.

You can use GNU Gettext's ngettext function and provide two English messages in your source code. Gettext will provide the infrastructure to choose from other (potentially more) messages when translated into other languages. See http://www.gnu.org/software/hello/manual/gettext/Plural-forms.html for a full description of GNU gettext's plural support.

GNU Gettext is under the LGPL. ngettext is named GettextResourceManager.GetPluralString in the C# port of Gettext.

(If you don't need localization support, and don't want to use Gettext right away, then write your own function that does this for English, and pass two full messages to it, that way if you need l10n later, you can add by rewriting a single function.)

Ken Bloom
  • 57,498
  • 14
  • 111
  • 168
3

How about to write function like

string GetOutputMessage(int count, string oneItemMsg, string multiItemMsg)
{
 return string.Format("{0} {1}", count, count > 1 ? multiItemMsg : oneItemMsg);
}

.. and use it whenever you need?

string message = "You have selected " + GetOutputMessage(noofitemsselected,"item","items") + ". Are you sure you want to delete it/them?";
Pavel Morshenyuk
  • 10,891
  • 4
  • 32
  • 38
3

For the first problem , I mean Pluralize, you can use Inflector.

And for the second, you can use a string representation extension with a name such as ToPronounString.

Yogesh
  • 14,498
  • 6
  • 44
  • 69
3

I had this exact same question posed to me yesterday by a member of our team.

Since it came up again here on StackOverflow I figured the universe was telling me to have a bash at producing a decent solution.

I've quickly put something together and it's by no means perfect however it might be of use or spark some discussion/development.

This code is based on the idea that there can be 3 messages. One for zero items, one for one item and one for more than one item which follow the following structure:

singlePropertyName
singlePropertyName_Zero
singlePropertyName_Plural

I've created an internal class to test with in order to mimick the resource class. I haven't tested this using an actual resource file yet so I'm yet to see the full result.

Here's the code (currently i've included some generics where I know I could have specified the third param simply as a Type and also the second param is a string, I think there's a way to combine these two parameters into something better but I'll come back to that when I have a spare moment.

    public static string GetMessage<T>(int count, string resourceSingularName, T resourceType) where T : Type
{
    var resourcePluralName = resourceSingularName + "_Plural";
    var resourceZeroName = resourceSingularName + "_Zero";
    string resource = string.Empty;
    if(count == 0)
    {
        resource = resourceZeroName;
    }
    else{
        resource = (count <= 1)? resourceSingularName : resourcePluralName;
    }
    var x = resourceType.GetProperty(resource).GetValue(Activator.CreateInstance(resourceType),null);

    return x.ToString();
}

Test resource class:

internal class TestMessenger
{
    public string Tester{get{
    return "Hello World of one";}}
    public string Tester_Zero{get{
    return "Hello no world";}}
    public string Tester_Plural{get{
    return "Hello Worlds";}}
}

and my quick executing method

void Main()
{
    var message = GetMessage(56, "Tester",typeof(TestMessenger));
    message.Dump();
}
Jamie Dixon
  • 53,019
  • 19
  • 125
  • 162
  • If you want this to really be complete, you should also add a message for `singlePropertyName_All` to make it even more obvious. – Danosaure Nov 23 '10 at 16:05
2

From my point of view, your first solution is the most suited one. Why I say that is, in case you need the application to support multiple languages, the second option can be painstaking. With the fist approach it is easy to localize the text without much effort.

Illuminati
  • 4,539
  • 2
  • 35
  • 55
2

You could go for a more generic message like 'Are you sure you want to delete the selected item(s)'.

Peter
  • 13,733
  • 11
  • 75
  • 122
  • 3
    This certainly works, but I think the asker's idea of showing the number of items about to be deleted at a glance is a good one. More than once, I've tried to delete a file from my desktop and accidentally deleted more than that one file. – Cody Gray - on strike Nov 23 '10 at 08:51
2

I depends on how nice a message you want to have. From easiest to hardest:

  1. Re-write your error message to avoid pluralization. Not as nice for your user, but faster.

  2. Use more general language but still include the number(s).

  3. Use a "pluralization" and inflector system ala Rails, so you can say pluralize(5,'bunch') and get 5 bunches. Rails has a good pattern for this.

  4. For internationalization, you need to look at what Java provides. That will support a wide variety of languages, including those that have different forms of adjectives with 2 or 3 items. The "s" solution is very English centric.

Which option you go with depends on your product goals. - ndp

ndp
  • 21,546
  • 5
  • 36
  • 52
2

Why would you want to present a message the users can actually understand? It goes against 40 years of programing history. Nooooo, we have a good thing going on, don't spoil it with understandable messages.


(j/k)

John Alexiou
  • 28,472
  • 11
  • 77
  • 133
  • +1: good to see some humour inside here also. But even if you are kidding I understand your underlying point. Many many programs created during the years have extremely bad messages. Usually because they get way to technical for a normal user to understand anyway. – Øyvind Bråthen Nov 24 '10 at 06:24
  • Good point. I always love it when I get a message like "Error 494-B on user input" -- it adds that extra little mystery about just what's wrong that makes life so much more interesting. I recently got an error trying to create a password that simply said, "Password does not meet security requirements". No hint just what requirement I wasn't meeting. My password included both upper and lower case letters and several digits. I tried adding a secial character. Still no. It finally passed when I DELETED a character. I think they had a rule that you can't have the same character twice in a row. – Jay Nov 24 '10 at 14:26
2

Do it like it's done in World of Warcraft:

BILLING_NAG_WARNING = "Your play time expires in %d |4minute:minutes;";
Andreas Bonini
  • 44,018
  • 30
  • 122
  • 156
0

It gets a little bit shorter with

string message = "Are you sure you want to delete " + noofitemsselected + " item" + (noofitemsselected>1 ? "s" : "") + "?";
Jonas Elfström
  • 30,834
  • 6
  • 70
  • 106
0

One approach I haven't seen mentioned would be the use of a substitution/select tag (e.g. something like "You are about to squash {0} [?i({0}=1):/cactus/cacti/]". (in other words, have a format-like expression specify the substitution based upon whether argument zero, taken as an integer, equals 1). I've seen such tags used in the days before .net; I'm not aware of any standard for them in .net, nor do I know the best way to format them.

supercat
  • 77,689
  • 9
  • 166
  • 211
0

I would think out of the box for a minute, all of the suggestions here are either do the pluralization (and worry about more than 1 level of pluralization, gender, etc) or not use it at all and provide a nice undo.

I would go the non lingual way and use visual queues for that. e.g. imagine an Iphone app you select items by wiping your finger. before deleting them using the master delete button, it will "shake" the selected items and show you a question mark titled box with a V (ok) or X (cancel) buttons...

Or, in the 3D world of Kinekt / Move / Wii - imagine selecting the files, moving your hand to the delete button and be told to move your hand above your head to confirm (using the same visual symbols as I mentioned before. e.g. instead of asking you delete 3 files? it will show you 3 files with a hovering half transparent red X on and tell you to do something to confirm.

Eran Medan
  • 44,555
  • 61
  • 184
  • 276