Formatting Messages Handling plurality adequately

Published: March 1st, 2016, Updated: Some days ago

Once in a while I stumble upon code to format message texts in C#, either mine or from developers on Stack Overflow. It looks like this:

string messageTemplate;
if (_itemCount == _persistedItemCount)
    messageTemplate = Resources.ProtocolMessageAllItemsAreFiled;
else if (_persistedItemCount == 0)
    messageTemplate = Resources.ProtocolMessageNoItemsAreFiled;
else
    messageTemplate = Resources.ProtocolMessageSomeItemsAreFiled;
return String.Format(messageTemplate, _persistedItemCount, _itemCount);

Developers get quite — let's say — "creative" to format readable message texts for the user, especially when it comes to plurality. Source code like this is ok, but we can do better.

I cannot decide which code provided with the answers of the Stack Overflow page I dislike most, so I spare you the sight of it. (I should mention that some chose to solve the problem the way I did or quite similar.) Many of the offered solutions don't use resources, which will lead to not-localizable applications. Or the code handles plurality for exactly one language: English. French and other languages often request adjustments at more than one or two parts of the sentence. So every source code using just two localization resources as parts of a sentence putting a number or date in between might fail to be adequately localizable.

I don't like my solution presented above neither. If the messages get more complex the code to format them proliferates. All solutions I saw were not elegant, but I know from my past how an elegant solution looks like.

Recently I got so tired of this that I decided to implement my C# version of the Java class MessageFormat and its mighty brother ChoiceFormat. A lot of Java developers know MessageFormat. Unfortunately not many know ChoiceFormat, either because they don't use it directly or they don't use it at all. What do these two classes do? Have a look:

String messageText =
MessageFormat.format("There {0,choice,0#are no files|1#is one file|1<are {0,number,integer} files}.", numberOfFiles);

Of course, you would get the real message format from a resource file. MessageFormat parses the format. If it discovers the embedded choice format pattern, it converts this part of the message with the help of ChoiceFormat. ChoiceFormat examines the value in the argument list (numberOfFiles) and renders the appropriate text. That's it. Depending on the numberOfFiles messageText will contain the following:

numberOfFiles messageText
0 There are no files.
1 There is one file.
1234 There are 1,234 files.

The last example, of course, depends on the current locale.

My own MessageFormat was not too complicated to implement. I only adjusted the format pattern for C#, so the example above reads as follows:

string messageText =
MessageFormat.Format("There {0:choice,0#are no files|1#is one file|1<are {{0}} files}.", numberOfFiles);

If you want to do it as well, beware of the double opening and closing curly braces ({{ and }}), which also have special meaning in String.Format. Also consider that you have to pass over the result of your formatter to String.Format. If you want to be as accurate as me, you also have to consider that a choice format pattern might contain another choice format pattern.