So far, all of our example projects have assumed that our apps were destined for English speakers, but many applications can benefit from being available to non-English-speaking audiences. The App Store takes care of presenting our app to the right audience, but it’s our job as developers to configure it in such a way that the appropriate resources are displayed to users from different regions. This process is called localization.
Fortunately, iOS makes it surprisingly easy to localize resources using bundles. The NSBundle class automatically selects the appropriate asset by taking into account the user’s preferred language. For example, if you’ve provided different versions of the same image for English speakers versus Spanish speakers, the pathForResource:ofType: method discussed in the previous chapter returns different file paths depending on the user’s settings. This is one of the primary reasons you shouldn’t directly access bundle resources using hardcoded paths.
The three aspects of an app that typically need to be localized are images, audio, or videos containing a specific language, hardcoded strings, and storyboards. In this chapter, we’ll take a brief look at localizing media resources and hardcoded strings using NSBundle’s built-in internationalization capabilities. Storyboard files can be localized using the same process.
Creating the Example Application
The example for this chapter is a simple application that displays different images or strings based on the user’s preferred language. Create a new Single View Application and call it “Internationalization.” As always, Use Storyboards, and Use Automatic Reference Counting should be selected.
Enabling Localization
The first step to making an application multilingual is to add the supported languages to the project. In the project navigator, select the project icon.
Then, select the Internationalization project in the left column (not to be confused with the Internationalization target). Make sure the Info tab is selected; you should see the following window:
To add support for another language, select the plus sign under the Localizations section. You can pick any language you like, but this book will use Spanish. Selecting a language will open a dialog asking which files should be localized. Clear the selection of MainStoryboard.storyboard, but leave InfoPlist.strings selected.
It’s now possible to add a Spanish version of each resource to the application bundle.
Localizing Images
Next, we’ll look at localizing media assets. In the resource package for this book, you’ll find a file called syncfusion-icon-en.png. Add this file to the application bundle by dragging it to the Project Navigator and rename it as syncfusion-icon.png. Then, display it in the view by changing the viewDidLoad method in ViewController.m to the following:
- (void)viewDidLoad { [super viewDidLoad]; // Find the image. NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"syncfusion-icon" ofType:@"png"]; NSLog(@"%@", imagePath); // Load the image. UIImage *imageData = [[UIImage alloc] initWithContentsOfFile:imagePath]; if (imageData != nil) { // Display the image. UIImageView *imageView = [[UIImageView alloc] initWithImage:imageData]; CGRect screenBounds = [[UIScreen mainScreen] bounds]; imageView.contentMode = UIViewContentModeCenter; CGRect frame = imageView.frame; frame.size.width = screenBounds.size.width; frame.size.height = screenBounds.size.height; imageView.frame = frame; [[self view] addSubview:imageView]; } else { NSLog(@"Could not load the file"); } }
When you compile the project, you should see a small icon displayed in the middle of the screen:
You should also see the path Internationalization.app/syncfusion-icon.png in the Output Panel. Nothing new here, just an image at the top level of the application bundle, but this is going to change once we localize the image file.
To do this, select the image in the Project Navigator, open the Utilities panel, and click Make Localized under the Localization section.
The next dialogue prompts you to choose a language. Select English and click Localize.
This tells iOS that this version of syncfusion-icon.png is for English speakers. We’ll add a Spanish version in a moment, but first let’s look at what’s happening behind the scenes. To see your localizations in action, you’ll have to reset the iOS Simulator and do a clean build. To reset the simulator, navigate to iOS Simulator > Reset Content and Settings in the menu bar and select Reset in the resulting dialog.
Quit the simulator and go back to the Internationalization project in Xcode. To do a clean build, navigate to Product > Clean in the menu bar and compile the project again as you normally would. You should see a different file path in the Output Panel:
Internationalization.app/en.lproj/syncfusion-icon.png.
The new en.lproj/ subdirectory is the internal way of organizing language-specific files in iOS. All the resources localized in English will appear in this subdirectory, and all of the Spanish versions will appear in the es.lproj/ subdirectory. But again, we don’t actually have to know where the file resides; NSBundle’s pathForResource:ofType: method figures it out automatically.
So, our English version of the image is set up. Next, we need to configure the Spanish version. Select the English version of the file in the Project Navigator, and select the check box next to Spanish in the Localization section of the Utilities panel.
This copies the existing English-language version of syncfusion-icon.png into the es.lproj/ subdirectory. Back in the Project Navigator, you should be able to see this by expanding the syncfusion-icon.png file.
Of course, we need to replace the Spanish version with a completely different file. The easiest way to do this is by selecting the syncfusion-icon.png (Spanish) file and clicking the arrow icon next to the Full Path string in the Utilities panel.
This displays the contents of the es.lproj/ folder in the Finder, which gives us the opportunity to replace the file manually. Delete the existing syncfusion-icon.png file and copy the syncfusion-icon-es.png file from the resource package into es.lproj/. Make sure to rename it as syncfusion-icon.png. It’s important for localized versions of the same file to have identical file names so NSBundle can find them. After replacing the file, you should see different images when you select the two localizations in Xcode.
That should be it for localizing our image file. To test it out, you can change the device language the same way you would change it in a real device—through the Settings app. Click the device’s home button in the simulator, click and drag the screen to the right, and launch the Settings application. Under General > International > Language, you can select the device language.
Choose Español, and re-open your application. You should see the Spanish version of syncfusion-icon.png. You might need to close the simulator and compile the program again. Note that the file path output by NSLog() now reads:
Internationalization.app/es.lproj/syncfusion-icon.png.
As you can see, it’s incredibly easy to localize files using NSBundle’s built-in functionality. The idea is to use NSBundle as an abstraction between your application code and the assets that they rely on. This isolates the localization process from the development process, making it very easy to outsource translations.
Localizing video and audio files uses the exact same process just discussed. However, preparing text for an international audience requires a little bit more work.
Localizing Text
When you’re dealing with a multilingual app, hardcoded strings must be abstracted into a bundle asset so that NSBundle can load the correct language at run time. iOS uses what’s called a strings file to store translations of all the string literals in your application. After creating this strings file, you can localize it using the same method discussed in the previous section.
Let’s change our viewDidLoad method to display a button and output a message when the user taps it.
- (void)viewDidLoad { [super viewDidLoad]; UIButton *aButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [aButton setTitle:@"Say Hello" forState:UIControlStateNormal]; aButton.frame = CGRectMake(100.0, 200.0, 120.0, 40.0); [[self view] addSubview:aButton]; [aButton addTarget:self action:@selector(sayHello:) forControlEvents:UIControlEventTouchUpInside]; } - (void)sayHello:(id)sender { NSLog(@"Hello, World!"); }
These methods have two string literals that we’ll have to move into a strings file. They are @"Say Hello" and @"Hello, World!".
To create the strings file, create a new file and choose Resource > Strings File. Use Localizable.strings for the file name, which is the default string file that iOS looks for.
The content of the strings file is a simple list of key or value pairs, formatted as follows.
"Button Title" = "Say Hello"; "Greeting" = "Hello, World!";
The left side is the key that you’ll use to reference the translated string in your application code. The keys are arbitrary strings, but developers typically use either a semantic name describing how the string will be used, or the target phrase in their native language. In our strings file, we opted for the former. The values for each key follow an equal sign. Be sure to include a semicolon at the end of each line or terrible things will happen when you try to run your application.
As with media assets, you can access the contents of Localizable.strings via NSBundle. The localizedStringForKey:value:table: method returns the value of a key from a particular strings file. The value argument lets you specify a default return value if the key isn’t found, and the table argument determines which strings file to use. When you specify nil for table, the default Localizable.strings file is used.
Since accessing translated strings is such a common task, the Foundation Framework also provides a convenient NSLocalizedString() macro that you can use as a simple shortcut for localizedStringForKey:value:table:. It passes an empty string for the value argument and nil for the table argument. For most applications, NSLocalizedString() is all you really need to access localized text.
So, let’s change our button’s title configuration to use NSLocalizedString():
[aButton setTitle:NSLocalizedString(@"Button Title", nil) forState:UIControlStateNormal]; If you compile the project, the button should still read, “Say Hello”—but now it’s loaded from Localizable.strings. Let’s do the same for the sayHello method: - (void)sayHello:(id)sender { NSLog(@"%@", NSLocalizedString(@"Greeting", nil)); }
Now that our strings are dynamically loaded instead of being hardcoded, it’s trivial to localize them. We’ll use the exact same process as with images. In the Project Navigator, select the Localizable.strings file, then click Make localized in the Utilities panel. Select English in the resulting dialog box to use this version of the file for English-speaking users.
To add a Spanish version, select Localizable.strings again and select the check box next to Spanish in the Localizations section.
Just like syncfusion-icon.png, you should be able to expand the Localizable.strings file in the Project Navigator.
Finally, add some translations to the Spanish version of the file.
"Button Title" = "Dice Hola"; "Greeting" = "Hola, Mundo!";
You can test it the same way we tested images. Navigate to Reset Content and Settings in the simulator, close the simulator, and do a clean build from Xcode. After changing the language to Español, your button should read "Dice Hola" instead of "Say Hello", and clicking it should output “Hola, Mundo!”
That’s all there is to localizing strings in an iOS application. Again, having all your translated text in a single file entirely abstracted from your application code makes it easy to outsource your localization efforts. This is a very good thing, as most developers don’t fluently speak all of the languages that they would like to translate their app into.
Localizing Info.plist
There is one important detail that hasn’t been addressed yet—localizing the app name. If you take a look at the home screen in the iOS Simulator, you’ll notice that the title under your app icon hasn’t been translated to Spanish. If you’ve already gone through the trouble of localizing the string inside your app, you might as well take the time to translate a little bit of metadata too.
An app’s display name is defined in the Info.plist under the CFBundleDisplayName key. Instead of forcing you to translate values in the main Internationalization-Info.plist file, iOS gives you a dedicated string file for overwriting certain configuration options with localized values. In the Supporting Files group of the Project Navigator, open the InfoPlist.strings file. This is just like the Localizable.strings file we created in the previous section, except it should only provide values for Info.plist keys. Add the following to your InfoPlist.strings file.
"CFBundleDisplayName" = "Hola, Mundo!";
Now, if you reset the simulator and do a clean build, you should see a Spanish title under your application icon.
Summary
In this chapter, we learned how to localize media assets, text, and metadata using NSBundle. By abstracting the resources that need to be localized into isolated files and referencing them indirectly via methods like pathForResource:ofType:, it’s possible to translate your application into another language without touching a single line of application code. This is a very powerful feature of iOS, especially considering the international prevalence of iPhone and iPad devices.
The final chapter of iOS Succinctly takes a brief look at the built-in audio support for iOS applications. As we touched on in previous chapters, audio files use the same bundle structure as images and strings files. However, instead of focusing on how to access those resources, we’ll discuss the higher-level tools for controlling audio playback.
This lesson represents a chapter from iOS Succinctly, a free eBook from the team at Syncfusion.
Comments