iOS Swift Perfect Localization with Navigation

Author - Krishna Baldha

Localization is a process of translating the application’s text, resources and user interface into multiple languages. For localization, you need to first internationalize your application. Internationalization is the process of making your app able to adopt different languages, regions, cultures and user interface. In this blog, we will translate our app into the Hebrew language. Hebrew is written from right to left using Hebrew Alphabets. So we just have not to change the text but also have to change direction images and app’s navigation from right to left. Our app will be looked like this:

Let’s get started

First, create a single view application.

Click on the project folder in the project navigator and select project section. Click on the + button under the localizations section.

You will see the following list of applications. Select the language you want to add into your app.

After selecting the language, following popup will be appeared.

In this popup, there will be a list of all storyboards shown with checked. Select the storyboards which you want to localize. Click on the finish button. If we localize with the storyboard, then language changes only reflected when we quit the application.  In our demo, changes will reflect within the application’s open state. For that, we will localize our app programmatically.

Go to File > New > File. Then select Strings File from the list.

Give the file name “Localizable.strings”. Click on this file. Then click on Localize button. You will see the below popup. Select your language and then click on Localize button.

You will see English and Hebrew under the localization section. Select Hebrew language also.

Now you can see following file structure.

Now open Main.storyboard. Embed View controller in Navigation controller. Design title, tableView and next button as below.

We will save our selected language in UserDefaults with “Language” key.

 

Helper Class:

Create one Helper class for global methods. Add the following function for getting localize string.

class AppHelper: NSObject {

    static func getLocalizeString(str:String) -> String {
        let string = Bundle.main.path(forResource: UserDefaults.standard.string(forKey: "Language"), ofType: "lproj")
        let myBundle = Bundle(path: string!)
        return (myBundle?.localizedString(forKey: str, value: "", table: nil))!
    }
}

This function returns localize string of selected language. If English is selected then app’s navigation should be left to right direction and if Hebrew is selected then app’s navigation should be right to left. This will be done by the following code.

UIView.appearance().semanticContentAttribute = .forceLeftToRight
UIView.appearance().semanticContentAttribute = .forceRightToLeft

Write the below code didFinishLaunchingWithOptions method of AppDelegate.

if UserDefaults.standard.object(forKey: "Language") != nil && UserDefaults.standard.object(forKey: "Language") as! String == "he"
{
    UserDefaults.standard.set("he", forKey: "Language")
    UIView.appearance().semanticContentAttribute = .forceRightToLeft
}
else
{
    UserDefaults.standard.set("en", forKey: "Language")
    UIView.appearance().semanticContentAttribute = .forceLeftToRight
}
return true

For the first time of application install, we haven’t set any language in user defaults. So UserDefaults.standard.object(forKey: “Language”) will return nil. Therefore we set the English language for the first time install.

Write the localize string in Localizable.strings (English) and Localizable.strings (Hebrew) file.

Localizable String:

Localizable.strings (English) file :

“Choose Language” = “Choose Language”;
“English” = “English”;
“Hebrew” = “Hebrew”;
“Next” = “Next”;
“Country List” = “Country List”;
“India” = “India”;
“Australia” = “Australia”;
“Mexico” = “Mexico”;
“Kenya” = “Kenya”;
“China” = “China”;
“Bhutan” = “Bhutan”;
“Canada” = “Canada”;
“Japan” = “Japan”;
“Qatar” = “Qatar”;
“Israel” = “Israel”;”Country Description Here…” = “Country Description Here…”;

Localizable.strings (Hebrew) file:

“Choose Language” = “בחר שפה”;
“English” = “אנגלית”;
“Hebrew” = “עברית”;
“Next” = “הבא”;
“Country List” = “רשימת מדינות”;
“India” = “הודו”;
“Australia” = “אוסטרליה”;
“Mexico” = “מקסיקו”;
“Kenya” = “קניה”;
“China” = “סין”;
“Bhutan” = “בהוטאן”;
“Canada” = “קנדה”;
“Japan” = “יפן”;
“Qatar” = “קטאר”;
“Israel” = “ישראל”;
“Country Description Here…” = “תיאור המדינה כאן …”;

