Internationalizing Your iOS App: Getting Started

Richard Critz
Update note: This tutorial has been updated for iOS 11 and Xcode 9 by Richard Critz. The original tutorial was written by Sean Berry with updates by Ali Hafizji.

Creating a great iOS app is no small feat, yet there is much more to it than great code, gorgeous design and intuitive interaction. Climbing the App Store rankings requires well-timed product marketing, the ability to scale up along with the user base, and utilizing tools and techniques to reach as wide an audience as possible.

For many developers, international markets are an afterthought. Thanks to the painless global distribution provided by the App Store, you can release your app in over 150 countries with a single click. Asia and Europe alone represent a continually growing pool of potential customers, many of whom are not native English speakers. In order to capitalize on the global market potential of your app, you’ll need to understand the basics of app internationalization and localization.

This tutorial will guide you through the basic concepts of internationalization by taking a simple app called iLikeIt and adding internationalization support. This simple app has a label and a You Like? button. Whenever the user taps the button, some fictitious sales data and a cheerful image fade in below the button.

Currently, the app is English only; time to fix that!

Note: Because changing languages can also change the size of UI elements, it is crucial that you use Auto Layout in any app that you plan to internationalize.

Internationalization vs Localization

Making your app speak another language requires both internationalization and localization. Aren’t they just two words for the same thing? Not at all! They represent separate and equally important steps in the process of bringing your app into the world of multiple languages.

Internationalization is the process of designing and building your app for international compatibility. This means, for example, building your app to:

  • Handle text input and output processing in the user’s native language.
  • Handle different date, time and number formats.
  • Utilize the appropriate calendar and time zone for processing dates.

Internationalization is an activity that you, the developer, perform by utilizing system-provided APIs and making the necessary modifications to your code to make your app as good in Chinese or Arabic as it is in English. These modifications allow your app to be localized.

Localization is the process of translating an app’s user interface and resources into different languages. Unless you happen to be fluent in the language you’re supporting, this is something you can, and should, entrust to someone else.

Getting Started

Download the starter project here. Build and run; tap You like?. You should see something similar to the following:

iOS Internationalization

As you can see, you will need to localize four items:

  • The “Hello” label
  • The “You Like?” button
  • The “Sales Data” label
  • The text in the image

Take a moment to browse the project’s files and folders to familiarize yourself with the project structure. Main.storyboard contains a single screen which is an instance of MainViewController.

Separating Text From Code

Currently, all of the text displayed by the app exists as hard-coded strings within Main.storyboard and MainViewController.swift. In order to localize these strings, you must put them into a separate file. Then, rather than hard-coding them, you will retrieve the appropriate strings from this separate file in your app’s bundle.

iOS uses files with the .strings file extension to store all of the localized strings used within the app, one or more for each supported language. A simple function call will retrieve the requested string based on the current language in use on the device.

Choose File\New\File from the menu. In the resulting dialog, select iOS\Resource\Strings File and click Next.

new strings file

Name the file Localizable and click Create.

Note: Localizable.strings is the default filename iOS uses for localized text. Resist the urge to name the file something else unless you want to type the name of your .strings file every time you reference a localized string.

A .strings file contains any number of key-value pairs, just like a Dictionary. Conventional practice uses the development, or base, language translation as the key. The file has a specific, but simple, format:

"KEY" = "CONTENT";
Note: Unlike Swift, the .strings file requires that each line terminate with a semicolon.

Add the following to the end of Localizable.strings:

"You have sold 1000 apps in %d months" = "You have sold 1000 apps in %d months";
"You like?" = "You like?";

As you can see, you may include format specifiers in either the key or the value portion of the string to allow you to insert real data at run time.

NSLocalizedString(_:tableName:bundle:value:comment:) is the primary tool you use in your code to access these localized strings. The tableName, bundle, and value parameters all have default values so you normally specify only the key and the comment. The comment parameter is there for you to provide a hint to translators as to what purpose this string serves in your app’s user experience.

Open MainViewController.swift and add the following function:

