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:@"%.2f", amount]; STAssertTrue([text1 isEqualToString:@"3450.26"], @"Text1 = %@", text1); }
Dieser Test liefert das erwartete Ergebnis, die Zusicherung wird erfüllt.
Nicht aber beim nächsten Test:
- (void)test2 { NSString * text1 = [NSString localizedStringWithFormat:@"%.2f", amount]; STAssertTrue([text1 isEqualToString:@"3450,26"], @"Text1 = %@", 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:@"%.2f" locale:dictLocale, amount]; STAssertTrue([text1 isEqualToString:@"3450,26"], @"Text1 = %@", 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:@"%.2f" locale:locale, amount]; STAssertTrue([text1 isEqualToString:@"3450,26"], @"Text1 = %@", 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 = %@", text1); }
Dieser Test liefert endlich das gewünschte Ergebnis.