Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using NSNumberFormatter to get a decimal value from an international currency string

It seems that the NSNumberFormatter can't parse Euro (and probably other) currency strings into a numerical type. Can someone please prove me wrong.

I'm attempting to use the following to get a numeric amount from a currency string:

NSNumberFormatter *currencyFormatter = [[[NSNumberFormatter alloc] init] autorelease];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSNumber *currencyNumber = [currencyFormatter numberFromString:currencyString];

This works fine for UK and US currency amounts. It even deals with $ and £ and thousands separators with no problems.

However, when I use it with euro currency amounts (with the Region Format set to France or Germany in the settings app) it returns an empty string. All of the following strings fail:

12,34 €
12,34
12.345,67 €
12.345,67

It's worth noting that these strings match exactly what comes out of the NSNumberFormatter's stringFromNumber method when using the corresponding locale.

Setting the Region Format to France in the settings app, then setting currencyNumber to 12.34 in the following code, results in currencyString being set to '12,34 €' :

NSNumberFormatter *currencyFormatter = [[[NSNumberFormatter alloc] init] autorelease];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSString *currencyString = [currencyFormatter stringFromNumber:currencyNumber];

It would obviously be fairly easy to hack around this problem specifically for the Euro but I'm hoping to sell this app in as many countries as possible and I'm thinking that a similar situation is bound to occur with other locales.

Does anyone have an answer?

TIA, Duncan

like image 272
pieSquared Avatar asked Aug 24 '10 20:08

pieSquared


4 Answers

This is a difficult one. Doing the following works without issues:

double moneyAmount = 1256.34;
NSLocale *french = [[NSLocale alloc] initWithLocaleIdentifier:@"fr_FR"]; 
NSNumberFormatter *currencyStyle = [[NSNumberFormatter alloc] init];

[currencyStyle setLocale:french];
[currencyStyle setNumberStyle:NSNumberFormatterCurrencyStyle];
NSNumber *amount = [NSNumber numberWithDouble:moneyAmount];
NSString *amountString =  [currencyStyle stringFromNumber:amount];

NSNumber *pAmount = [currencyStyle numberFromString:amountString]; // == 1256.34

However, the output of the -currencyGroupingSeparator method returns the string \u00a0, a non-breaking space, rather than the expected ,.

The formatter classes (as well as the regex classes, among others) on OS X / iOS make use of the ICU library, also used by other projects. I found at least two other bug reports with regards to this particular issue. It appears that this is expected behaviour, though. (I spent some time sifting through the ICU source to find where this is defined, but it is a labyrinth.)

My recommendation, for the time being, would be to pass -setLenient:YES to the currency formatter. Perhaps even file a bug report with Apple's radar.

like image 95
Sedate Alien Avatar answered Oct 07 '22 20:10

Sedate Alien


Have you tried setting the region to France in settings and then attempting to get the numeric amount for the string? Or you could manually set the local of the number formatter with setLocale:. NSNumberFormatter uses whatever locale it has to figure out what the numbers is a string are. If you just need the user to be able to put in a localized currency string for their area, change the system to that locale and then test. If you need the user to be able to put in multiple formats then add some sort of selection system so you know what they are putting in.

like image 28
theMikeSwan Avatar answered Oct 07 '22 21:10

theMikeSwan


I verified that (at least on iOS 4.3) using the [formatter setLenient:YES] flag worked for me using Euros. Without that flag set, the formatter does not return correct values for Euros.

like image 30
Ed Fernandez Avatar answered Oct 07 '22 21:10

Ed Fernandez


Its working code in Swift..

    let formatter = NumberFormatter()
    formatter.usesGroupingSeparator = true
    formatter.locale = Locale(identifier: "de_DE")  
    formatter.numberStyle = NumberFormatter.Style.currency
    formatter.string(from: NSNumber(floatLiteral: InputAsDoubleValue))!

Identifiers:

  • for German - "de_DE"
  • for US - "en_US"
  • for France - "fr_FR"

The formatted value would also be returned with the respective currency symbol.

like image 28
Shailesh Tyagi Avatar answered Oct 07 '22 21:10

Shailesh Tyagi