override func viewDidLoad() {
  super.viewDidLoad()
  
  likeButton.setTitle(NSLocalizedString("You like?", comment: "You like the result?"),
                      for: .normal)
}

Here, you update the title on the button using the localized value. Now, in the function likeButtonPressed(), find the following line:

salesCountLabel.text = "You have sold 1000 apps in \(period) months"

Replace that line with:

let formatString = NSLocalizedString("You have sold 1000 apps in %d months",
                                     comment: "Time to sell 1000 apps")
salesCountLabel.text = String.localizedStringWithFormat(formatString, period)

You use the String static function localizedStringWithFormat(_:_:) to substitute the number of months in your sales period into the localized string. As you’ll see later, this way of performing the substitution respects the user’s locale setting.

Note: The comment strings in this tutorial have been purposely kept short to make them format nicely on-screen. When writing them in your own code, take the time to make them as descriptive as possible. It will help your translators significantly and result in better translations.

Build and run. Your project should appear exactly as it did before.

Adding a Spanish Localization

To add support for another language, click on the blue iLikeIt project folder in the Project navigator and select the iLikeIt Project in the center pane (NOT the Target). On the Info tab, you will see a section for Localizations. Click the + and choose Spanish (es) to add a Spanish localization to your project.

adding a localization

Xcode will list the localizable files in your project and allow you to select which ones it should update. Keep them all selected and click Finish.

select files to add for Spanish

But wait! Where is Localizable.strings? Don’t worry; you haven’t marked it for localization yet. You’ll fix that shortly.

At this point, Xcode has set up some directories, behind the scenes, to contain localized files for each language you selected. To see this for yourself, open your project folder using Finder, and you should see something similar to the following:

new project structure

See en.lproj and es.lproj? They contain the language-specific versions of your files.
“en” is the localization code for English, and “es” is the localization code for Spanish. For other languages, see the full list of language codes.

So what is Base.lproj? Those are the files in the base, or development, language — in this case, English. When your app asks iOS for the localized version of a resource, iOS looks first in the appropriate language directory. If that directory doesn’t exist, or the resource isn’t there, iOS will fetch the resource from Base.lproj.

It’s that simple! Put your resources in the appropriate folder and iOS will do the rest.

Localizable.strings doesn’t do much good unless it exists in these .lproj directories. Tell Xcode to put it there by selecting it in the Project navigator, then clicking Localize… in the File inspector.

localize Localizable.strings

Xcode will ask you to confirm the file’s language. The default will be English since that’s your development language. Click Localize.

Select localization language

The File inspector will update to show the available and selected languages. Click the checkbox next to Spanish to add a Spanish version of the file.

Add Spanish Localizable.strings

Look at the Project navigator. Localizable.strings now has a disclosure triangle next to it. Expand the list and you’ll see that Xcode lists both the English and Spanish versions.

expand Localizable.strings group

Select Localizable.strings (Spanish) in the Project navigator and replace its contents with the following:

"You have sold 1000 apps in %d months" = "Has vendido 1000 aplicaciones en %d meses";
"You like?" = "¿Es bueno?";

Xcode makes it easy to test your localizations without the bother of constantly changing languages or locales on your simulator. Click the active scheme in the toolbar and choose Edit scheme… from the menu (you can also Option-Click on the Run button).

edit scheme

The Run scheme will be selected by default and that’s the one you want. Click the Options tab, then change Application Language to Spanish. Click Close.

set application language to Spanish

Build and run and you should see something like this when you click ¿Es bueno?:

first run in Spanish

Hooray! There’s some Spanish in your app!

Internationalizing Storyboards

UI elements in your storyboard, such as labels, buttons and images, can be set either in your code or directly in the storyboard. You have already learned how to support multiple languages when setting text programmatically, but the Hello label at the top of the screen has no IBOutlet and only has its text set within Main.storyboard.

You could add an IBOutlet, connect it to the label in Main.storyboard, then set its text property using NSLocalizedString(_:tableName:bundle:value:comment:) as you did with likeButton and salesCountLabel, but there is a much easier way to localize storyboard elements, without the need for additional code.

