Localization in iOS tutorial using Swift

Learn the ins and outs about iOS localization

Cali Castle
Cali’s Tech Blog

--

This tutorial is updated with Swift 4, Xcode 9.2 ✔️

Okay, so you decided to add localization (l10n) to support a wider range of users. Congrats, that’s an awesome step further for your app! Well, in Xcode it’s actually very easy to get started in just a few seconds.

TL;DR

Let’s get started by choosing languages 😄

First things first, before you start translating interfaces and messages into different languages, make sure you have configured the language you desire to localize into. What you should do is open the Project Settings and select the Project of your app in the drop-down menu instead of the Target:

Project settings drop-down

Then you should be able to see something like this:

Now, start by clicking the + button below the Localization sections and select a language you’d like to localize into. And I am going to choose Chinese (Simplified) as an example here.

Then Xcode will ask you which files do you want to localize with the language you just chose, select all the files that apply and hit Finish:

(What does “Base” mean? Base language means your development language, the default language which you were already using under development, it is set to English by default in Xcode)

🙌🏻 You’re off to a good start

Believe it or not, it’s just as easy as that and you’re good to go with composing the actual translation files now.

Notice that Main.storyboard and LaunchScreen.storyboard now have a triangle icon ▶, which indicates the file has localization turned on. We can specify the localized string of every text you have already defined in the Storyboard or XIB files by clicking on the triangle icon ▶ to expand it:

Now the localization files are listed under the Main.storyboard file hierarchy.

Main.storyboard (Base) is the file you have been developing on.

Example of Main.strings with content in Storyboard

if I open Main.strings (Chinese (Simplified)), you’re going to see a file with a bunch of strings if your storyboard has some texts already.

However, I just created this project so there is nothing in my Main.storyboard yet and it means that my Main.strings file is empty.

Now it raises a question: “If I update my storyboard, let’s say I created a new label in my view controller, will the .strings get updated simultaneously?”

The answer is a bummer, unfortunately Xcode is not able to do that for you yet. You’ll have to manually define the string using the Object ID which we will discuss later.

Now I just added a Navigation Controller that has a simple Table View Controller:

The navigation item on the left has a “Sign in” button and on the right has a “Sign up” button.

Let’s say that I want to automatically localize “Sign in” and “Sign up” using the .strings file under the storyboard and localize using code to set the title of the Table View Controller to “Welcome”.

First, let’s select the bar button item on the left that says “Sign in” and open its Identity Inspector on the right side panel:

Great, now you can copy the Object ID of this Bar Button Item under Document section. In this case, my Object ID is W70–6p-Mom (totally random). And open its .strings file and type in this line:

// The syntax is: "ObjectID.{attribute}" = "Translation";
"W70–6p-Mom.title" = "登录";

The attribute is a bit tricky because in this case, UIBarButtonItem has only title attribute instead of what UILabel utilizes text attribute to set the text. Therefore, setting the text of a UILabel would be something like: "ABC-DE-FGH.text" = "Text";

Perfect! Now we repeat this process and paste in the other bar button item as well: (Feel free to add as many comments as you’d like to document translations)

/* Sign in button on the left */
"W70-6p-Mom.title" = "登录";
/* Sign up button on the right */
"o6T-iI-vYl.title" = "注册";

That’s it. We’re done. Simple as that. We just localized two buttons without writing a single line of code to make it happen. Let’s run the application and check it out.

Oh wait, the buttons are still in English. Why? Because we didn’t change the language on iPhone? Correct, but there is actually an easier way to check localization without setting the language of an iPhone.

Edit the scheme you’re going to run:

Change the Application Language under Options tab to the language you’d want to see (in my case Chinese (Simplified)).

Once you’ve got that done, run the app again and you shall see the magic appears:

Voila, the texts are now in Chinese (Simplified).

Let us dig deeper and write some code now to change the title of the current view controller.

Moving on, how to localize with code ⌨️

In viewDidLoadmethod of the View Controller, let’s change the title:

class ViewController: UITableViewController {

override func viewDidLoad() {
super.viewDidLoad()

title = "Welcome"
}

}

To use localized strings, first you have to create a .strings file called Localizable.strings:

Select Strings File and click next, name it Localizable and save.

And enter "Welcome" = "Welcome";

The last step is to click Localize on the right panel:

And Xcode will prompt and ask you where do you want to move the current file to:

Because what I just entered "Welcome" = "Welcome"; is an English string, so keep the selection to English and click Localize button.

