Changing language on the fly, in running iOS, programmatically

后端 未结 7 998
没有蜡笔的小新
没有蜡笔的小新 2020-11-29 22:45

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

7条回答
  •  渐次进展
    2020-11-29 23:00

    This is an old question, but i was developing an helper that notifies me when the language change on the fly.

    Take a look at the code of helper:

    import Foundation
    
    class LocalizableLanguage {
    
        // MARK: Constants
    
        fileprivate static let APPLE_LANGUAGE_KEY = "AppleLanguages"
    
        /// Notification Name to observe when language change
        static let ApplicationDidChangeLanguage = Notification.Name("ApplicationDidChangeLanguage")
    
        // MARK: Properties
    
        /// An array with all available languages as String
        static var availableLanguages: [String]? = {
            return UserDefaults.standard.object(forKey: APPLE_LANGUAGE_KEY) as? [String]
        }()
    
        /// The first element of available languages that is the current language
        static var currentLanguageCode: String? = {
            return availableLanguages?.first
        }()
    
        /// The current language code with just 2 characters
        static var currentShortLanguageCode: String? = {
            guard let currentLanguageCode = currentLanguageCode else {
                return nil
            }
    
            let strIndex = currentLanguageCode.index(currentLanguageCode.startIndex, offsetBy: 2)
            return currentLanguageCode.substring(to: strIndex)
        }()
    
        // MARK: Handle functions
    
        /// This accepts the short language code or full language code
        /// Setting this will send a notification with name "ApplicationDidChangeLanguage", that can be observed in order to refresh your localizable strings
        class func setLanguage(withCode langCode: String) {
    
            let matchedLangCode = availableLanguages?.filter {
                $0.contains(langCode)
            }.first
    
            guard let fullLangCode = matchedLangCode else {
                return
            }
    
            var reOrderedArray = availableLanguages?.filter {
                $0.contains(langCode) == false
            }
    
            reOrderedArray?.insert(fullLangCode, at: 0)
    
            guard let langArray = reOrderedArray else {
                return
            }
    
            UserDefaults.standard.set(langArray, forKey: APPLE_LANGUAGE_KEY)
            UserDefaults.standard.synchronize()
    
            LocalizableLanguage.refreshAppBundle()
    
            NotificationCenter.default.post(name: ApplicationDidChangeLanguage, object: fullLangCode)
        }
    }
    
    // MARK: Refresh Bundle Helper
    
    private extension LocalizableLanguage {
    
        class func refreshAppBundle() {
            MethodSwizzleGivenClassName(cls: Bundle.self, originalSelector: #selector(Bundle.localizedString(forKey:value:table:)), overrideSelector: #selector(Bundle.specialLocalizedStringForKey(_:value:table:)))
        }
    
        class 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);
            }
        }
    }
    
    extension Bundle {
    
        func specialLocalizedStringForKey(_ key: String, value: String?, table tableName: String?) -> String {
    
            let availableLanguages = UserDefaults.standard.object(forKey: LocalizableLanguage.APPLE_LANGUAGE_KEY) as? [String]
            let currentLanguageCode = availableLanguages?.first ?? "en-US"
            let currentShortLanguageCode = currentLanguageCode.substring(to: currentLanguageCode.index(currentLanguageCode.startIndex, offsetBy: 2))
    
            let path =
                    Bundle.main.path(forResource: currentLanguageCode, ofType: "lproj") ??
                    Bundle.main.path(forResource: currentShortLanguageCode, ofType: "lproj") ??
                    Bundle.main.path(forResource: "Base", ofType: "lproj")
    
            guard
                self == Bundle.main,
                let bundlePath = path,
                let bundle = Bundle(path: bundlePath)
            else {
                return self.specialLocalizedStringForKey(key, value: value, table: tableName)
            }
    
            return bundle.specialLocalizedStringForKey(key, value: value, table: tableName)
        }
    }
    

    You just need to copy that code and put in your project.

    Then, you simple implement the listener like this:

    NotificationCenter.default.addObserver(forName: LocalizableLanguage.ApplicationDidChangeLanguage, object: nil, queue: nil) { notification in
                guard let langCode = notification.object as? String else {
                    return
                }
                self.accountStore.languageCode.value = langCode
            } 
    

    Note that this line self.accountStore.languageCode.value = langCode is what i need to refresh when the app language as changed, then i can easily change all strings of my ViewModels in order to change the language to the user immediately.

    In order to change the language, you can just call:

    LocalizableLanguage.setLanguage(withCode: "en")
    

    Other helper that could be nice to you is:

    import Foundation
    
    extension String {
    
        var localized: String {
            return NSLocalizedString(self, comment: "")
        }
    
    }
    

    So if you have in your localizable files something like that:

    main.view.title = "Title test";
    

    You can simple call:

    "main.view.title".localized
    

    And you have your string translated.

提交回复
热议问题