In the Project navigator, open the disclosure triangle next to Main.storyboard and you should see Main.storyboard (Base) and Main.strings (Spanish).

storyboard Spanish localization

Click on Main.strings (Spanish) to open it in the editor. You should already have an entry for the Hello label which will look something like this:

/* Class = "UILabel"; text = "Hello"; ObjectID = "jSR-nf-1wA"; */
"DO NOT COPY AND PASTE.text" = "Hello";

Replace the English translation with the Spanish translation:

/* Class = "UILabel"; text = "Hello"; ObjectID = "jSR-nf-1wA"; */
"DO NOT COPY AND PASTE.text" = "Hola";
Note: Never directly change the auto-generated ObjectID. Also, do not copy and paste the lines above, as the ObjectID for your label may be different from the one shown above.

Change the localizations for the other two entries. Again, do not edit the unique ObjectID:

"DO NOT COPY AND PASTE.text" = "Has vendido 1000 aplicaciones en 20 meses";
"DO NOT COPY AND PASTE.normalTitle" = "¿Es bueno?";

Xcode lets your preview your storyboard localizations. Select Main.storyboard in the Project navigator and open the assistant editor with View\Assistant Editor\Show Assistant Editor. Make sure it is showing the preview of the storyboard:

assistant editor preview

Click the language menu in the lower right corner and select Spanish.

select Spanish

Your preview, except for the image, should be in Spanish.

Spanish preview

Internationalizing Images

Since the app uses an image that contains English text, you will need to localize the image itself. Unfortunately, while Apple recommends that you put all of your images into an asset catalog, they provide no direct mechanism for localizing those images. Not to worry, however, as there is a simple trick that makes it easy to do.

Open Assets.xcassets and you will see a Spanish-localized version of the image named MeGusta. Now open Localizable.strings (English) and add the following at the end:

"imageName" = "iLikeIt";

Next, open Localizable.strings (Spanish) and add the following at the end:

"imageName" = "MeGusta";

Finally, open MainViewController.swift and replace viewDidLoad() with:

override func viewDidLoad() {
  super.viewDidLoad()
  
  imageView.image = UIImage(named: NSLocalizedString("imageName",
                                                     comment: "name of the image file"))
}

You use the key imageName to retrieve the name of the localized version of the image and load that image from the asset catalog. You also deleted setting the title of the button from MainViewController.swift because it’s now set in the storyboard localization.

Internationalizing Numbers

You have made great progress in preparing your app to run in multiple languages, but there is more to the process than just changing the words. Formatting for other common data, such as numbers and dates, varies around the world. For example, in the US you might write “1,000.00”. In Spain, you would write “1.000,00” instead.

Luckily, iOS provides various formatters such as NumberFormatter and DateFormatter to do all of this for you. Open MainViewController.swift and in likeButtonPressed() replace:

salesCountLabel.text = String.localizedStringWithFormat(formatString, period)

with:

let quantity = NumberFormatter.localizedString(from: 1000, number: .decimal)
salesCountLabel.text = String.localizedStringWithFormat(formatString, quantity, period)

This creates a localized presentation of the number 1000 and inserts it in the formatted string assigned to the label.

Open Localizable.strings (English) and change the second “1000” to %@.

"You have sold 1000 apps in %d months" = "You have sold %@ apps in %d months";

Do the same in Localizable.strings (Spanish).

"You have sold 1000 apps in %d months" = "Has vendido %@ aplicaciones en %d meses";

Make sure your scheme is still set to run with the Application Language set to Spanish, then build and run. You should see something like this:

Spanish number localization

Edit your run scheme and change the Application Language back to System Language. Build and run again; this time you should see something like:

English number localization

Note: If you live in a country where English is not the primary language, you still may not see 1,000 formatted with the comma. In this case, change the scheme’s Application Region to United States to get the results shown above.

Using various combinations of Application Language and Application Region, you can test almost all localizations you desire.

Pluralization

