Since iOS 9.0 I'm not getting the same results as before. It turns out that since iOS 9.0 when you use: -[NSString initWithFormat:locale:,...] it picks pluralization rules based on given locale (and not based on the language app is running in).
NSString *format = NSLocalizedString... from .stringsdict
NSLocale *locale = ... some locale
NSInteger number = 42; // random
NSString *pluralizedString = [[NSString alloc] initWithFormat:format locale:locale, number];
That means, if you pass in different locale, NSString picks wrong format string from .stringsdict. Is that a correct behavior, or is that a bug? I strongly believe that that is a bug, I've submitted it (22804555) months ago, but got no responce.
More specific example:
Let's have a .stringsdict:
<?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>format_key</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%1$#@format_key_plural@</string>
<key>format_key_plural</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>lu</string>
<key>zero</key>
<string>ZERO</string>
<key>one</key>
<string>ONE</string>
<key>few</key>
<string>FEW</string>
<key>many</key>
<string>MANY</string>
<key>other</key>
<string>OTHER</string>
</dict>
</dict>
</dict>
</plist>
And code:
NSLog(@"[[NSBundle mainBundle] preferredLocalizations] = %@", [[NSBundle mainBundle] preferredLocalizations]); // language app is running in
NSLog(@"[NSLocale currentLocale].localeIdentifier = %@", [NSLocale currentLocale].localeIdentifier); // regional conventions
NSString *format = NSLocalizedString(@"format_key", nil); // from a .stringsdict provided above
NSUInteger numForMany = 5; // it should be mapped to "many" in Russian(ru) and "other" in English(en)
// ru
NSLocale *ruLocale = [NSLocale localeWithLocaleIdentifier:@"ru"];
NSString *ruResult = [[NSString alloc] initWithFormat:format locale:ruLocale, numForMany];
NSLog(@"%@ for %lu: %@", ruLocale.localeIdentifier, numForMany, ruResult);
// en
NSLocale *enLocale = [NSLocale localeWithLocaleIdentifier:@"en"];
NSString *enResult = [[NSString alloc] initWithFormat:format locale:enLocale, numForMany];
NSLog(@"%@ for %lu: %@", enLocale.localeIdentifier, numForMany, enResult);
// currentLocale
NSLocale *currentLocale = [NSLocale currentLocale];
NSString *currentLocaleResult = [[NSString alloc] initWithFormat:format locale:currentLocale, numForMany];
NSLog(@"%@ (currentLocale) for %lu: %@", currentLocale.localeIdentifier, numForMany, currentLocaleResult);
And that what it outputs with language set to russian, while regional settings are all set to english. So that currentLocale return en_US while preferred localization (the language app is running in) would be ru:
// iOS 8
2015-09-21 12:56:00.245 LocalizationTests[99346:9581633] [[NSBundle mainBundle] preferredLocalizations] = (
ru
)
2015-09-21 12:56:00.271 LocalizationTests[99346:9581633] [NSLocale currentLocale].localeIdentifier = en_US
2015-09-21 12:56:00.272 LocalizationTests[99346:9581633] ru for 5: MANY
2015-09-21 12:56:00.272 LocalizationTests[99346:9581633] en for 5: MANY
2015-09-21 12:56:00.272 LocalizationTests[99346:9581633] en_US (currentLocale) for 5: MANY
That's good, it uses correct plural rules for the .stringsdict, which contains strings in Russian for Russian plural forms. No matter which NSLocale I provide, it uses correct plural rules and formats numbers according to settings from NSLocale.
// iOS 9
2015-09-21 13:00:31.472 LocalizationTests[99610:9610901] [[NSBundle mainBundle] preferredLocalizations] = (
ru
)
2015-09-21 13:00:31.776 LocalizationTests[99610:9610901] [NSLocale currentLocale].localeIdentifier = en_US
2015-09-21 13:00:31.785 LocalizationTests[99610:9610901] ru for 5: MANY
2015-09-21 13:00:31.785 LocalizationTests[99610:9610901] en for 5: OTHER
2015-09-21 13:00:31.785 LocalizationTests[99610:9610901] en_US (currentLocale) for 5: OTHER
You see? In iOS 9 it uses given locale to get plural rules for! It would use different plural rules depending on given NSLocale instance while loading the same resource in a single language.
It breaks the concept of two different settings - language and regional conventions. At second it breaks existing codebases. So, if you compile against iOS SDK 9.0+ and user sets different region you'll end up with wrong text. How can we deal with that (without specifying locale based on language app is running in)?