I want get the language code of the device (en, es...) in my app written with Swift. How can get this?
I'm trying this:
var preferredLanguages : NSLocale!
let pre = preferredLanguages.displayNameForKey(NSLocaleIdentifier, value: preferredLanguages)
But this returns nil.
In Swift 3
let langStr = Locale.current.languageCode
It's important to make the difference between the App language and the device locale language (The code below is in Swift 3)
Will return the Device language:
let locale = NSLocale.current.languageCode
Will return the App language:
let pre = Locale.preferredLanguages[0]
Swift 4 & 5:
Locale.current.languageCode
Swift 3 & 4 & 4.2 & 5
Locale.current.languageCode does not compile regularly. Because you did not implemented localization for your project.
You have two possible solutions
1) String(Locale.preferredLanguages[0].prefix(2))
It returns phone lang properly.
If you want to get the type en-En, you can use Locale.preferredLanguages[0]
2)
Select Project(MyApp)->Project (not Target)-> press + button into Localizations, then add language which you want.
In Swift 3:
NSLocale.current.languageCode
TL;DR:
Use Bundle.main.preferredLocalizations[0] to get the language your app's UI is currently displayed in. Don't use Locale.current because it describes the region format (time, currency, distance, etc) and has nothing to do with language.
Detailed Answer:
The definite answer about how to get the language(!) code for the language your app's UI is displayed in comes from Apple engineer Quinn "The Eskimo", and I quote/paraphrase for Swift:
Locale.current returns the current locale, that is, the value set by Settings > General > Language & Region > Region Formats. It has nothing to do with the language that your app is running in. It's perfectly reasonable, and in fact quite common, for users in the field to have their locale and language set to 'conflicting' values. For example, a native English speaker living in France would have the language set to English but might choose to set the locale to French (so they get metric weights and measures, 24 time, and so on).
The language that your app runs in is determined by the language setting, that is, Settings > General > Language & Region > Preferred Language Order. When the system runs your app it takes this list of languages (the preferred list) and matches it against the list of languages that your app is localised into (the app list). The first language in the preferred list that exists in the app list is the language chosen for the app. This is what you'll find in the first entry of the main bundle's preferredLocalizations array.
Language Name from Code
To get the human-readable name of a language from its code, you can use this:
let langCode = Bundle.main.preferredLocalizations[0]
let usLocale = Locale(identifier: "en-US")
var langName = ""
if let languageName = usLocale.localizedString(forLanguageCode: langCode) {
langName = languageName
}
This will give you the English name of the current UI language.
To get current language used in your app (different than preferred languages)
NSLocale.currentLocale().objectForKey(NSLocaleLanguageCode)!
swift 3
let preferredLanguage = Locale.preferredLanguages[0] as String
print (preferredLanguage) //en-US
let arr = preferredLanguage.components(separatedBy: "-")
let deviceLanguage = arr.first
print (deviceLanguage) //en
Locale.current.languageCode returns me wrong code, so I use these extensions:
extension Locale {
static var preferredLanguageCode: String {
guard let preferredLanguage = preferredLanguages.first,
let code = Locale(identifier: preferredLanguage).languageCode else {
return "en"
}
return code
}
static var preferredLanguageCodes: [String] {
return Locale.preferredLanguages.compactMap({Locale(identifier: $0).languageCode})
}
}
Swift 5.4:
let languagePrefix = Locale.preferredLanguages[0]
print(languagePrefix)
you may use the below code it works fine with swift 3
var preferredLanguage : String = Bundle.main.preferredLocalizations.first!
I want to track the language chosen by the user in Settings app every time the user launches my app - that is not yet localized (my app is in English only). I adopted this logic:
create an enum to to make it easier to handle the languages in array
enum Language: String {
case none = ""
case en = "English"
case fr = "French"
case it = "Italian"
} // add as many languages you want
create a couple of extension to Locale
extension Locale {
static var enLocale: Locale {
return Locale(identifier: "en-EN")
} // to use in **currentLanguage** to get the localizedString in English
static var currentLanguage: Language? {
guard let code = preferredLanguages.first?.components(separatedBy: "-").last else {
print("could not detect language code")
return nil
}
guard let rawValue = enLocale.localizedString(forLanguageCode: code) else {
print("could not localize language code")
return nil
}
guard let language = Language(rawValue: rawValue) else {
print("could not init language from raw value")
return nil
}
print("language: \(code)-\(rawValue)")
return language
}
}
When you need, you can simply use the extension
if let currentLanguage = Locale.currentLanguage {
print(currentLanguage.rawValue)
// Your code here.
}
In Swift, You can get the locale using.
let locale = Locale.current.identifier
This is what I use in Swift 5 Xcode 11:
Inside the class variables:
let languagePrefix = Bundle.main.preferredLocalizations.first?.prefix(2)
This comes as a string. It returns 2 characters, i.e. "en", "es", "de"...
From this I can easily determine what language to display:
if languagePrefix == "es" { self.flipCard.setTitle("última carta", for: .normal) }
if languagePrefix == "en" { self.flipCard.setTitle("Last Card", for: .normal) }
If you want the full information of the language, then remove ?.prefex(2)
in most cases you want to get the language code of the current app UI, to send over an API to get localized response
extension Bundle {
var currentLocalizedUILanguageCode: String {
guard let code = Bundle.main.preferredLocalizations.first?.components(separatedBy: "-").first else {
return Locale.current.languageCode ?? "en"
}
return code
}
}
use like
headers["X-Language"] = Bundle.main.currentLocalizedUILanguageCode
use this function for get your system's current language code from iOS devices
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
}
Almost none of the answers are correct. This is working Swift 5.7 solution.
extension Locale {
// Gets the language of the device, had to remove the content of AppleLanguages since `preferredLanguages`
// is combining the result from multiple APIs. AppleLanguage is then being set to the old value
static var preferredLanguageCode: String {
let appleLanguages = UserDefaults.standard.stringArray(forKey: kLanguage)
UserDefaults.standard.removeObject(forKey: kLanguage)
guard let preferredLanguage = preferredLanguages.first,
let code = Locale(identifier: preferredLanguage).languageCode else {
UserDefaults.standard.set(appleLanguages, forKey: kLanguage)
return "en"
}
UserDefaults.standard.set(appleLanguages, forKey: kLanguage)
return code
}
}
I tried the following code:
let units: [ByteCountFormatter.Units] = [.useBytes, .useKB, .useMB, .useGB, .useTB, .usePB, .useEB, .useZB, .useYBOrHigher]
let localizedDescriptions = units.map { (unit) -> String in
let formatter = ByteCountFormatter()
formatter.includesCount = false
formatter.includesUnit = true
formatter.allowedUnits = [unit]
formatter.countStyle = .file
return formatter.string(fromByteCount: .max)
}
And expect it to be localized according to the documentation.
Class
ByteCountFormatter
A formatter that converts a byte count value
into a localized description that is formatted with the appropriate
byte modifier (KB, MB, GB and so on).
But unfortunately, I got only:
["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
I tested:
Switch system locale and reload my mac(Saw different file size format in finder: "КБ", "МБ"... instead of "KB", "MB")
Playground/macOS template project.
Switched "Application language" in macOS template project.
PS
In any case thanks for reading this...
You cannot set a locale to ByteCountFormatter, but you can with MeasurementFormatter.
Here is a sample (modify the unitStyle and other properties as you need).
let units: [UnitInformationStorage] = [.bytes, .kilobytes, .megabytes, .gigabytes, .terabytes, .petabytes, .zettabytes, .yottabytes]
let localizedDescriptions = units.map({ unit -> String in
let formatter = MeasurementFormatter()
formatter.unitStyle = .short
formatter.locale = Locale(identifier: "ru_RU") //hard coded here, I guess it takes the current one
return formatter.string(from: unit)
})
Output:
$> ["Б", "кБ", "МБ", "ГБ", "ТБ", "ПБ", "ZB", "YB"]
Zetta & Yotta aren't translated though?
From NSHipster:
ByteCountFormatter, EnergyFormatter, MassFormatter, LengthFormatter, and MKDistanceFormatter are superseded by MeasurementFormatter.
Legacy Measure: ByteCountFormatter
Measurement Formatter Unit: UnitInformationStorage
My scenario, I am trying to pass the language code based on it SFSpeechRecognizer changing language for speech to text. Here, I can able to use only apple provided 63 languages. If I pass some other languages I am getting crash because of language unavailability. How to validate and handle it in a proper way using swift?
My Code
private let speechRecognizer = SFSpeechRecognizer(locale: Locale.init(identifier: "en-US")) //1
I followed below tutorial for implementing speech to text
https://www.appcoda.com/siri-speech-framework/
You can check if the Locale is supported by supportedLocales
// Locales which support speech recognition.
// Note that supported does not mean currently available; some locales
// may require an internet connection, for example.
+ (NSSet<NSLocale *> *)supportedLocales;
let locale = Locale(identifier: "hi-IN")
SFSpeechRecognizer.supportedLocales().contains(locale) // Can check using this
let sr = SFSpeechRecognizer(locale: locale)
sr?.isAvailable // Can check using this
lazy var srf: SFSpeechRecognizer? = {
let locale = Locale(identifier: "hi-IN")
return SFSpeechRecognizer.supportedLocales().contains(locale) ? SFSpeechRecognizer(locale: locale) : nil
}()
I want to be able to nicely use a Measurement and MeasurementFormatter for output and input with a NSTextFieldCell.
I am able to display the measurement correctly with...
let areaFormatter = MeasurementFormatter()
areaFormatter.unitStyle = .medium
areaFormatter.unitOptions = .providedUnit
let area = Measurement<UnitArea>( value: 123.43, unit: .squareInches)
let editInput = NSTextFieldCell
editInput.objectValue = area
editInput.formatter = areaFormatter
This displays something like
123.43 in^2
The problem starts when I want to read this back in with
var inputArea = editInput.objectValue as! Measurement<UnitArea>
I think because the get Object value of the Measurement Formatter is not defined.
open func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer<AnyObject?>?, for string: String, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool
Is my understanding correct? Are there any examples in Swift where this function has been defined for measurements?
Right now the user can edit the entire string including the text in the units. Is there a good way to block the units in the NSTextFieldCell? I would like the user to be able to edit the number but not the units and then return the measurement with
var inputArea = editInput.objectValue as! Measurement<UnitArea>
so this gets displayed
123.43 in^2
but only the 123.43 can be edited.
In my app I've got a certain distance in meters.
And I want to display it in kilometers if user prefers kilometers and display it in miles if user prefers miles. And in the first case I want to add to a string "kilometers" at the end and in the second one to add "miles".
What is the best way to achieve this goal?
Thanks.
To determine whether the user uses metric or not, NSLocale can tell you:
- (BOOL)isMetric {
return [[[NSLocale currentLocale] objectForKey:NSLocaleUsesMetricSystem] boolValue];
}
Swift equivalent of Chris' answer would be something like this:
func isMetric() -> Bool {
return ((Locale.current as NSLocale).object(forKey: NSLocale.Key.usesMetricSystem) as? Bool) ?? true
}
Note that it defaults to true under certain circumstances. Change as needed.
You could ask the user whether they prefer miles or kilometers, in a preference or something. Then whenever you display a distance you would say.
In pseudo c code
function distance(meters) {
if (userPrefersKM) {
return meters / 1000 + " kilometers";
else if (userPrefersMiles) {
return meters / METERS_IN_A_MILE + " miles";
}
Where METERS_IN_A_MILE would be about 1600, but you should look that up.
In Swift, Locale.current.usesMetricSystem gives what the user would expect. But you don't need that if you use Measurement which handles it for you.
let distanceInMeters: Double = 2353.45
let formatter = MeasurementFormatter()
formatter.unitStyle = .medium // adjust according to your need
let distance = Measurement(value: distanceInMeters, unit: UnitLength.meters)
formatter.string(from: distance)
The current locale dictates how it is presented to the user. To see how it works for different locales, try this in a Xcode Playground (examples are for UK and France):
let distanceInMeters: Double = 2353.45
let formatter = MeasurementFormatter()
formatter.unitStyle = .medium // adjust according to your need
let distance = Measurement(value: distanceInMeters, unit: UnitLength.meters)
formatter.locale = Locale(identifier: "en_UK")
formatter.string(from: distance) // 1.462 mi
formatter.locale = Locale(identifier: "en_FR")
formatter.string(from: distance) // 2,353 km
Unless the iPhone provides this information directly, you'll have to have a lookup table from locale to default unit. Then you should allow the user to override that default.