You may have observed that iLikeIt randomly chooses for you to take either 1, 2 or 5 months to sell 1000 apps. If not, run the app now and tap You like? several times to see this in action. You’ll notice, whether you’re running in English or Spanish, that the message is grammatically incorrect when you take only one month.

Never fear, iOS to the rescue again! iOS supports another localization file type called a .stringsdict. It works just like a .strings file except that it contains a dictionary with multiple replacements for a single key.

Choose File\New\File from the menu. In the resulting dialog, select iOS\Resource\Stringsdict File and click Next. Name the file Localizable and click Create. Open all of the disclosure triangles and you will see the following:

stringsdict format

Here’s what each section of the dictionary does:

  1. The Localized String Key is a dictionary that represents one localization and it’s the key used by NSLocalizedString(_:tableName:bundle:value:comment:). The localization lookup searches the .stringsdict first and then, if it finds nothing there, the equivalent .strings file. You may have as many of these keys as you like in your .stringsdict; iLikeIt will only need one.
  2. The Localized Format Key is the actual localization — the value returned by NSLocalizedString(_:tableName:bundle:value:comment:). It can contain variables for substitution. These variables take the form %#@variable-name@.
  3. You must include a Variable dictionary for each variable contained in the Localized Format Key. It defines the rules and substitutions for the variable.
  4. There are two rule types for a variable: Plural Rule and Size Rule. This tutorial will only cover the former.
  5. The Number Format Specifier is optional and tells iOS the data type of the value being used to make the substitution.
  6. The Variable dictionary contains one or more keys that specify the exact substitutions for the variable. For a Plural Rule, only the other key is required; the others are language-specific.
Note: For complete information on the stringsdict file format, see Appendix C of Apple’s Internationalization and Localization Guide.

Edit the dictionary to match this picture; specific changes are listed below.

English stringsdict file

Here are the specific changes you are making:

  1. Change the name of the Localized String Key to You have sold 1000 apps in %d months
  2. Change the value of the Localized Format Key to You have sold %@ apps in %#@months@
    This defines a variable months for use in the dictionary.
  3. Rename the Variable dictionary to months
  4. Set the Number Format Specifier (NSStringFormatValueTypeKey) to d
  5. Set the one key’s value to %d month
    Use this key when you want the singular form of the phrase.
  6. Set the other key’s value to %d months
    Use this key for all other cases.

You may delete the empty keys but I recommend against it since you may need them later. If the key is empty, iOS just ignores it.

Note: Xcode switches from showing the “friendly” names of some keys and values to showing their raw values when you edit the stringsdict. While it’s ugly, it’s not incorrect. Just ignore it.

You’ve now completed your base Localizable.stringsdict and are ready to add the Spanish version. In the File inspector, click Localize….

As it did with Localizable.strings, Xcode will ask you to confirm the file’s language. The default will be English since that’s your development language. Click Localize.

The File inspector will update to show the available and selected languages. Click the checkbox next to Spanish to add a Spanish version of the file.

Click the disclosure triangle next to Localizable.stringsdict in the Project navigator to show the individual language files. Open Localizable.stringsdict (Spanish) and make the following changes:

  1. NSStringLocalizedFormatKey: Has vendido %@ aplicaciones en %#@months@
  2. one: %d mes
  3. other: %d meses

Spanish stringsdict file

Build and run. Tap You like? until you have seen all three values and see that the grammar is now correct. And you didn’t change a bit of code! It’s worth internationalizing your app just to get plural handling for free!

English singular correct

Edit your scheme and change the Application Language to Spanish. Build and run. Tap ¿Es bueno? several times to see that the Spanish localization is working correctly.

Notice that although you have left the localizations for your sales volume in the various Localizable.strings files, those localizations are superseded by the ones in Localizable.stringsdict.

Adding Another Language

You may be wondering why there are so many options in the Values dictionary. While many languages such as English and Spanish have one form for singular and one form for plural, other languages have more complex rules for plurals, decimals, zero and so on. iOS implements all of the rules the for languages it supports. To see details on these rules, check out the CLDR Language Plural Rules specified by the Unicode organization.

