Mit iOS 6 hat sich ein ärgerlicher Fehler in die NSString Klasse eingeschlichen. Die Methode localizedStringWithFormat gibt große Zahlen jetzt inklusive dem NSLocaleGroupingSeparator aus, obwohl keine entsprechende Format-Option angegeben ist.
Beispiel: Mit dem Format @".2f" werden Zahlen ≥ 1000 in der Locale de_DE so ausgegeben: 2.500.300,23. Jede Tausender-Stelle wird also markiert. In iOS 5 war das noch nicht so.
Den Fehler kann man leicht mit einem Unit-Test zeigen.
Zum warm werden einmal die normale stringWithFormat Methode ohne Lokalisierung testen:
- (void)setUp
{
[super setUp];
amount = 3450.26;
}
- (void)test1
{
NSString * text1 = [NSString stringWithFormat:@"{ba6935268c41f4895ee6bc2a25f7961fbf3a326e86d711819d5b965ee27c3439}.2f", amount];
STAssertTrue([text1 isEqualToString:@"3450.26"], @"Text1 = {ba6935268c41f4895ee6bc2a25f7961fbf3a326e86d711819d5b965ee27c3439}@", text1);
}
Dieser Test liefert das erwartete Ergebnis, die Zusicherung wird erfüllt.
Nicht aber beim nächsten Test:
- (void)test2
{
NSString * text1 = [NSString localizedStringWithFormat:@"{ba6935268c41f4895ee6bc2a25f7961fbf3a326e86d711819d5b965ee27c3439}.2f", amount];
STAssertTrue([text1 isEqualToString:@"3450,26"], @"Text1 = {ba6935268c41f4895ee6bc2a25f7961fbf3a326e86d711819d5b965ee27c3439}@", text1);
}
error: "[text1 isEqualToString:@"3450,26"]" should be true. Text1 = 3.450,26
Unerwartet wird in Tausender-Stellen gruppiert.
Kann man vielleicht am Aufruf etwas ändern? In der iOS-Dokumentation steht zur Methode localizedStringWithFormat:
„This method is equivalent to using
initWithFormat:locale:and passing[[NSUserDefaults standardUserDefaults] dictionaryRepresentation]as the locale argument.“
Einmal ausprobieren:
- (void)test3
{
NSDictionary * dictLocale = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
NSString * text1 = [[NSString alloc] initWithFormat:@"{ba6935268c41f4895ee6bc2a25f7961fbf3a326e86d711819d5b965ee27c3439}.2f" locale:dictLocale, amount];
STAssertTrue([text1 isEqualToString:@"3450,26"], @"Text1 = {ba6935268c41f4895ee6bc2a25f7961fbf3a326e86d711819d5b965ee27c3439}@", text1);
}
error: "[text1 isEqualToString:@"3450,26"]" should be true. Text1 = 3450.26
Es findet offenbar gar keine Lokale-Übersetzung statt. Wenn man sich das Dictionary dictLocale anschaut, verwundert das nicht, denn dort sind überhaupt keine Lokale-Informationen zu sehen! Diese iOS-Dokumentation stimmt also nicht!
Wenn man anstelle des Dictionaries tatsächlich ein NSLocale-Objekt übergibt, dann verhält sich initWithFormat äquivalent zu localizedStringWithFormat.
- (void)test4
{
NSLocale * locale = [NSLocale currentLocale];
NSString * text1 = [[NSString alloc] initWithFormat:@"{ba6935268c41f4895ee6bc2a25f7961fbf3a326e86d711819d5b965ee27c3439}.2f" locale:locale, amount];
STAssertTrue([text1 isEqualToString:@"3450,26"], @"Text1 = {ba6935268c41f4895ee6bc2a25f7961fbf3a326e86d711819d5b965ee27c3439}@", text1);
}
error: "[text1 isEqualToString:@"3450,26"]" should be true. Text1 = 3.450,26
Es bleibt dabei: Die Methode localizedFormatWithString hat einen Bug und dem ist nicht beizukommen.
Test5 zeigt eine mögliche Ausweich-Strategie:
- (void)test5
{
NSNumberFormatter * formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
formatter.usesGroupingSeparator = NO;
NSString * text1 = [formatter stringFromNumber:[NSNumber numberWithDouble:amount]];
STAssertTrue([text1 isEqualToString:@"3450,26"], @"Text1 = {ba6935268c41f4895ee6bc2a25f7961fbf3a326e86d711819d5b965ee27c3439}@", text1);
}
Dieser Test liefert endlich das gewünschte Ergebnis.