Now, check all languages you have configured in the Project Settings:

And open Localizable.strings in Chinese (Simplified), then change the “Welcome” text to “欢迎”:

"Welcome" = "欢迎";

Finally, let’s change title = "Welcome" in your View Controller to NSLocalizedString instead of a string literal:

override func viewDidLoad() {
super.viewDidLoad()

title = NSLocalizedString("Welcome", comment: "")
}

We pass the lookup key to the first argument here, so it matches whatever localization it is defined which means you can instead write: "home.title" = "Welcome"; and localize it to Chinese: "home.title" = "欢迎"; . Then you call NSLocalizedString("home.title", comment: "") and you’ll get the same result.

Run the application again:

Works like a charm! Now you’ve got it. That’s how you localize your app interface without writing a single line of code and how you localize using code to have more control.

Congratulations on making it this far! 🎉

If you would like to learn more about localization like refactoring with Extension in Swift and dealing with plurals. Then continue reading! You’ll find these somewhat helpful for your development.

All about reusable code

It would be really a pain to write NSLocalizedString("Key", comment: "") each time you want to set a localized string, wouldn’t it?

Let’s refactor and make it more reusable:

First, create a Swift file called +String.swift (the plus “+” means that it’s an extension, it’s a naming convention of mine, you can just call it String.swift if you’d prefer):

extension String {

var localized: String {
return NSLocalizedString(self, comment: "")
}

}

We create a new instance variable called localized , it’s sort of like a shortcut to get a localized string. Using it would be much easier now:

title = "Welcome".localized

And you get the same result as before, how about that. Now it’s easier to read too.

If you want to pass comment argument to NSLocalizedString as well, then add the following code into the String extension declaration in +String.swift

func localized(comment: String = "") -> String {
return NSLocalizedString(self, comment: comment)
}

Which means that now you can call "Welcome".localized(comment: "Comment") to do the same thing.

How to handle plurals?

First and foremost, why do we need to handle plurals differently? Can’t we just define it in .strings files and just get done with it?

The truth is, one of the most complicated issues in localization is plural.

In English, one ship and two ships are different because it adds an “s” suffix to indicate that its plural form. In Chinese, however, it is much more simple than English. For example, ship in Chinese is 船, no matter there is one or two or thousands of them, the noun stays unchanged: 一艘船(one ship), 两艘船(two ships). Similarly, in German, ein Schiff (one ship) and zwei Schiffe (two ships) are different in plural form too, but Tausende von Schiffen (thounds of ships) uses Schiffen instead of Schiffe because there’s a von before it. As you can see, localization is not just about translation. It can be far more complicated.

In order to tackle this, we need .stringsdict file instead of a simple .strings file. Create a Localizable.stringsdict file:

Using the template Xcode provides is a pretty good way to get started:

There are 3 places you need to define your own here:

Let’s say that I want to localize “You have been using this app for % days”.

Then I changed Localized String Key in Localizable.stringsdict file to You have been using this app for %d day(s) and the value in Localized Format Key to %#@days@ (swap VARIABLE with days)

Lastly, set the Variable to the variable you just defined, which is days:

Now we need to change the value in NSStringFormatValueTypeKey to d. (Because %d formats as an integer to a string)

All you have to do is type in different strings that will be matched automatically. For example, if the user just started using this app, it doesn’t make sense for the user to see such text like “You have been using this app for 0 days”, it’s much better if you say “You have just installed this app today”, right?

Write localization in these different cases:

Zero means when the variable is 0, one means 1, two means 2.

Few and Many are used for additional language-dependent categories.

Other is required for general purposes localization.

All set! Now we just have to add an extra function in String extension for handling plurals:

func localized(with variable: CVarArg, comment: String = "") -> String {
return String(format: localized(comment: comment), [variable])
}

In View Controller, add this for the table header:

"You have been using this app for %d day(s)".localized(with: 0)

Localize Localizable.stringsdict using the steps above to Chinese (Simplified):

Run the application:

Change the variable to 1 and change scheme language back to System Settings:

"You have been using this app for %d day(s)".localized(with: 1)

That’s it for handling localization plurals. Not that hard, isn’t it?

Thank you for reading this far, you’re awesome! 👍🏻

If you like this article or find it helpful to you, please share and clap as hard as you can 😄

Follow me: LinkedIn

Stay tuned and connected for more articles in iOS development.

--

--

Cali Castle
Cali’s Tech Blog

iOS Engineer, Full Stack Web Developer./Designer Never stop learning