Changing language on the fly, in running iOS, programmatically

后端 未结 7 1010
没有蜡笔的小新
没有蜡笔的小新 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

    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:

    1. 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
              }
          }
      }
      
    2. Add some rules to make sure you UI components will be always updated:

      protocol Localizable {
          func localizeUI()
      }
      
    3. 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")
          }
      }
      
    4. Inherit any controller u want to conform dynamic app switch functionality and implement localizeUI() func

      final class WelcomeTableViewController: LoadableTableViewController
      
    5. Switch language as needed:

      LanguageService.service.switchToLanguage(.en)
      
    6. All localizabled string should be set as :

      label.text = dynamicLocalizableString()
      

    Note: dont forget to add Localizable.strings with same codes as in LanguageName

提交回复
热议问题