Change the language of iOS 13

Please tell me maybe you know how to do this. It is necessary to programmatically change the selected location. In iOS 13, you can select it in the settings. Interestingly, you can change in a local language change (if you changed the localization in the application) immediately.

Replies

Could you clarify what is your question ?

the user changes the language of the application in the application, it is necessary that it changes in the application settings.

https://www.icloud.com/iclouddrive/0K1YrvSJwpcjhXcCnpO66Jf1w#IMG_0074

This choice needs to be changed from the application.

OK, so that is not a question but a request for change.


You'd better file a bug report for enhancement (this has been done by others, but you should request and explain as well).


I do agree, it is a pain that it is not possible to do it in app ; there are cases where that would be much needed.

Note: you can do it in fact, but you have to manage the localization yourself, which is not very hard, but not convenient ; in fact, this is needed if you want to localize in a language that is not supported.

Is it not possible to change the selected language?

Not possible with authorized API.

there is an application that can change the language in the settings

Yes it is technically possible, but you should be warned that it may have side effects. The safe way is to do it yourself. I did it in one app.


May also have a look at this old threads.

https://forums.developer.apple.com/message/95091#95091

https://forums.developer.apple.com/message/335094#335094

https://forums.developer.apple.com/thread/97110


I did file a bug report, 2 years ago, with the following content:

FB5433678 (37636544)

Here is the use case : Working with a foreign friend, it may be needed to change the language on the fly, each time one or the other has to do something with the app.

There is no easy or safe way to do this directly from the app without being forced to go through the phone setting, which forces to interrupt use of app and takes a long time (up to 15s).

I have found ways on how to do it, by changing the mainBundle class.

See: h ttp://blog.xebia.fr/2016/08/03/internationaliser-vos-applications-ios/

It seems to work well, but I was advised not to do so, because of possible bad side effects.

See discussion in forum : https://forums.developer.apple.com/thread/97110

Tell me how you did it?


https://www.icloud.com/iclouddrive/0gCoW3CtjbW4hrLo7XvNrMVZw#RPReplay_Final1583649661


solution


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

Here is how I did it. It is a bit complex (that's why localization is so interesting)


I localize separately items that are defined statically in IB and those defined dynamically in code (somehow in the same way it is done with Main.strings and localizable.strings)


I created an enum, with a computed property dictionary traductions, which returns, for a given language, the localized string of each IB element when it is static text.


I copy the IB item Object ID into its restoration ID, for every object.


enum Language: String {
    case french = "fr-FR"
    case english = "en-US"
    case italian = "it-IT"
    case spanish = "es-ES"

    static let allLanguages = [french, english, italian, spanish]

    var traductions: [String:String] {
        var trados = [String:String]()
        switch self {
        case .french :
            trados["xle-Bf-Akb"] = "texte en français"
            // many more like this
        case .english :
            trados["xle-Bf-Akb"] = "text in english"
            // many more like this
        case .italian :
            trados["xle-Bf-Akb"] = "testo in italiano"
            // many more like this
        case .spanish :
            trados["xle-Bf-Akb"] = "texto en español"
            // many more like this
          }
        return trados
    }
}


I declared a global for dynamic texts:


var textesTraduits = [String: [Language:String]] ()  // Dynamic texts: translation of each field (by its restoration ID) in each language

Now this is done in each VC.

In the code, each time I define a dynamic string for some object, I populate textesTraduits


    @IBAction func numericEntered(_ sender: UITextField) {     // I validate a text field ; I could have to set several items on display, just show for one

        var allTexts = [Language:String]()
        textesTraduits["VI0-db-g0w"] = allTexts // empty, a priori for this given ID
          // Here is for a label with restoration ID of "6XA-e2-fHz"
            allTexts[.french] = "Pas un chiffre"
            allTexts[.english] = "Not a number"
            allTexts[.italian] = "Non un numero"
            allTexts[.spanish] = "No un numero"
            errorLabel.text = allTexts[gSelectedLanguage]
            textesTraduits["6XA-e2-fHz"] = allTexts     // That will be used when I toggle language later
}

I set the language via a segmentedControl and store the selection in a global var


var gSelectedLanguage : Language = .french // To start with



Now, in each VC, I call this func on viewWillAppear (I could also call it if notified that language has changed, but in my case I didn't need it)

I call similar code when I change the segmentedControl, as well as in viewDidLoad in the VC that contains this control.


    func setLanguage() {
       
        let allViews = self.view.getAllSubviews()
        for sub in allViews { 
            if let label = sub as? UILabel, let id = sub.restorationIdentifier {
                // Static messages are in Languages enum
                if let traduit = gSelectedLanguage.traductions[id] {
                    label.text = traduit
                } else if let traduit = textesTraduits[id] {
                    //  Translate dynamic message referenced by restoration ID
                    label.text = traduit[gSelectedLanguage]
                }
            }
            // for textFields, 
                if let textView = sub as? UITextField, let id = sub.restorationIdentifier {
                    if let traduit = textesTraduits[id], !traduit.isEmpty { 
                        textView.text = traduit[gSelectedLanguage]
                    }
                }
           
            if let button = sub as? UIButton, let id = sub.restorationIdentifier {
                if let traduit = gSelectedLanguage.traductions[id] {
                    button.setTitle(traduit, for: .normal)
                } else if let traduit = textesTraduits[id] {
                    button.setTitle(traduit[gSelectedLanguage], for: .normal)
                }
            }
            if let textView = sub as? UITextView, let id = sub.restorationIdentifier {
                if let traduit = gSelectedLanguage.traductions[id] {
                    textView.text = traduit
                } else if let traduit = textesTraduits[id] {
                    textView.text = traduit[gSelectedLanguage]
                }
            }
        }
        // For a button that was created in code, we don't have the restoration ID
        var buttonName = "↵"
        switch gSelectedLanguage {
        case .french : buttonName = "retour"
        case .english: buttonName = "done"  
        case .italian: buttonName = "invio"  
        case .spanish: buttonName = "entrar"
        }
        returnButton.setTitle(buttonName, for: UIControl.State())
    }



I told you it was a bit complex (probably can be simplified a bit, but it works well). I can even add languages that are not listed in the languages supported by iOS.


Some caveat: the app does not localize when you change the iPhone setting, unless you restart the app. But that's what we wanted, isn't it ? And if you have selected italian in app when iPhone setting is english, the apps settings in General settings will be english, not italian.


with this mechanism, you can switch language on the fly, for instance when you rotate the device of flip form portrait up to protrait down… Pretty cool.