In my current app, I have code that displays a message, e.g. "5 Items Processed." To keep the phrase grammatically correct, i.e. whether or not it should be "5 Item" or "5 Items," I use the following code:
int numItems = 5;
NSString *myString = [[NSString alloc] initWithFormat:@"%d Item%@ Processed", numItems, (numItems == 1 ? @"" : @"s")];
This works fine for now. But I'm localizing my app, and want to make sure that the text is grammatically correct in all the languages I am translating the app into. I could do something like this:
int numItems = 5;
NSString *myString = (numItems == 1 ?
NSLocalizedStringWithTable(@"%d Item Processed", @"myApp", @"singular version") :
NSLocalizedStringWithTable(@"%d Items Processed", @"myApp", @"plural version"));
However, not all languages have the same rules for how plurals operate! For example, (forgive my very specific example here) in Russian, nouns modified with numbers ending with the last digit 1 (i.e., 21, 31, but not 11) take the nominative case, numbers ending in 2-4 take the genitive singular, and 5+ take the genitive plural case. This would require much more serious logic to handle how to pluralize a particular noun in a grammatically correct fashion, and this logic would not match up to the English logic. Therefore, in theory, I cannot have the grammatical logic in my Objective-C code, but should rather have the grammatical logic in the strings file. Is there a way to do this? How do people translate dynamic text for their apps so that it remains grammatically correct?
As of iOS 7, Foundation framework has native support for pluralization. Here is a quick tutorial how to use it:
Create a plist file named as Localizable.stringsdict
English Localization:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>%d tasks waiting for action</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@tasks@ waiting for action</string>
<key>tasks</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>A task is</string>
<key>two</key>
<string>Two tasks are</string>
<key>other</key>
<string>%d tasks are</string>
</dict>
</dict>
</dict>
</plist>
Polish Localization:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>%d tasks waiting for action</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>Masz %#@zadanie@ do zrobienia</string>
<key>zadanie</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>jedno zadanie</string>
<key>few</key>
<string>%d zadania</string>
<key>other</key>
<string>%d zadań</string>
</dict>
</dict>
</dict>
</plist>
And finally in your implementation file, you can call the dictionary like this:
cell.tasksInfoLabel.text = [NSString localizedStringWithFormat:NSLocalizedString(@"%d tasks waiting for action", @"%d tasks waiting for action"), (long)taskCount];
EDIT: Thanks Zaphod for pointing this out ->: You also need to create the Localizable.strings file alongside the .stringsdict to have the pluralization work (even if it's empty).