Introduction
Formatting user-interface messages is hard. This quick tip attempts to ease that pain by resorting to a minimal, cross-platform library for better maintainability.
Scenario
We want to display a message in our UI that indicates how many unread messages a user has.
Example: There are no unread messages for Jeff.
Example: There is one unread message for Bob.
Example: There are 4 unread messages for Joe.
The requirement is proper pluralization, as well as translations in English and Danish.
Background
Let's have a look at a naive implementation for the scenario above.
-
-
- public string GetUnreadMessagesString_Danish(int unreadMessagesCount, string user) {
- var message = "Der er";
- if (unreadMessagesCount == 0)
- {
- message += "ingen ulaeste beskedder"
- } else if (unreadMessagesCount == 1)
- {
- message += "kun 1 ulaest besked";
- } else {
- message += string.Format("{0} ulaeste beskedder", unreadMessagesCount);
- }
- message += string.Format("til {0}", user);
- return message;
- }
-
- public string GetUnreadMessagesString(int unreadMessagesCount, string user)
- {
-
- string currentLanguage = LanguageManager.GetCurrentLanguage();
- if (currentLanguage == "en")
- return GetUnreadMessagesString_English(unreadMessagesCount, user);
- else if (currentLanguage == "da")
- return GetUnreadMessagesString_Danish(unreadMessagesCount, user);
- throw new Exception("Not supported!")
- }
-
- label1.Text = GetUnreadMessagesString(2, "Jeff");
This code is:
- Hard to maintain
- Hard to read and understand
- Hard to get translated externally
We can do better.
A Better Implementation, Using MessageFormat
C, Java, PHP and JavaScript, among others, have this neat utility called MessageFormat
. .NET dioes not have one, so I wrote one myself and it is very useful.
It is open-sourced on GitHub at https://github.com/jeffijoe/messageformat.net.
Installing MessageFormat Into Your Project
MessageFormat
is available via the NuGet package manager. All you must do, is run:
Install-Package MessageFormat
From the Package Manager Console. Alternatively, you can use the Manage NuGet Packages UI for this.
Figure 1: Package Manager Console
Note: It works with Xamarin as well.
Using MessageFormat to Implement Our Solution
MessageFormat
will parse strings
and properly format them based on the input given. It does not generate code. This means we can put our actual UI string
s in external files and I strongly advise you to do this.
So, for brevity, these are our translation files:
UnreadMessages.en.txt
There {unreadMessagesCount, plural,zero {are no unread messages}one {is one unread message}other {are # unread messages}} for {user}.
UnreadMessages.da.txt
Der er {unreadMessagesCount, plural,zero {ingen ulaeste beskedder}one {kun 1 ulaest besked}other {# ulaeste beskedder}} til {user}.
Notice the format it was written in. I won't go over the specifics, since they can be found in the readme in the GitHub repository.
unreadMessagesCount
is the variable, plural
is the formatter being used, zero
, one
and other
are the parameters passed to the formatter.
Simply put, it does exactly what the previous, hard-to-maintain code did, except here there's no code, just string
s.
zero
: if the variable equals 0
one
: if the variable equals 1
other
: none of the above. The #
will be replaced by the variable's value.
Whitespace in the format is ignored. Personally, I think it makes it more readable.
This is how you would use it:
-
- var language = LanguageManager.GetCurrentLanguage();
- var str = File.ReadAllText("UnreadMessages." + language + ".txt");
-
-
-
- label1.Text = MessageFormatter.Format(str, new
- {
- unreadMessagesCount = 2,
- user = "Jeff"
- });
Points of Interest
We have now achieved:
- externalized
string
s, that can be changed without recompiling the code.
- no more custom formatting code.
Additionally, there is a select formatter, that works pretty much like a switch
statement.
This is what it looks like without MessageFormat
:
- string message = string.Empty;
- switch (gender)
- {
- case "male":
- message += "He likes"
- break;
- case "female":
- message += "She likes"
- break;
- default:
- message += "They like"
- }
- message += " this stuff";
And with MessageFormat
:
- var str = "{gender, select, male {He likes} female {She likes} other {They like}} this stuff.";
- var message = MessageFormatter.Format(str, new
- {
- gender = "male"
- });
You can read all about it in the project's README.