One language that has more than one plural form is Polish. You’re going to add a Polish localization to iLikeIt in order to see it in action. You have already performed all of these steps in this tutorial to add your Spanish localization so this should be easy for you.

  1. Select the blue iLikeIt icon in the Project navigator to reveal the project localizations. Click the + to add Polish. Select all three files to be localized.
  2. Under Main.storyboard, open Main.strings (Polish). Change the values as follows:
    • Hello label text: Cześć
    • Sales label text: Sprzedałeś 1000 aplikacji w 20 miesięcy
    • You like button title: Lubisz to?
    • Polish storyboard strings

  3. Under Localizable.strings, open Localizable.strings (Polish). Replace the contents with:
  4. "You like?" = "Lubisz to?";
    "imageName" = "LubieTo";
    
  5. Under Localizable.stringsdict, open Localizable.stringsdict (Polish) and make the following changes:
    1. NSStringLocalizedFormatKey: Sprzedałeś %@ aplikacji w %#@months@
    2. one: %d miesiąc
    3. few: %d miesiące
    4. many: %d miesięcy
    5. other: %d miesiąca

Polish stringsdict file

And that’s all there is to it! Edit your scheme and change the Application Language to Polish. Build and run. Tap Lubisz to? several times to see the various singular and plural forms of the sales message. Notice the formatting of the number 1000 has changed as well.

Polish localization complete

Localizing Your Icon

There’s one last little bit of localization for you to do to make your app look totally professional: the name of the app as it appears under the icon on the home screen.

Using the skills you’ve already learned, add a new strings file to your project and name it InfoPlist.strings. This is another “magic” name that iOS looks for. For more information, check out the Information Property List Key Reference.

Add the following to InfoPlist.strings:

CFBundleDisplayName = "iLikeIt";

Now localize the file as English and add Spanish and Polish localizations. Change the value of the display name in InfoPlist.strings (Spanish) to MeGusta. In InfoPlist.strings (Polish), make it LubięTo.

Build and run; exit the app and check the home screen. You’ll see it’s still called iLikeIt. Unfortunately, the only way to test this localization is to change the language setting in the simulator itself.

On the simulator, open the Settings app. Navigate to General > Language & Region > iPhone Language to select a new language. Choose either Spanish or Polish and tap Done. Accept the language change and wait while the simulator reboots. Go back to the home screen and check your app now!

Spanish icon title

Helpfully, the Settings app will have English as the second choice on the screen when you’re ready to return the setting to English.

Where to Go From Here?

You can download the completed project for this tutorial here.

To learn more about internationalization, check out:

Both videos explain more about stringsdict files and the use of size rules as well as plural rules. To save you from endless and fruitless searching in the documentation, the “magic” numbers in size rules are the “M” width of a display — the number of uppercase Ms that fit on a single line.

I hope you enjoyed this tutorial. If you have any questions or comments, please join the forum discussion below!

Richard Critz

Richard is the iOS Team Lead for RayWenderlich.com.

He is on career number three as a professional photographer (after first being a software engineer doing mainframe O/S development for 20+ years and then a stint as a corporate pilot) doing contract iOS development on the side. Some would say he just can't make up his mind. Actually, he just likes diversity!

When he's not working on either of those, he's probably playing League of Legends (with or without the rest of his family).

On Twitter, while being mainly read-only, he can be found @rcritz. The rest of his professional life can be found at www.rwcfoto.com

Other Items of Interest

Save time.
Learn more with our video courses.

raywenderlich.com Weekly

Sign up to receive the latest tutorials from raywenderlich.com each week, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

PragmaConf 2016 Come check out Alt U

Our Books

Our Team

Video Team

... 27 total!

iOS Team

... 83 total!

Android Team

... 43 total!

Unity Team

... 16 total!

Articles Team

... 4 total!

Resident Authors Team

... 31 total!

Podcast Team

... 8 total!

Recruitment Team

... 8 total!

Illustration Team

... 4 total!