In the last article we have run through the basics of internationalization and localization for the iOS and localized a simple app in Portuguese.
Today we will dig deeper and share some common problems faced during localization and how to fix them.
Format strings come in handy when you need to display string with data at runtime. For instance:
John Appleseed finished 1 out of 3 tasks.
would be the result of:
en.lproj/Localizable.strings
/* %@{username} finished %lu{progress} out of %lu{total} tasks. */
"user_task_progress_format" = "%@ finished %lu out of %lu tasks.";
ViewController.m
NSString *messageFormat = NSLocalizedString(@"user_task_progress_format", @"%@{username} finished %lu{progress} out of %lu{total} tasks.");
NSString *message = [NSString stringWithFormat:messageFormat, username, progress, taskCount];
Tips: If you have difficulties understanding the above code, check out Apple’s String Programming Guide.
The above format string works well for English, but what about other languages? Here’s the translation in Traditional Chinese:
John Appleseed 完成了 3 項任務中的 1 項。
As you can see the order of the arguments is different from the English version. So how do we achieve the above result with format string?
One of the handy features of format string is positional specifiers, you can tell the formatter the order of the arguments by inserting n$
to the format specifier, e.g. %1$@
, %2$lu
, %3$lu
. Now the format string for Traditional Chinese should look like this:
zh-Hant.lproj/Localizable.strings
/* %@{username} 完成了 %lu{total} 項任務中的 %lu{progress} 項。 */
"user_task_progress_format" = "%1$@ 完成了 %3$lu 項任務中的 %2$lu 項。";
If you are a detail-minded person you might have noticed another problem in the above example, what if there is only 1 task in total?
John Appleseed finished 1 out of 1 tasks. // grammatical mistake!
I have seen many people try to solve this problem by using another format specifier for the s
or complex plural logic. There are also quite a number of 3rd-party libraries out there that promise to address this problem. But there’s a better solution supported officially since iOS 7, introducing:
The file extension is .stringsdict
but it’s actually a plist
file. It is not a replacement of .strings
file but rather an addition that tells the system the rules to follow when dealing with plurals, the filename of the .stringsdict
should match the .strings
file accordingly. Let’s see how we can make use of this new file format for our example:
en.lproj/Localizable.stringsdict
user_task_progress_format NSStringLocalizedFormatKey %1$@%2$#@lu_progress@%3$#@lu_total_tasks@ lu_progress NSStringFormatSpecTypeKey NSStringPluralRuleType NSStringFormatValueTypeKey lu other lu_total_tasks NSStringFormatSpecTypeKey NSStringPluralRuleType NSStringFormatValueTypeKey lu one has finished %2$lu out of 1 task. other has finished %2$lu out of %3$lu tasks.
It might seem tedious at first but it gets easier once you get a hang of it. The best thing about this solution is that you don’t need to change a single line of code in your source file, no plural logic is needed.
.stringsdict
also support gender rules?While the release notes of OS X 10.9 mentioned support of both plural and gender rules, the .stringsdict
file format can not handle gender rules as of iOS 8.3. The official documentation did not say a word about gender rules either. Hopefully it will be supported in a future release.
We now have a better understanding of how the system handles format strings and plurals. Download the source code at the box on the right (for web browswer) or below (for mobile browser). Let us know if you have any question and feedback!
In the next article we will discuss best practices when using NSLocalizedString and FAQ about the localization system on iOS.