Reputation: 6839
I am trying to implement a function that can change app language.
I tried to set it like this:
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject("de", forKey: "AppleLanguages")
And after app restart language is still 'system default'.
Now I test different languages by setting Scheme > Application Language > language
.
But is it possible to user click a button and after restart to see picked language?
Also what is the proper way to do this to avoid changing language on restart?
Upvotes: 12
Views: 26625
Reputation: 119
This solution work for me:
1. Create an extension of Bundle
import Foundation
class L012Localizer: NSObject {
class func DoTheSwizzling() {
MethodSwizzleGivenClassName(cls: Bundle.self, originalSelector: #selector(Bundle.localizedString(forKey:value:table:)), overrideSelector:
#selector(Bundle.specialLocalizedString(key:value:table:)))
}
}
extension Bundle {
@objc func specialLocalizedString(key: String, value: String?, table tableName: String?) -> String {
let currentLanguage = Utils.currentLanguage().rawValue
var bundle = Bundle();
if currentLanguage != "" , let _path = Bundle.main.path(forResource: currentLanguage, ofType: "lproj") {
bundle = Bundle(path: _path)!
} else {
let _path = Bundle.main.path(forResource: "Base", ofType: "lproj")!
bundle = Bundle(path: _path)!
}
return (bundle.specialLocalizedString(key: key, value: value, table: tableName))
}
}
func MethodSwizzleGivenClassName(cls: AnyClass, originalSelector: Selector, overrideSelector: Selector){
let origMethod: Method = class_getInstanceMethod(cls, originalSelector)!;
let overrideMethod: Method = class_getInstanceMethod(cls, overrideSelector)!;
if (class_addMethod(cls, originalSelector, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod))) {
class_replaceMethod(cls, overrideSelector, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
} else {
method_exchangeImplementations(origMethod, overrideMethod);
}
}
2. Call DoTheSwizzling in AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
L012Localizer.DoTheSwizzling()
return true
}
3. Create Language Utils
class Utils: NSObject {
class func setLanguage(_ lang: LanguageType) {
UserDefaults.standard.set([lang.rawValue], forKey: "AppleLanguages")
}
class func currentLanguage() -> LanguageType {
if let langs = UserDefaults.standard.object(forKey: "AppleLanguages") as? [String], let currentLang = langs.first {
return LanguageType(rawValue: currentLang) ?? .english
}
return .english
}
}
4. Create Language Type
enum LanguageType: String {
case english = "en"
case korea = "ko"
case vietnamese = "vi-VN"
func toString() -> String {
switch self {
case .korea:
return "Korea".localized
case .vietnamese:
return "Vietnamese".localized
default:
return "English".localized
}
}
}
5. Remember that you have to config Application Language in Scheme to SystemLanguage
6. Then Every time you need to localize your app, you only need to call. Utils.setLanguage({LanguageType})
Upvotes: 3
Reputation: 676
Updated Swift syntax of the main.swift
file mentioned in @royherma's answer. This will avoid having to restart the app after overriding the UserDefaults settings:
import Foundation
import UIKit
// Your initialization code here
let langCultureCode: String = "LANGUAGE_CODE"
UserDefaults.standard.set([langCultureCode], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()
UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, NSStringFromClass(AppDelegate.self))
paired together with the removal of @UIApplicationMain
in your AppDelegate.swift
file.
Upvotes: 0
Reputation: 2654
You may try below in AppDelegate.swift though changes will not appear instantly but after you kill and relaunch your app. -
NSUserDefaults.standardUserDefaults().removeObjectForKey("AppleLanguages")
NSUserDefaults.standardUserDefaults().setObject(["de"], forKey: "AppleLanguages"
NSUserDefaults.standardUserDefaults().synchronize()
Upvotes: 1
Reputation: 990
You have to set the AppleLanguages
key with an array, not a string:
UserDefaults.standard.set(["de"], forKey: "AppleLanguages")
Upvotes: 21
Reputation: 4203
Here is how you change the language prior to launch in swift
-
Lets say i want to force Hebrew
localization:
import Foundation
import UIKit
// Your initialization code here
let langCultureCode: String = "he_IL"
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject([langCultureCode], forKey: "AppleLanguages")
defaults.synchronize()
UIApplicationMain(Process.argc, Process.unsafeArgv, nil, NSStringFromClass(AppDelegate))
//@UIApplicationMain <-- COMMENT THIS OUT
class AppDelegate: UIResponder, UIApplicationDelegate
This will make your app forced to a locale WITHOUT needing any launch
Upvotes: 7
Reputation: 26383
If mean for test purpose, just change the language in simulator settings app.
If you are trying to make a sort of language selector in your app, it' pretty painful and in my opinion you should not.
Your app reads language and locale settings from the device and change the UI accordingly.
Override this behavior is tough and you will never accomplish a full change in language, for instance if you try to display a map in you app and your device language is Spanish, but the app language is english, you will see the map indications written in Spanish.
Here a project that could help.
Upvotes: 1
Reputation: 764
Yes you can change the app language immediately like,
var language = "de"
let path = NSBundle.mainBundle().pathForResource(language, ofType: "lproj")
let bundle = NSBundle(path: path!)
let string = bundle?.localizedStringForKey("key", value: nil, table: nil)
use your NSUserDefaults value to language.
Upvotes: 11