Here, I have written all the strings and its localizable that will use in our project.

Storyboards

Add two storyboard file named CountryListSB.storyboard and CountryDetailSB.storyboard in the project.

Add two ViewController file CountryListViewController.swift and CountryDetailViewController.swift file in the project.

ViewController.swift

Create following variables for storing language dictionary and selected language.
Initialize dictionary in viewDidLoad method.
Write the viewWillAppear method as follow :

var dictLanguage = [String:String]()
var selectedLanguage: String = ""
dictLanguage = ["en" : "English", "he":"עברית"]
override func viewWillAppear(_ animated: Bool)
{
    super.viewWillAppear(animated)
    self.tblLanguage.reloadData()
    selectedLanguage = dictLanguage[UserDefaults.standard.object(forKey: "Language") as! String]!
    lblTitle.text = AppHelper.getLocalizeString(str: "Choose Language")
    btnNext.setTitle(AppHelper.getLocalizeString(str: "Next"), for: .normal)        
}

Here is table view delegate and data source method:

extension ViewController: UITableViewDelegate, UITableViewDataSource
{
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        return dictLanguage.keys.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        
        let lblLanName = cell.viewWithTag(101) as! UILabel
        lblLanName.text = Array(dictLanguage)[indexPath.row].value
        
        let imgSelect = cell.viewWithTag(102) as! UIImageView
        if Array(dictLanguage)[indexPath.row].value == selectedLanguage
        {
            imgSelect.isHidden = false
        }
        else
        {
            imgSelect.isHidden = true
        }
        
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
    {
        tblLanguage.deselectRow(at: indexPath, animated: false)
        let cell = tblLanguage.cellForRow(at: indexPath)
        selectedLanguage = (cell?.viewWithTag(101) as! UILabel).text!
        tblLanguage.reloadData()
    }
}

Button Click Method:

Write Next button click method as below:

@IBAction func onClickBtnNext(_ sender: Any)
{
    if selectedLanguage != dictLanguage[UserDefaults.standard.object(forKey: "Language") as! String]
    {
        if selectedLanguage == "English"
        {
            UserDefaults.standard.set("en", forKey: "Language")
            UIView.appearance().semanticContentAttribute = .forceLeftToRight
        }
        else
        {
            UserDefaults.standard.set("he", forKey: "Language")
            UIView.appearance().semanticContentAttribute = .forceRightToLeft
        }
        let objStoryboard = UIStoryboard.init(name: "Main", bundle: nil)
        let rootNav = objStoryboard.instantiateViewController(withIdentifier: "rootNav") as! UINavigationController
        UIApplication.shared.keyWindow?.rootViewController = rootNav
        UIApplication.shared.keyWindow?.makeKeyAndVisible()
        let objListStoryboard = UIStoryboard.init(name: "CountryListSB", bundle: nil)
        let objListVC = objListStoryboard.instantiateViewController(withIdentifier: "CountryListViewController") as! CountryListViewController
        rootNav.pushViewController(objListVC, animated: true)
    }
    else
    {
        let objListStoryboard = UIStoryboard.init(name: "CountryListSB", bundle: nil)
        let objListVC = objListStoryboard.instantiateViewController(withIdentifier: "CountryListViewController") as! CountryListViewController
        self.navigationController!.pushViewController(objListVC, animated: true)
    }
}

In the above method, first, we have checked whether the selected language is equal to the previously selected language or not. If the currently selected language is the same as the previously selected language, then no need to change the application’s language. So simply push into the Country List view. Otherwise, check the selected language. If it is English then set it into UserDefaults and set app’s navigation left to right.

If the selected language is Hebrew, then set it into UserDefaults and set app’s navigation right to left. We have to set new rootViewController for getting the effect of newly set semantic. With the previous navigation, it is not possible to get the effect of new semantic so we created new navigation and set it into the application’s rootViewContoller. Then push view to the CountryListViewController. You can see that if the English language is selected then push animation will look from left to right and if Hebrew is selected then push animation will look from right to left.

Now open CountryListSB. Design title and tableview as below.

CountryListViewController.swift

Create a variable arrCountry for storing the country’s name in the selected language. and add data in viewDidLoad method as below:

var arrCountry = [String]()
override func viewDidLoad()
{
    super.viewDidLoad()
    arrCountry = [AppHelper.getLocalizeString(str: "India"),      
        AppHelper.getLocalizeString(str: "Australia"),
        AppHelper.getLocalizeString(str: "Mexico"),
        AppHelper.getLocalizeString(str: "Kenya"),
        AppHelper.getLocalizeString(str: "China"),
        AppHelper.getLocalizeString(str: "Bhutan"),
        AppHelper.getLocalizeString(str: "Canada"),
        AppHelper.getLocalizeString(str: "Japan"),
        AppHelper.getLocalizeString(str: "Qatar"),
        AppHelper.getLocalizeString(str: "Israel")]
        
    lblTitle.text = AppHelper.getLocalizeString(str: "Country List")
    if UserDefaults.standard.object(forKey: "Language") as! String == "he"
    {
        self.btnBack.imageView!.transform = CGAffineTransform(scaleX: -1, y: 1)
    }
}

Check Selected Language:

Here we have checked the selected language from UserDefaults and if it is Hebrew then rotate the back arrow image 180 degrees because as said before, in Hebrew language direction is right to left. We will rotate all directional images 180 degrees for the Hebrew language.

Write the following function for back button.

@IBAction func onClickBack(_ sender: Any)
{
    _ = self.navigationController?.popViewController(animated: true)
}

Here is table view delegate and data source method:

extension CountryListViewController: UITableViewDelegate, UITableViewDataSource
{
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        return arrCountry.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        
        let lblCountryName = cell.viewWithTag(101) as! UILabel
        lblCountryName.text = arrCountry[indexPath.row]
        
        let imgSelect = cell.viewWithTag(102) as! UIImageView
        if UserDefaults.standard.object(forKey: "Language") as! String == "he"
        {
            imgSelect.transform = CGAffineTransform(scaleX: -1, y: 1)
        }
        
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
    {
        let objDetailStoryboard = UIStoryboard.init(name: "CountryDetailSB", bundle: nil)
        let objDetailVC = objDetailStoryboard.instantiateViewController(withIdentifier: "CountryDetailViewController") as! CountryDetailViewController
        objDetailVC.countryName = arrCountry[indexPath.row]
        self.navigationController?.pushViewController(objDetailVC, animated: true)
    }
}

What’s next?

Here we have set arrCountry data in tableview. And on select cell, push the viewController to the country details view. We have passed countryName to the detail view so that it can be set to the title of that view.

Open CountryDetailSB.storyboard. Design title and tableview as below.

Here we have done a simple design with the country name and its description.

CountryDetailViewController.swift

We have created countryName variables and store the value that is passed from CountryListViewController.

Implement viewDidLoad method as below for setting country name and description.

var countryName: String = ""
override func viewDidLoad()
{
    super.viewDidLoad()
    lblTitle.text = AppHelper.getLocalizeString(str: countryName)
    lblDescription.text = AppHelper.getLocalizeString(str: "Country Description Here...")
    if UserDefaults.standard.object(forKey: "Language") as! String == "he"
    {
        self.btnBack.imageView!.transform = CGAffineTransform(scaleX: -1, y: 1)
    }
}

Implement back button action

@IBAction func onClickBack(_ sender: Any)
{
    _ = self.navigationController?.popViewController(animated: true)
}

Our Localization demo is complete. You can localize your app with these methods. Hope you enjoyed this blog.

Don’t miss the next post!

Loading

Related Posts