The Magic of Replaceable Parameters

Apart from hard-coding “Hello, world!”, another bad habit you may have picked up when you learned programming is constructing user-visible messages from parts: strings, numbers and other data, concatenated together.

For example, say that you had to show the user how many unread messages there are in a given mailbox. Let’s assume that mailboxName contains the name of the mailbox, and messageCount holds the number of unread messages. In Java, you might be tempted to whip up a user-visible message like this:

String message = "There are " + messageCount + " unread messages in mailbox '" + mailboxName + "'";

This is not the way to do it in an international application. Let’s find out why, and have a look at a better and much more future-proof way of doing it.

The user would see something like:

There are 7 unread messages in mailbox 'Inbox'

But the traditional string concatenation will cause problems sooner or later when you begin to localize your application. It has to do with the different ways languages work.

If this message had to be localized into Finnish, a good way to address the user would be with the following message:

Postilaatikossa 'Saapuneet' on 7 lukematonta viestiä

Even if you don’t speak Finnish, you may be able to figure out the problem already: the mailbox name and the message count are in a different order. Would you like to write different pieces of code to handle these situations, or would you rather use the same code for all localizations, no matter what the language? And what if your message has three or more ever-changing parts?

It doesn’t matter if you don’t know how different languages behave in this respect, because your translator does, and that is what you pay her for. But you need to make it possible for her to do a good job, and that is much more difficult if you try to use the old string concatenation method. This is why we have messages with replaceable parameters.

Still working in Java, you would put the string that you use to construct the message into your English-language properties file (just as you learned in the “Hello, world!” example), and it would look like this:

unreadMail=There are {0} unread messages in mailbox '{1}'

The parameters have ordinal numbers, and when the message is actually constructed at runtime, they will be replaced by the message count and the mailbox name. The syntax of this example is specific to Java SE, but the idea is the same in many other programming languages and libraries too.

Then, you would put this into your Finnish-language properties file:

unreadMail=Postilaatikossa '{1}' on {0} lukematonta viestiä

Notice that the mutual order of the parameters is different than in the English string. This is the magic of replaceable parameters.

At runtime, the result that you finally show your user will be constructed like this:

import java.text.MessageFormat;
import java.util.ResourceBundle;
//...
ResourceBundle bundle = ResourceBundle.getBundle("messages");
int messageCount = ...
String mailboxName = ...
Object[] parameters = { new Integer(messageCount), mailboxName };
String message = MessageFormat.format(bundle.getString("unreadMail"), parameters);

The result in message is what you would show your user. Once you provide the parameters in the array in the correct order, the result will be correct, no matter what their order is in the template.

Of course, you need to make sure that your translator knows about the syntax of replaceable parameters in messages. You may also want to localize the mailbox name.

If you want to make the message grammatically correct for all the usual cases (zero, one, or more unread messages) you will need to do a little more work. Come to our training to find out how, and to learn about other important concepts in software internationalization!

And you thought it was just a matter of having some text translated…