Chapter 13. Localization

If the application you create is useful, you will want to share it with all the people of the world. Unfortunately, we don't all speak the same language. Suppose you wish to make your RaiseMan application available to French speakers. We would say, “You are going to localize RaiseMan for French speakers.”

If you are creating an application for the world, you should plan on localizing it for at least the following languages: English, French, Spanish, German, Dutch, Italian, and Japanese. Clearly, you do not want to have to rewrite the entire app for each language. In fact, our goal is to ensure that you don't have to rewrite any Objective-C code for each language. That way all the nations of the world can use a single executable in peace and harmony.

Instead of creating multiple executables, you will localize resources and create string tables. Inside your project directory, an English.lproj directory holds all the resources for English speakers: nib files, images, and sounds. To localize the app for French speakers, you will add a French.lproj directory. The nibs, images, and sounds in this directory will be appropriate for French speakers. At runtime, the app will automatically use the version of the resource appropriate to the user's language preference.

What about the places in your application where you use the language programmatically? For example, in MyDocument.m, you have the following line of code:

int choice = NSRunAlertPanel(@"Delete",
                       @"Do you really want to delete %d records?",
                       @"Delete", @"Cancel", nil,
                       [selectedPeople count]);

That alert panel is not going to bring about world peace. For each language, you will have a table of strings. You will ask NSBundle to look up the string, and it will automatically use the version appropriate to the user's language preference (Figure 13.1).

Completed Application

Figure 13.1. Completed Application

Localizing a Nib File

In Xcode, select—but do not open—MyDocument.nib and bring up the info panel. Click the Add Localization button (Figure 13.2).

Create a French Version of MyDocument.nib

Figure 13.2. Create a French Version of MyDocument.nib

You will be prompted for a locale. Choose French.

If you look in Finder, you will see that a copy of English.lproj/ MyDocument.nib has been created in French.lproj. You will francophize this copy. In Xcode, under the Resources group, you will have two versions of MyDocument.nib: English and French, as shown in Figure 13.3. Double-click on the French version to open it in Interface Builder.

Completed Application

Figure 13.3. Completed Application

Make your window look like Figure 13.4.

Completed Interface

Figure 13.4. Completed Interface

To type in characters with accents, you will need to use the option key. For example, to type “é”, type the “e”, while holding down the option key, and then type “e” again. (In the International page of System Preferences, you can add the Keyboard Viewer to your input menu. If you are using a lot of unusual characters, the Keyboard Viewer can help you learn which key combinations create which characters.)

At this point, you have created a localized resource. Notice that if you make a lot of changes to your program, you may need to update both nib files (the French version and the English version). For this reason, it is a good idea to wait until the application is completed and tested before localizing it.

Build your app. Before running it, bring up the International page of the System Preferences application. Set Français as your preferred language. Now run your application. Note that the French version of the nib is used automatically.

String Tables

For each language, you can create several string tables. A string table is a file with the extension .strings. For example, if you had a find panel, you might create a Find.strings file for each language. This file would have the phrases used by the find panel, such as “None found.”

The string table is just a collection of key-value pairs. The key and the value are strings surrounded by quotes, and the pair is terminated with a semicolon:

"Key1" = "Value1";
"Key2" = "Value2";

To find a value for a given key, you use NSBundle:

NSBundle *main;
NSString *aString;

main = [NSBundle mainBundle];
aString = [main localizedStringForKey:@"Key1"
                                value:@"DefaultValue1"
                                table:@"Find"];

This would search for the value for “Key1” in the Find.strings file. If it is not found in the user's preferred language, the second-favorite language is searched, and so on. If the key is not found in any of the user's languages, “DefaultValue1” is returned. If you do not supply the name of the table, Localizable is used. Most simple applications just have one string table for each language: Localizable.strings.

Creating String Tables

To create a Localizable.strings file for English speakers, choose the New File… menu item in Xcode. Create an empty file, and name it Localizable.strings. Save it in the English.lproj directory (Figure 13.5).

Create an English String Table

Figure 13.5. Create an English String Table

Edit the new file to have the following text:

"Delete" = "Delete";
"SureDelete" = "Do you really want to delete %d records?";
"Cancel" = "Cancel";

Save it.

Now create a localized version of that file for French. Select the English version of the Localizable.strings file in Xcode, bring up the info panel, and create a localized variant (Figure 13.6).

Create a French String Table

Figure 13.6. Create a French String Table

Edit the file to look like this:

"Delete" = "Supprimer";
"SureDelete" =
      "Etes-vous sûr que vous voulez effacer ces %d données?";
