If an application will be used in more than one part of the world, its resources should be customized, or localized, for the language, country, or cultural region. Cocoa provides a group of conventions and services, known as an internationalization architecture , that are flexible enough to enable multiple localizations of character strings, icons, user interfaces, and even context help packaged into your application. The localized resources appropriate to the user’s preferences are dynamically loaded as needed.
The aim of Cocoa’s localization architecture is to enable multilingual applications to be created without generating any new Objective-C code for each supported locale. Even if you don’t have an immediate need for multilingual support in your application, it is always a good idea to keep the localization abilities of a Cocoa application in mind, so you can enable it when the need arises. With proper design, your application’s source code won’t have to be touched if and when it does need to be localized—minimizing the risk of introducing problems by modifying the code.
In this chapter, we’ll take a look at how Cocoa’s localization system works, how it depends on the user’s language preferences, and how to structure your application’s nib files and use of strings to take advantage of it.
To see how Mac OS X supports different languages, try the following process:
Open the TextEdit application (/Applications/TextEdit), and take a look at its user interface.
Close TextEdit, and open the System Preferences application. Select the International preferences panel.
Reorder your preferred language order by dragging Français to the top of the list, as shown in Figure 14-1.
Open TextEdit again, and notice how the menu items are now in French rather than English, as shown in Figure 14-2.
Quit TextEdit (File → Quit, or
-Q), and then reset your language preferences to English.
What just happened here? By changing the System Preferences, TextEdit uses Mac OS X’s and Cocoa’s internationalization system to display the correct interface for the locale we specified. Under the hood, the system is using localized interface components stored within separate files and directories within the application’s bundle.
In Chapter 13, we took a look at how Cocoa applications are packaged into application bundles. These bundles can contain multiple sets of resources, each set contained in a directory with an .lproj extension and identified by a combination of language and locale. The ability for bundles to hold all of the localized resources for an application, combined the ability for with bundle resource look-up routines to be aware of a user’s language preferences, is what enables one version of an application to support multiple languages.
To get a feel for how localized resources are stored inside an application bundle, take a look at the contents of the Clock application.
Use the Finder to locate the Clock application (/Applications/Clock).
Control-click on the Clock application icon, and select Show Package Contents from the pop-up menu.
Select View → List from the Finder menus, browse through the application as shown in Figure 14-3, and notice the files shown.
As you can see, the same files, Clock2.niband InfoPlist.strings, exist in various subfolders of the Resources folder. These subfolders, which have a language name or country code and a .lproj extension, contain the language-support files for the project.
Mac OS X defines localizations using three different conventions. Each convention allows a different degree of specificity.
Languages supported by Mac OS X include English, French, German, Japanese, Chinese, Spanish, Italian, Swedish, and Portuguese.
The language abbreviations, some of which are shown in Table 14-1, conform to the ISO 639 specification.
Language |
Country code or abbreviation |
Chinese |
zh |
Danish |
da |
Dutch |
nl |
English |
en |
French |
Fr |
Korean |
Ko |
Polish |
Pl |
You can easily find language abbreviations on the Web by searching for “iso 639”. One resource is OASIS-OPEN (http://www.oasis-open.org/cover/iso639a.html).
The locale abbreviations consist of a language abbreviation (see Table 14-1), followed by an underscore and a two letter code. These codes, some of which are shown in Table 14-2, conform to the ISO 3166 specification that can identify a regional variant of a language.
Region |
Locale abbreviation |
British English |
en_UK |
American English |
en_US |
Canadian French |
fr_CA |
Tawainese Chinese |
zh_TW |
Mainland China Chinese |
zh_CN |
Again, you can easily find country abbreviates on the web by searching for “iso 3166”. OASIS-OPEN has a resource at http://www.oasis-open.org/cover/country3166.html.
A common practice of developers is to use the traditional language name for those that exist, then to use the language abbreviation, and then—only when necessary—the regional variant abbreviation.
Cocoa’s various resource-handling methods in the
NSBundle
class automatically return the filesystem
location of the resource that best matches the
user’s language and regional preferences. These
methods look for resources of the following types until a matching
resource is found:
Files that are stored at the top level of the Resources directory in the bundle
Files that are stored in a regional directory (such as en_UK.lproj) under the Resources directory, as specified by the user’s preferences
Files that are stored in a regional directory (such as English.lproj, da.lproj, or French.lproj), as specified by the user’s preferences
Files that are in a regional directory (such as en_UK.lproj), as specified by the region in which the application was developed
Files that are in a language directory (such as English.lproj, da.lproj, or French.lproj), as specified by the region in which the application was developed
Note that global resources take precedence over localized resources. This allows a quick return of resources that never change between locales, without going through the rest of the search process. You shouldn’t have a global version of a resource if you have localized versions, as the localized versions will always be masked by a global version.
Nib files are typically localized all at once. A person performing localization takes a nib file, translates all the user-visible strings, and makes any adjustments that are necessary, such as resizing controls so that the translated strings appear correctly.
To illustrate how to localize nib files, we will create an application interface that isn’t hooked up to anything, but which contains various controls that we can localize. The following steps will guide you:
Create a new Cocoa Application project in Project Builder (File → New Project → Application → Cocoa Application) named “Localization”, and save it in your ~/LearningCocoa folder.
Open the MainMenu.nib file in Interface Builder by double-clicking on its icon in the Resources folder of the Groups & Files pane in Project Builder.
Using the various controls available on Interface Builder’s palettes, lay out an interface that looks like Figure 14-4.
Create a subclass of NSObject
. Click on the
Classes tab of the MainMenu.nib window, find
NSObject
, Control-click it, and select Subclass
NSObject from the pop-up menu; name the subclass
Controller
.
Create an outlet named statusField
on the
Controller object, using the Info window (Tools →
Show Info, or Shift-
-I).
Create an action for the Controller subclass, named
submit:
.
Create the files for the Controller subclass (Classes → Create Files for Controller).
Instantiate the Controller subclass (Classes → Instantiate Controller).
Control-drag a connection between the Controller object and the Not
Submitted text field, and connect it to the
statusField
outlet in the Info window.
Connect the Submit button to the submit:
method of
the Controller
by Control-dragging a connection
from the Submit button to the Controller object.
Save the nib file (
-S), close the MainMenu.nib window in Interface Builder, and then return to Project Builder.
In Project Builder’s Groups & Files panel, click the disclosure triangle beside the MainMenu.nib file, and select the English entry underneath it, as shown in Figure 14-5.
Bring up the Info panel (Project → Show Info, or
-I).
Select Add Localized Variant from the Localization & Platforms pop-up menu. You will be prompted for a locale. Enter French, as shown in Figure 14-6.
Close the Info window by clicking on the red close window button.
You should see two localizations of the MainMenu.nib file in Project Builder.
Double-click the French variant to open it in Interface Builder.
Modify the various strings in the interface to match Figure 14-7. To type in the ç character, type Option-e, then e again (without the Option key). Notice that you’ll have to resize the window and move things around to accommodate the longer text fields.
Save the nib file (
-S), and return to Project Builder.
Build and run the application (
-R) to verify that everything works.
Quit the application (
-Q).
Launch the System Preferences application in the Dock, and click on the International button to open its preferences panel.
Change your language preferences from English to Français by clicking on Français and dragging it to the top of the list in the Languages window.
Run the application again to see the French interface run.
Quit and return to the System Preferences; reset your preferred language to English by dragging it to the top of the list.
Not all strings used by a program are located in a nib file. Many strings, such as those that appear in dialog boxes, are usually encoded directly in source code. To localize these strings without requiring changes to the source, Cocoa provides a function to look up strings against a strings file directly from code. This function has the following signature:
NSLocalizedString(NSString *key, NSString *comment);
This function uses the
localizedStringForKey:
value
:
table
:
method of the NSBundle
class to look up the
strings out of the main application bundle. We will add a couple of
.strings files (and a bit of code that uses
them) in our project to illustrate how this works.
Add a new file, named Localizable.strings, to the project in Project Builder (File → New File → Empty File). Save it to the ~/LearningCocoa/Localization/English.lproj folder. Make sure that it is in the Resources group, as shown in Figure 14-8.
Edit the new file to have the following text:
"Not Submitted" = "Not Submitted"; "Accepted" = "Accepted";
Bring up the Info panel (Project → Show Info, or
-I).
Select Add Localized Variant from the Localization & Platforms pop-up menu. You will be prompted for a localization. Enter French, and then close the Info window.
Click on the disclosure triangle next to the Localizable.strings file. Beneath that, you will see two files: English, which we created in steps 1 and 2; and French, which we created in the previous step.
Edit the French variant of the file to match the following text:
"Not Submitted" = "Non soumis"; "Accepted" = "Admis";
Edit the submit:
method of the
Controller.m file.
- (IBAction)submit:(id)sender
{
[statusField setStringValue:NSLocalizedString(@"Accepted", nil)];
}
This code sets the value of our status field to the localized form of the Accepted string.
Save the project (File → Save, or
-S).
Build and run (
-R) the application. Test out the Submit button, and make sure that the status field changes.
Quit the application, and set Français as your preferred language using System Preferences.
Run the application again to see the French interface run. Click the Submit button.
Quit and return your preferred language to English.
Localize the Currency Converter project from Chapter 5 into the language of your choice. Use the Translation channel of Sherlock 3 if you need help translating the strings.