I\'ve been stackling and googling for hours. And I\'m kind of desperate now. I would like to change the language of my application inside the app not only with the default l
According to Apple guidelines this is not a good idea to change language in the app programmatically, but in case u have no power to change requested behaviour, you can do something like next:
Prepare some service to manage your language even after app restart
enum LanguageName: String {
case undefined
case en
case es
}
let DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey = "DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey"
func dynamicLocalizableString(_ key: String) -> String {
return LanguageService.service.dynamicLocalizedString(key)
}
class LanguageService {
private struct Defaults {
static let keyAppleLanguage = "AppleLanguages"
static let keyCurrentLanguage = "KeyCurrentLanguage"
}
static let service:LanguageService = LanguageService()
var languageCode: String {
get {
return language.rawValue
}
}
var currentLanguage:LanguageName {
get {
var currentLanguage = UserDefaults.standard.object(forKey: Defaults.keyCurrentLanguage)
if let currentLanguage = currentLanguage as? String {
UserDefaults.standard.set([currentLanguage], forKey: Defaults.keyAppleLanguage)
UserDefaults.standard.synchronize()
} else {
if let languages = UserDefaults.standard.object(forKey: Defaults.keyAppleLanguage) as? [String] {
currentLanguage = languages.first
}
}
if let currentLanguage = currentLanguage as? String,
let lang = LanguageName(rawValue: currentLanguage) {
return lang
}
return LanguageName.undefined
}
}
func switchToLanguage(_ lang:LanguageName) {
language = lang
NotificationCenter.default.post(name: NSNotification.Name(rawValue: DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey), object: nil)
}
private var localeBundle:Bundle?
fileprivate var language: LanguageName = LanguageName.en {
didSet {
let currentLanguage = language.rawValue
UserDefaults.standard.set([currentLanguage], forKey:Defaults.keyAppleLanguage)
UserDefaults.standard.setValue(currentLanguage, forKey:Defaults.keyCurrentLanguage)
UserDefaults.standard.synchronize()
setLocaleWithLanguage(currentLanguage)
}
}
// MARK: - LifeCycle
private init() {
prepareDefaultLocaleBundle()
}
//MARK: - Private
fileprivate func dynamicLocalizedString(_ key: String) -> String {
var localizedString = key
if let bundle = localeBundle {
localizedString = NSLocalizedString(key, bundle: bundle, comment: "")
} else {
localizedString = NSLocalizedString(key, comment: "")
}
return localizedString
}
private func prepareDefaultLocaleBundle() {
var currentLanguage = UserDefaults.standard.object(forKey: Defaults.keyCurrentLanguage)
if let currentLanguage = currentLanguage as? String {
UserDefaults.standard.set([currentLanguage], forKey: Defaults.keyAppleLanguage)
UserDefaults.standard.synchronize()
} else {
if let languages = UserDefaults.standard.object(forKey: Defaults.keyAppleLanguage) as? [String] {
currentLanguage = languages.first
}
}
if let currentLanguage = currentLanguage as? String {
updateCurrentLanguageWithName(currentLanguage)
}
}
private func updateCurrentLanguageWithName(_ languageName: String) {
if let lang = LanguageName(rawValue: languageName) {
language = lang
}
}
private func setLocaleWithLanguage(_ selectedLanguage: String) {
if let pathSelected = Bundle.main.path(forResource: selectedLanguage, ofType: "lproj"),
let bundleSelected = Bundle(path: pathSelected) {
localeBundle = bundleSelected
} else if let pathDefault = Bundle.main.path(forResource: LanguageName.en.rawValue, ofType: "lproj"),
let bundleDefault = Bundle(path: pathDefault) {
localeBundle = bundleDefault
}
}
}
Add some rules to make sure you UI components will be always updated:
protocol Localizable {
func localizeUI()
}
Implement them
class LocalizableViewController: UIViewController {
// MARK: - LifeCycle
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(self.localizeUI), name: NSNotification.Name(rawValue:DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey), object: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
localizeUI()
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
extension LocalizableViewController: Localizable {
// MARK: - Localizable
func localizeUI() {
fatalError("Must Override to provide inApp localization functionality")
}
}
Inherit any controller u want to conform dynamic app switch functionality and implement localizeUI() func
final class WelcomeTableViewController: LoadableTableViewController
Switch language as needed:
LanguageService.service.switchToLanguage(.en)
All localizabled string should be set as :
label.text = dynamicLocalizableString()
Note: dont forget to add Localizable.strings with same codes as in LanguageName