"Cancel" = "Annuler";

(To create the “u” with the circumflex, type “i” while holding down the option key, and then type “u”. To type “é”, type “e” while holding down the option key, and then type “e” again.)

When saving a file with unusual characters, you should use the Unicode (UTF-8) file encoding. While the French Localizable.strings file is active in Xcode, choose Unicode from the File Encodings submenu in the Format menu (Figure 13.7).(If you are presented with a panel asking if you wish to convert the file to UTF-8, click the button labeled Convert.)

Change the File Encoding

Figure 13.7. Change the File Encoding

Save the file.

Using the String Table

In an app with just one string table, you would probably do this:

NSString *deleteString;
deleteString = [[NSBundle mainBundle]
                    localizedStringForKey:@"Delete"
                                    value:@"Delete?"
                                    table:nil];

Fortunately, there is a macro defined in NSBundle.h for this purpose:

#define NSLocalizedString(key, comment)
          [[NSBundle mainBundle] localizedStringForKey:(key)
                                                 value:@""
                                                 table:nil]

(Notice that the comment is completely ignored by this macro. It is, however, used by a tool called genstrings, which scans through your code for calls to the macro NSLocalizedString and creates a skeleton string table. This string table includes the comment. )

In MyDocument.m, find the place where you run the alert panel. Replace that line with this one:

int choice = NSRunAlertPanel
    (NSLocalizedString(@"Delete", @"Delete"),
    NSLocalizedString(@"SureDelete",
                     @"Do you really want to delete %d records?"),
    NSLocalizedString(@"Delete", @"Delete"),
    NSLocalizedString(@"Cancel", @"Cancel"),
    nil,
    [selectedPeople count]);

Build the app. Change your preferred language back to French in System Preferences, and run the app again. When you delete a row from the table, you should get an alert panel in French.

For the More Curious: nibtool

Clearly, as you develop and localize many applications, you will develop a set of common translations. It would be handy to have an automated way to get the translated strings into a nib file. This is one of several uses for nibtool.

The nibtool command, which is run from the terminal, can list the classes or objects in a nib file. It can also dump the localizable strings into a plist. Here is how you would dump the localizable strings from the English.lproj/MyDocument.nib file into a file named Doc.strings:

> cd RaiseMan/English.lproj
> nibtool -L MyDocument.nib > Doc.strings

The resulting Doc.strings file would look something like this:

/* NSButton (Delete Record) : <title:Delete Record> (oid:24) */
"Delete Record" = "Delete Record";

/* NSTableColumn : <title:Expected Raise> (oid:53) */
"Expected Raise" = "Expected Raise";

/* NSTableColumn : <title:Name> (oid:54) */
"Name" = "Name";

/* NSButton (New Empty) : <title:New Empty> (oid:23) */
"New Empty" = "New Empty";

/* NSWindow (Window) : <title:Window> (oid:5) */
"Window" = "Window";

To create a Spanish dictionary for this nib file, you could edit the file to look like this:

/* NSButton (Delete Record) : <title:Delete Record> (oid:24) */
"Delete Record" = "Borrar Archivo";

/* NSTableColumn : <title:Expected Raise> (oid:53) */
"Expected Raise" = "Aumento Esperado";

/* NSTableColumn : <title:Name> (oid:54) */
"Name" = "Nombre";

/* NSButton (New Empty) : <title:New Empty> (oid:23) */
"New Empty" = "Crear Nuevo Archivo";

/* NSWindow (Window) : <title:Window> (oid:5) */
"Window" = "Ventana";

To substitute the strings in a nib file with their Spanish equivalents from this dictionary, you could create a new nib file like this:

> mkdir ../Spanish.lproj
> nibtool -d Doc.strings -w ../Spanish.lproj/MyDocument.nib
MyDocument.nib

To learn more about nibtool, use Unix's man command:

> man nibtool

For the More Curious: Explicit Ordering of Tokens in Format Strings

As text is moved from language to language, besides the words changing, the order of the words changes. For example, in one language the words may be laid out like this: “Ted wants a scooter.” In another, the order might be “A scooter is what Ted wants.” Suppose you try to localize the format string to be used like this:

NSString * theFormat = NSLocalizedString(@"Wants", @"%@ wants a %@");
NSString *x = [NSString stringWithFormat:theFormat, @"Ted",
@"Scooter"];

For the first language,

"Wants" = "%@ wants a %@";

will work fine.

For the second language, you would need to explicitly indicate the index of the token you want to insert. This is done with a number and the dollar sign:

"Wants =  "A %2$@ is what %1$@ wants".
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset