18

I'm trying to format currency values in an iOS app, and I'm using the current Locale settings on the device to use the appropriate currency formatting.

In the simulator, everything seems to run fine: when using currencyFormatter.locale = Locale.current, it takes the right locale settings and prints numbers with the right currency format.

On my iPhone however, which is configured in French with French regional settings, I would expect another format to be used (e.g.: 1 234,56 €). But it does not work, and seems to use an English formatting style (e.g.: €1 234,56).

In fact, if I print the current Locale from my app on the device, it does not return fr_FR as I would expect:

NSLog(Locale.current.identifier)
>>> en_FR

The region is good but the language is not, though iOS on that device is clearly in French.

Has anyone an idea about this?

Thanks!

Romain
  • 3,718
  • 3
  • 32
  • 48
  • 3
    Does your app have a french localization? Otherwise the fallback language is english. – Martin R Jan 07 '18 at 11:32
  • It does not, but I would have hoped I would be able to print currency numbers in the user's regional settings without having to provide a localization for every language. Is that possible? – Romain Jan 07 '18 at 11:35
  • 5
    See for example https://stackoverflow.com/a/3328809/1187415: *"... the fallback language is the language which was most recently chosen by the user in the device Settings, **that is also represented in the app's bundle.**"* – Martin R Jan 07 '18 at 11:36
  • Thanks, so in fact, the language code returned by `Locale.current` does not necessarily represent the device's current language, but the first language supported by the app, or the fallback language... I guess I'll have to use another way to do what I want. – Romain Jan 07 '18 at 11:38
  • 4
    Details here: https://developer.apple.com/library/content/qa/qa1828/_index.html – I *assume* that the intent is to provide a consistent user interface, i.e. not something like "Last update: 22 décembre 2010" – Martin R Jan 07 '18 at 11:45

9 Answers9

36

Based on @Romain's answer the forced unwrapping of first! could be avoided by using the Locale.current.identifier as fallback.

func getPreferredLocale() -> Locale {
    guard let preferredIdentifier = Locale.preferredLanguages.first else {
        return Locale.current
    }
    return Locale(identifier: preferredIdentifier)
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
florieger
  • 1,310
  • 12
  • 22
  • 1
    You’re right, that’s safer. Marking it as the right answer! – Romain Jan 07 '18 at 15:39
  • 3
    Hi! Did you find a good solution to your problem. I have the same issue and keep getting "en" when using Locale.preferredLanguages[0] or Locale.current.identifier, but my phone is set to French. And this happens only with one app the others work as expected, returning "fr". – Michel Nov 15 '19 at 15:14
  • There is one more thing to try! Look at my post below :) – multitudes May 18 '21 at 16:16
  • I applied this answer in order to show the correct locale in an app widget. Thanks! – smukamuka Jun 08 '21 at 13:54
  • clever idea , this was what I look for it . – Coder ACJHP May 31 '22 at 09:42
16

@Romain The problem here is that you have not localized your app for French but only for English.

Go to the upper left blue file with the name of your app, select Project > Info and in the Localization area you will see "English - Development Language". Press + and choose French (fr).

That's it. Now you can use Locale.current and if the first language of a device (or simulator) is French, your app will show French currency format.

  • Thank you! This explains the behavior that I thought was wrong at first. – Kirill Jun 04 '20 at 04:58
  • 1
    This answer must be marked as solution. But I have one comment: it works only if you have one or more resources for localization, for example Localizable.strings or something else that can be localized. – Sound Blaster Aug 02 '20 at 00:11
8

@florieger's answer in the form of extension:

import Foundation

extension Locale {
    static func preferredLocale() -> Locale {
        guard let preferredIdentifier = Locale.preferredLanguages.first else {
            return Locale.current
        }
        return Locale(identifier: preferredIdentifier)
    }
}

Then use it like this:

dateFormatter.locale = Locale.preferredLocale()
datePicker.locale = Locale.preferredLocale()
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Denis Kutlubaev
  • 15,320
  • 6
  • 84
  • 70
6

If everything else fails

I spent an hour debugging this. I am in Germany and my iPhone language settings are English and my Region is Germany.

I am debugging an app that takes the locale from the iPhone and pass it in an API call as a header to a server. I was wondering why I always get all the data strings in German. Looking into the app it was all done correctly:

    if let deviceLanguageCode = Locale.current.languageCode,
    deviceRegionCode = Locale.current.regionCode {
        // do stuff here
    }

The variable above deviceLanguageCode always returns de.
But it should be "en"!
I tried everything. I used Locale.autoupdatingCurrent.languageCode or Locale.preferredLanguages.first etc, nothing would work. I checked my project settings, my Localizable.strings files. All good. If I made a new Xcode project and print those variables I would get the correct locale, and region: en_DE.

This is so infuriating!
Only when I exhausted all possibilities, I read this post by Paul Hudson, (who else?) and at the end, he gives a tip for testing the localisations:

Tip: You can switch between languages by going to the Product menu, holding down the Alt key, then choosing “Run...” Look under the Options tab and you’ll see Application Language is set to System Language by default, but you can change to others there for testing purposes.

And... 'quelle horreur!' Somebody had been there before me and had set the App Language and Region to Germany!!! No wonder I had a hard time!

End of story: Make sure these are set to system or nothing will work ;)

enter image description here

multitudes
  • 2,898
  • 2
  • 22
  • 29
2

Following @MartinR's hint, I'm now using currencyFormatter.locale = Locale(identifier: Locale.preferredLanguages.first!), which corresponds exactly to the device's current language & region settings.

I'm not entirely sure this code is bullet-proof though (because of first!, most notably) so if you have other suggestions, please feel free.

Romain
  • 3,718
  • 3
  • 32
  • 48
0

use this code for get current System language code from iOS device

let pre = Locale.preferredLanguages[0]

Still if you face this issue if you got wrong language code. kindly remove this object from UserDefaults "AppleLanguages" and use this same code Locale.preferredLanguages[0]

for your reference

func getSystemLanguageCode() -> String {
    UserDefaults.standard.removeObject(forKey: "AppleLanguages")
    let pref_Language = NSLocale.preferredLanguages[0] as String //"fr-IN"
    let language = pref_Language.components(separatedBy: "-") //["fr","IN"]
    let lang_Code = language.first?.lowercased() ?? "" //"fr"
    UserDefaults.standard.set([lang_Code], forKey: "AppleLanguages")
    
    return lang_Code
}
0

Summary

Apple expects that when a developer decides to support localizations, the project would have localizable resources like strings or storyboards to provide consistent UI (e.g. not just to print currency in local format) and in this case using Locale.current is the appropriate choice and it would work just fine for supported localizations (as mentioned by this answer).

However, in cases like @Romain's:

"I would have hoped I would be able to print currency numbers in the user's regional settings without having to provide a localization for every language"

the workaround to achieve that is by using Locale.preferredLanguages.first with fallback in case of null (as mentioned by this answer)

Dima G
  • 1,945
  • 18
  • 22
0

if you want the current Language depends on the user region and Locale use this:

Locale.current.languageCode

BUT IF you want SETTINGS CURRENT Language, use this code:

Locale.preferredLanguages.first
jamal zare
  • 1,037
  • 12
  • 13
-1

When the widget should have the same language as the app and the iPhone language is different you can use this:

func getPreferredLocale() -> Locale {
    guard let preferredIdentifier = Bundle.main.preferredLocalizations.first else {
        return Locale.current
    }
    return Locale(identifier: preferredIdentifier)
}
Michael
  • 293
  • 3
  • 12
  • `Bundle.main.preferredLocalizations.first` return localization that your app support, nous user device preference... For example if your app have translation for en & fr but your device is in chinese, it will return "en"... – Aure77 Nov 23 '21 at 10:10