I work with localization and have a question:
I need this:
if language = english -> use this func, else use this func
I don't really understand how I can clarify this particular language
An option could be for you to have a localised key in both your translations:
Localizable.string (English) current_language = "en"
Localizable.string (Russian) current_language = "ru"
And then
let currentLanguage = NSLocalizedString("current_language", comment: "")
if currentLanguage == "en" {
englishFunction()
} else {
russianFunction()
}
2) 2nd option could be to check Device language:
let deviceLanguage = Locale.current.languageCode
3) 3rd option could be to check User preferred language:
let userPreferredLanguage = Locale.preferredLanguages[0]
To understand the difference between App Language and Device Language think of it as in this example: when your app develeopment language is EN and your device is set with IT (italian) language, and you have no localization for IT then: deviceLanguage = en, userPreferredLanguage = it
Your app development language is the default fallback language that will be used as localization in case you have no support language for the user preferences language, the default/fallback language you can find in your .plist file under this key: CFBundleDevelopmentRegion
Related
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 have a language code, such as "en_US", and I'm trying to get a friendly name from it such as "English".
Here's what I'm doing right now:
Locale.current.localizedString(forLanguageCode: code)
It works, but for languages such as Chinese, it doesn't quite do what I want.
zh-Hans should return "Simplified Chinese", and zh-Hant should return "Traditional Chinese".
However, they both just return "Chinese". How would you get them to return the correct values?
You can use NSLocale's displayName(forKey:value:) instead.
let code = "en_US"
if let identifier = (Locale.current as NSLocale).displayName(forKey: .identifier, value: code) {
print(identifier) /// English (United States)
}
let code = "zh_Hans"
if let identifier = (Locale.current as NSLocale).displayName(forKey: .identifier, value: code) {
print(identifier) /// Chinese, Simplified
}
"en_US" is not a language code, it's a locale identifier consisting of the language code "en" and the region code "US". Thus, calling into localizedString(forLanguageCode:) will not work properly. With an identifier (like you have), use localizedString(forIdentifier:):
let identifier = "en_US"
let humanReadableName =
Locale.current.localizedString(forIdentifier: identifier) ?? identifier
This also works nicely with identifiers such as zh-Hans which will return "Chinese, Simplified".
Note that I'm suggesting to append ?? identifier at the end because localizedString(forIdentifier:) returns an Optional in case of an invalid identifier where you can fall back to the identifier itself so you don't have to deal with an Optional String.
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'm currently checking out Swift's NSLinguisticTagger. For test purposes I used the code from appcoda Introduction to Natural Language Processing.
For the English language it works as expected and described in the tutorial. But when I use NSLinguisticTagger on languages other than English the Lemmatization, Parts of Speech and Named Entity Recognition produces no useful results. I can understand this for the Named Entity Recognition but for the first two options I thought at least a basic result should be possible. Did I miss a language specific setting or is NSLinguisticTagger only good for language detection and Tokenization when used for languages other than English?
Here's the code Sai Kambampati uses in his tutorial:
import Foundation
let quote = "Here's to the crazy ones. The misfits. The rebels. The troublemakers. The round pegs in the square holes. The ones who see things differently. They're not fond of rules. And they have no respect for the status quo. You can quote them, disagree with them, glorify or vilify them. About the only thing you can't do is ignore them. Because they change things. They push the human race forward. And while some may see them as the crazy ones, we see genius. Because the people who are crazy enough to think they can change the world, are the ones who do. - Steve Jobs (Founder of Apple Inc.)"
let tagger = NSLinguisticTagger(tagSchemes:[.tokenType, .language, .lexicalClass, .nameType, .lemma], options: 0)
let options: NSLinguisticTagger.Options = [.omitPunctuation, .omitWhitespace, .joinNames]
func determineLanguage(for text: String) {
tagger.string = text
let language = tagger.dominantLanguage
print("The language is \(language!)")
}
determineLanguage(for: quote)
func tokenizeText(for text: String) {
tagger.string = text
let range = NSRange(location: 0, length: text.utf16.count)
tagger.enumerateTags(in: range, unit: .word, scheme: .tokenType, options: options) { tag, tokenRange, stop in
let word = (text as NSString).substring(with: tokenRange)
print(word)
}
}
tokenizeText(for: quote)
func partsOfSpeech(for text: String) {
tagger.string = text
let range = NSRange(location: 0, length: text.utf16.count)
tagger.enumerateTags(in: range, unit: .word, scheme: .lexicalClass, options: options) { tag, tokenRange, _ in
if let tag = tag {
let word = (text as NSString).substring(with: tokenRange)
print("\(word): \(tag.rawValue)")
}
}
}
partsOfSpeech(for: quote)
func namedEntityRecognition(for text: String) {
tagger.string = text
let range = NSRange(location: 0, length: text.utf16.count)
let tags: [NSLinguisticTag] = [.personalName, .placeName, .organizationName]
tagger.enumerateTags(in: range, unit: .word, scheme: .nameType, options: options) { tag, tokenRange, stop in
if let tag = tag, tags.contains(tag) {
let name = (text as NSString).substring(with: tokenRange)
print("\(name): \(tag.rawValue)")
}
}
}
namedEntityRecognition(for: quote)
For the English sentence the result is exactly as expected.
e.g. for the Parts of Speech Tagging and the Named Entity Recognition:
The: Determiner
troublemakers: Noun
The: Determiner
round: Noun
pegs: Noun
...
Apple Inc.: Noun
Steve Jobs: PersonalName
Apple Inc.: OrganizationName
But for a German sentence
let quote = "Apple führt die Hitliste der Silicon-Valley-Unternehmen an, bei denen sich Ingenieure das Wohnen in der Nähe nicht mehr leisten können. Dahinter folgen das Portal Reddit (San Francisco), der Suchriese Google (Mountain View) und die sozialen Netzwerke Twitter (San Francisco) und Facebook (Menlo Park)"
only the language detection and the tokenization seems to work correct. For the Parts of Speech Tagging only "OtherWord" and for the Named Entity Recognition no result at all is returned:
Apple: OtherWord
führt: OtherWord
die: OtherWord
Hitliste: OtherWord
...
Did anyone tried to use the Class in other languages than English or is it only seriously usable when working with English text. I couldn't find any Apple documentation explaining the language capabilities beside from a list of languages that should be supported. Or am I doing something wrong?
Any comment pointing me to a solution is greatly appreciated.
Krid
I have not tested your above situation but I am attaching the following that I use to develop a part of speech tagger. It includes the setLanguage command and a setOthography command. (The latter, I have not yet experimented with yet).
My understanding is the tagger is to recognize the language and switch languages if needed or it can be set. It appears the logic used here is not fully revealed. I have determined that my best practice is set the language if I can. In this code the language stored as the the string language. (BTW, in my case is it done by reading a larger document that is also available.)
Lastly, I had a chance to see this in action this week. I was in an Apple store (in U.S.) on another matter and observed another customer testing a phone and discussing wanting to message in French. The tech demonstrated how if the iMessage continues to see French it will begin to understand. My thought observing this, is it did work, but it is better if the switch can be made externally if possible.
if let language = language {
// If language has a value, it is taken as a specification for the language of the text and set on the tagger.
let orthography = NSOrthography.defaultOrthography(forLanguage: language)
POStagger.setOrthography(orthography, range: range)
POStagger.setLanguage(NLLanguage(rawValue: language), range: range)
}
I'm trying to read the user set system preferences for Temperature unit (Celsius/Fahrenheit).
I was trying to get this data using NSLocale but I cannot find any evidence of a temperature setting in there.
Is it even possible to read this data?
Thanks!
The official API is documented under the Preferences Utilities:
let key = "AppleTemperatureUnit" as CFString
let domain = "Apple Global Domain" as CFString
if let unit = CFPreferencesCopyValue(key, domain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost) as? String {
print(unit)
} else {
print("Temperature unit not found")
}
If you wonder how I found it, I used the defaults utility in the Terminal:
> defaults find temperature
Found 1 keys in domain 'Apple Global Domain': {
AppleTemperatureUnit = Fahrenheit;
}
Found 1 keys in domain 'com.apple.ncplugin.weather': {
WPUseMetricTemperatureUnits = 1;
}
This is a bit of a hack, but you can do it this way on macOS 10.12+ and iOS 10+:
// Create a formatter.
let formatter = MeasurementFormatter()
// Create a dummy temperature, the unit doesn't matter because the formatter will localise it.
let dummyTemp = Measurement(value: 0, unit: UnitTemperature.celsius)
let unit = formatter.string(from: dummyTemp).characters.last // -> F
This outputs "F" in my playground which defaults to the US locale. But change your locale or use this code on a device and you'll get the locale specific temperature unit - or the string for it anyway.