1110
1110

Reputation: 6839

Setting "AppleLanguages" doesn't change app language

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

Answers (7)

Neo Nguyen
Neo Nguyen

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
enter image description here
6. Then Every time you need to localize your app, you only need to call. Utils.setLanguage({LanguageType})

Upvotes: 3

Andreas
Andreas

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

Santu C
Santu C

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

Junfeng
Junfeng

Reputation: 990

You have to set the AppleLanguages key with an array, not a string:

UserDefaults.standard.set(["de"], forKey: "AppleLanguages")

Upvotes: 21

royherma
royherma

Reputation: 4203

Here is how you change the language prior to launch in swift -

Lets say i want to force Hebrew localization:

1. Create your own Main.swift class

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))

2. Remove your AppDelegate.swift "main" responsibility

//@UIApplicationMain <-- COMMENT THIS OUT
class AppDelegate: UIResponder, UIApplicationDelegate 

This will make your app forced to a locale WITHOUT needing any launch

Upvotes: 7

Andrea
Andrea

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

Paddy
Paddy

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

Related Questions