localizedStringWithFormat Fehler in iOS 6

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.

Leave a Reply

Your email address will not be published.