In short
The locale settings related to currency are of two kinds:
- Currency dependent: these are related to the monetary value and depend only on the currency and remain valid wherever you use that currency. This is only the international ISO code and the number of decimals, as defined by ISO 4217.
- Cultural settings: these depend on the usages and practices related to the language and the country of the users and not directly to the currency. Typically, it's the position of the currency code or symbol relatively to the value, as well as the decimal and thousand separators.
Fortunately, Swift makes very well the difference. Here some code, that allows you to adapt the currency dependent settings, without ever touching to the cultural settings that important for the user. I'll also explain why you shouldn't change all the local settings.
The code
Here the demo code, with a couple of representative currencies:
let value: Double = 1345.23
for mycur in ["USD", "TND", "EUR", "JPY" ] {
let myformatter = NumberFormatter()
myformatter.numberStyle = .currencyISOCode
let newLocale = "\(Locale.current.identifier)@currency=\(mycur)" // this is it!
myformatter.locale = Locale(identifier:newLocale)
print ("currency:\(mycur): min:\(myformatter.minimumFractionDigits) max:\(myformatter.maximumFractionDigits)"
print ("result: \(myformatter.string(from: value as NSNumber) ?? "xxx")")
}
For a representative demo, I've used :
- the USD and the EUR, which, like most currencies can be divided in 100 sub-units (the cents),
- the TND (Tunesian Dinar), which, like a handfull of other dinar-currencies, can be divided in 1000 sub-units (the millims),
- the JPY(Japanese Yen), which could in the past be divided into sub-units (the sens) of such a small value that the Japanese government decided not to use them anymore. This is why there are no decimals anymore for JPY amounts.
The results
For the user, will benefit from the principle of least astonishment, and see the decimal and thousand separators and the positioning he/she is used-to.
in my current locale (in my language currency code is to the right, decimals are separated by a comma, and thousands with a hard space) the result will be:
cur:USD: min:2 max:2 result: 1 345,23 USD
cur:TND: min:3 max:3 result: 1 345,230 TND
cur:EUR: min:2 max:2 result: 1 345,23 EUR
cur:JPY: min:0 max:0 result: 1 345 JPY
But if you'd usually work in an English speaking environment, for example in a US culture, you'd get:
cur:USD: min:2 max:2 result: USD 1,345.23
cur:TND: min:3 max:3 result: TND 1,345.230
cur:EUR: min:2 max:2 result: EUR 1,345.23
cur:JPY: min:0 max:0 result: JPY 1,345
How it works:
The trick of the code is to create a new locale, by just changing the currency settings, but leaving intact all other country and language dependent parameters.
let newLocale = "\(Locale.current.identifier)@currency=\(mycur)" // this is it!
myformatter.locale = Locale(identifier:newLocale)
Why you should not fully implement what you wanted
If you would start to adapt the positioning to take the practice of the language of the country the currency is originating from, you might irritate the users who no longer see the currency code where they expect them. Fortunately, it will not create a real confusion.
Example: the EUR is the currency of countries with very different cultures. The rule about positioning of the currency or the currency symbol was therefore defined to be dependent on the language of the text in which the amount appears. Official reference
Now, if you would start to adopt thousand and decimal separators of another language or country because it's the currency's home country, this would create a real confusion, especially for smaller amounts. Moreover, it's not always possible.
Example: In Canada the same currency amount is written with comma decimal separator by French-speaking Canadians, but dot decimal separator by english-speaking Canadians. This clearly shows it's not the currency that determines the separators to use, but the language of the user.
You should therefore be respectful of the user's settings in this regard, and only adapt the currency specific settings.