The Problem

I recently came across this post on Twitter:

Any tips for Localization? Variable placement @”xxxx %@, xxxx %@” might translate differently in other languages due to prepostion placement?

The poster is wondering how you localize the placement of data when the formatted string hardcodes the location of the data in the string. For example, some languages might say “xx Dollars” for a monetary amount where xx is some variable, while others might say “Pounds xx”, placing the label before the amount.

Simple String Formatting

The normal way to format a string in Obj-C uses the NSString class method stringWithFormat:

[NSString stringWithFormat:@"%d Dollars", 20];

This will give us the string “20 Dollars”. We want to localize our string so that Dollars can be replaced with Pounds in the U.K:

NSString *dollars = [NSString stringWithFormat:@"%d %@", 20,
NSLocalizedString(@"Dollars", @"")];

Our formatted string now has placeholders for the amount and the label. We’re creating the label using the NSLocalizedString macro:

NSLocalizedString(key, comment)

The macro takes a key and a comment. The key corresponds to an entry in our application’s Localizable.strings file. This file is just a list of key-value pairs separated by semicolons. Your application contains many copies of this file stored in separate folders, one for each language into which your application has been translated. An entry is as simple as:

"Dollars" = "Dollars";

Our entry for Pounds uses the same key but a different value:

"Dollars" = "Pounds";

Now, when our user opens up the U.K. version of the app, the text will show up as “20 Pounds”.

Formatting Localized Strings

The trouble with our use case is that in this fictional U.K. the word Pounds appears before the amount. It isn’t “20 Pounds”, it’s “Pounds 20”. How can we localize this?

Not in the source code, which would be most unpleasant. We’d potentially have to fork for each localization everywhere in the code where monetary amounts are show to the user.

The solution is to approach the problem backwards. Instead of trying to fit a localized string (Dollars or Pounds) into a formatted string, we should place the formatted string in the localized string.

The trick is that localized strings can contain formatting escape codes. We can place the entire formatted string into our localized strings files and use the placeholder escape code for the amount. Our two key-value pairs will look like:

"Formatted" = "%d Dollars";
"Formatted" = "Pounds %d";

Now, when we build the string, we use the localized string as the primary argument to the method and only include our amount as an additional argument:

[NSString stringWithFormat:NSLocalizedString(@"Formatted", @""), 20];

Our class method loads the localized string, sees that it has formatting placeholders, and uses them to insert the additional arguments.

Summary

String formatting is easy. Localizable strings are easy. Combining the two is usually straightforward: include the localization as an additional argument to your formatted string. For more complex cases, come at the problem from the other direction and make the localized string a formatted string itself.

Leave a Reply