Skip to main content

iOS localization on the fly

If you have added languages to your app... shortly afterwards you'll be getting yourself knee deep in changing the language on the fly.  Followed by the question – how do you change the language of the app without having to restart the app?  To change the language of the app there are a couple of key steps involved:

  1. Adding / managing languages
  2. Managing the app bundle

By the way I have added to GitHub a demo app - switch languages showing how this works.

Keeping the design clean, there is only one word on this screen which is a button.  Using Unsplash, I downloaded a great photo by Paul Volkmer.  

Home screen in English of localization demo

 

Adding languages

If you don't know how to add languages to your app, read through iOS localization to your app.

 

Managing languages

To begin is there something that already exists to speed up the multi-language journey?  Coming across Switch Language via Cocoapods... Switch Language was a great stepping stone to resolving this issue.  [Update] I no longer use the pod Switch Language.  As it hasn't been updated for a few years and now requires a work around due to errors with current version of Xcode.  Don't get me wrong, this can be resolved...  however, I found it easier to work through the challenge via a different method.  Being that there is a section of their code that I'm using in this demo (in the file Language.swift).   

Generally, language abbreviation is to be set a two alpha character such as:

  • en (English)
  • et (Estonian)
  • fr (French)
  • ja (Japanese)
  • pt (Portuguese)
  • ru (Russian)
  • etc...

However, there is an exception to this list.  When using Xcode's localizations area, as noted above most language conform to two alpha characters.  The exception is Chinese which can be:

  • zh-HK (Hong Kong)
  • zh-Hans (Simplified)
  • zh-Hant (Traditional)

 

Key parts of the code to add to your app

Need to know the character code for language you are using?  There are plenty of reference sites - I have found Data Hub a handy reference.

// set current language:
Language.setCurrentLanguage(languageAbbreviation)
UserDefaults.standard.set([languageAbbreviation], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()

The bundle set language needs to be called when transitioning to the next screen.

func languageButtonAction() {
  // Update the language by swaping bundle
  Bundle.setLanguage(Language.getCurrentLanguage())
  // Done to reintantiate the storyboards instantly
  let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
  UIApplication.shared.keyWindow?.rootViewController = storyboard.instantiateInitialViewController()
}

When the language is changed, then the bundle class is called (see Managing the app bundle) below.

 

Managing the app bundle

After an enormous amount of searching (Google is my friend!), I found one solution that worked for me on the e-learn site reference six (6).  

Create a file named BundleExtension.swift and add the following code to it -

var bundleKey: UInt8 = 0

class AnyLanguageBundle: Bundle {

  override func localizedString(forKey key: String,
                              value: String?,
                              table tableName: String?) -> String {

    guard let path = objc_getAssociatedObject(self, &bundleKey) as? String,
        let bundle = Bundle(path: path) else {
        return super.localizedString(forKey: key, value: value, table: tableName)
    }

    return bundle.localizedString(forKey: key, value: value, table: tableName)
  }
}

extension Bundle {

  class func setLanguage(_ language: String) {
    defer {
        object_setClass(Bundle.main, AnyLanguageBundle.self)
    }
    objc_setAssociatedObject(Bundle.main, &bundleKey, Bundle.main.path(forResource: language, ofType: "lproj"), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  }
}

Now whenever you need to change the language call this method:

func languageButtonAction() {
    // This is done so that network calls now have the Accept-Language as Language.getCurrentLanguage() (Using Alamofire) Check if you can remove these
    UserDefaults.standard.set([Language.getCurrentLanguage()], forKey: "AppleLanguages")
    UserDefaults.standard.synchronize()

    // Update the language by swaping bundle
    Bundle.setLanguage(Language.getCurrentLanguage())

    // Done to reintantiate the storyboards instantly
    let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
    UIApplication.shared.keyWindow?.rootViewController = storyboard.instantiateInitialViewController()
}

That is it! Not too much work at all and now you have the app language changing at the flick of a switch.

 

English sample of the settings screen with text beneath ready to show the localization changes.

Settings screen in English of localization demo

 

Simplified Chinese sample of the settings screen showing the difference between the image above.  

Settings screen in simplified Chinese of localization demo

 

Switch language demo app

Okay that is good to know, however, where does the above code go?  If you are struggling in making this functionality work, I have created a demo version of the language on GitHub...

GitHub go to the demo switch languages.

Comments

Related articles

Andrew Fletcher12 Aug 2022
Using SwiftUI URLComponent to change a URL's scheme
The challenge I was facing, I had written a script to scan barcodes and use Google book API to view the contents.  However, a snippet of the JSON response { "contentVersion": "0.2.0.0.preview.0", "panelizationSummary": { "containsEpubBubbles": false, ...