Force NSLocalizedString to use a specific language using Swift

14,215

Solution 1

It's not possible to change app's language immediately by changing the value of AppleLanguages. It requires restarting the app before the change takes effect.

It seems that your problem is accessing the localization strings of different languages rather than changing the app's language, right? If you want your app to support multiple languages, you can just provide the translations and rely on settings.app for the actual change.

If you want to access the localization strings from other than currently used localization, you need to get access to the proper translations bundle. And then just query that bundle for the translations. The following piece of code should do the trick.

let language = "en"
let path = Bundle.main.path(forResource: language, ofType: "lproj")
let bundle = Bundle(path: path!)
let string = bundle?.localizedStringForKey("key", value: nil, table: nil)

Solution 2

With NSLocalizedString you can specify the bundle.

let language = "fr"
let path = Bundle.main.path(forResource: language, ofType: "lproj")!
let bundle = Bundle(path: path)!
let localizedString = NSLocalizedString(key, bundle: bundle, comment: "")

Or with a bundle, you may also call localizedStringForKey:value:table: directly too.

Solution 3

@Radu I also made this working for XCUITests thanks to @Markus' original answer :

You can specify explicitly the path to your MainBundle, it will only work on your Mac with the Simulator, but it is often used in continuous integration platforms so this might be acceptable :

let language: String = "en"
let path = "/Users/{username}/{path_to_your_project}/\(language).lproj"
let bundle = Bundle(path: path)
let string = bundle?.localizedString(forKey: "key", value: nil, table: nil)

Solution 4

In swift 4, I have solved it without needing to restart or use libraries. After trying many options, I found this function, where you pass the stringToLocalize (of Localizable.String, the strings file) that you want to translate, and the language in which you want to translate it, and what it returns is the value for that String that you have in Strings file:

    func localizeString (stringToLocalize: String, language: String) -> String
    {
        let path = Bundle.main.path (forResource: language, ofType: "lproj")
        let languageBundle = Bundle (path: path!)
        return languageBundle! .localizedString (forKey: stringToLocalize, value: "", table: nil)
    }

Taking into account this function, I created it as global in a Swift file:

struct CustomLanguage {

    func createBundlePath () -> Bundle {
        let selectedLanguage = //recover the language chosen by the user (in my case, from UserDefaults)
        let path = Bundle.main.path(forResource: selectedLanguage, ofType: "lproj")
        return Bundle(path: path!)!
    }
}

To access from the whole app, and in each string of the rest of ViewControllers, instead of putting:

NSLocalizedString ("StringToLocalize", comment: “")

I have replaced it with

let customLang = CustomLanguage() //declare at top

NSLocalizedString("StringToLocalize", tableName: nil, bundle: customLang.createBundlePath(), value: "", comment: “”) //use in each String

I do not know if it's the best way, but I found it very simple, and it works for me, I hope it helps you!

Share:
14,215

Related videos on Youtube

sazz
Author by

sazz

Mobile developer -> iOS/Android. Javascript, AngularJS, HTML, CSS. Love to help people & solve the problems.

Updated on September 15, 2022

Comments

  • sazz
    sazz over 1 year

    With swift, how can I force my app to read data from a specific Localizable.strings.

    I put this in didFinishLaunchingWithOptions before instantiate the ViewController but it still show me the App in English.

    NSUserDefaults.standardUserDefaults().removeObjectForKey("AppleLanguages")
    NSUserDefaults.standardUserDefaults().setObject("fr", forKey: "AppleLanguages"   
    NSUserDefaults.standardUserDefaults().synchronize()
    

    And I tried to pass an Array for the "AppleLanguages" key like this but it still doesn't work:

    NSUserDefaults.standardUserDefaults().setObject(["fr"], forKey: "AppleLanguages"
    

    And once this is done, can I call this inside the App and take the changes in consideration without restarting the App?

  • sazz
    sazz over 9 years
    The language changes was taken in consideration but I missed to restart the app after executing my code.
  • Markus
    Markus over 9 years
    Good to know, if you want to change the language without the app restart, you do that with the aforementioned code example too. Just wrap it in a function/class and use it instead of NSLocalizedString. Though I'd recommend using the standard Apple way for changing the language.
  • Radu
    Radu over 8 years
    Doesn't work for me. path is nil. It may be that I cannot access these from XCUITests ? That would suck...
  • cdescours
    cdescours over 8 years
    @Radu I've also encountered the same problem with XCUITest, but made it work thank to Markus, see my answer below.
  • petrosmm
    petrosmm over 4 years
    Friendly reminder for anyone who may have missed out, I found that it looks into Localizable.strings not Main.strings! Hope this saves someone time! Edit: I found the answer to pull from Main.strings -> let string = bundle.localizedString(forKey: "Language", value: nil, table: "Main")
  • AppleBee
    AppleBee over 4 years
    It is not working for me I have created a global function to use everywhere in app. let value: String = NSLocalizedString(key, bundle: Bundle.main, comment: comment) debugPrint("default String value: (value)") guard let path: String = Bundle.main.path(forResource: "fr", ofType: "lproj"), let bundle: Bundle = Bundle(path: path) else { return value } debugPrint("lang specific value: (bundle.localizedString(forKey: key, value: nil, table: nil))") return bundle.localizedString(forKey: key, value: nil, table: "Main") }