Today we are going to take a very simple application (really just a couple of strings), and show how to translate it into German. This will cover both translating strings stored inside XIB files and translating strings accessed in code. First, let's take a look at the application in English:
The second line of text, as you might expect from reading it, is set in the XIB file that represents that view (the "SOTC_LocalizationExampleViewController.xib"). The third line of text is just a line of text set in code. The first line of text is a bit more interesting - it is also set in code, but it displays the currently active region - the "Region Format" set on the Settings->General->International page:
This settings page is also where you can set the current language of the iPhone. One thing to note - I recommend changing the region before you change the language. Changing the language will kick you back out of the settings application to the main screen as the iPhone resets to the new language setting.
Ok, to start off, we are going to localize the XIB file. Bring up the "Info" screen for the XIB file to localize (in this case "SOTC_LocalizationExampleViewController.xib"):
At the bottom left, you will see a button with the name "Make File Localizable". When you click it, a new Xcode sub-project will be created, named "English.lproj":
This project manifests itself in Xcode as a sub item of the "SOTC_LocalizationExampleViewController.xib":
Double clicking that "English" entry won't get you anything, since it is essentially the same XIB as the regular entry. The fun comes in when you start adding translations. If you go back to the XIB info window, you will see that the button "Add Localization..." is now enabled. Click it and add a localization - in my case I added German, or "de".
You might be tempted to name your localization using the full language name - don't do it! You should use the two letter language ISO codes. It is currently a known bug in Xcode that the English translation gets named "English" when you start the localization process - it really should be named "en". While the project will still work with English as "English" instead of "en", you don't want to compound the problem by misnaming the other translations.
Ok, once you add the "de" translation (or whatever language you decided to translate to), you will see that there is a new sub-entry under "SOTC_LocalizationExampleViewController.xib", named whatever you named your translation. If you open that up, you can just go ahead and change strings to your heart's content. In this case, I translated the middle string using Google Translate (so forgive to horrible German translation - I really have no idea what it actually says :P).
We don't really care about the first and third labels, since we are going to be setting them in code in a little bit - so translating them here wouldn't be useful (the values would just get overridden).
And that is it for translating a XIB file! Pretty nice, right? You get in place translation, and you don't have to worry about it at all from the original XIB.
Translating strings in code is slightly more painful, and probably more along the lines of what you have seen in localizing apps in other frameworks. First, let's take a look at the code behind the app above:
//
// SOTC_LocalizationExampleViewController.h
// SOTC-LocalizationExample
#import
@interface SOTC_LocalizationExampleViewController : UIViewController {
IBOutlet UILabel *localeNameLabel;
IBOutlet UILabel *labelToSetInCode;
}
@end
// SOTC_LocalizationExampleViewController.h
// SOTC-LocalizationExample
#import
@interface SOTC_LocalizationExampleViewController : UIViewController {
IBOutlet UILabel *localeNameLabel;
IBOutlet UILabel *labelToSetInCode;
}
@end
A very simple header file, with just two
IBOutlets
. The first one (localeNameLabel
) is hooked to the first label where we display the locale, and the second (labelToSetInCode
) is hooked to the third label.//
// SOTC_LocalizationExampleViewController.m
// SOTC-LocalizationExample
#import "SOTC_LocalizationExampleViewController.h"
@implementation SOTC_LocalizationExampleViewController
- (void)viewDidLoad {
labelToSetInCode.text = NSLocalizedString(@"This is my default value.",
@"This is a comment about my default value string.");
NSLocale* curentLocale = [NSLocale currentLocale];
localeNameLabel.text = [NSString stringWithFormat:
NSLocalizedString(@"The current locale is: %@",
@"String used to display the current locale."),
[curentLocale displayNameForKey:NSLocaleIdentifier
value:[curentLocale localeIdentifier]]];
[super viewDidLoad];
}
@end
// SOTC_LocalizationExampleViewController.m
// SOTC-LocalizationExample
#import "SOTC_LocalizationExampleViewController.h"
@implementation SOTC_LocalizationExampleViewController
- (void)viewDidLoad {
labelToSetInCode.text = NSLocalizedString(@"This is my default value.",
@"This is a comment about my default value string.");
NSLocale* curentLocale = [NSLocale currentLocale];
localeNameLabel.text = [NSString stringWithFormat:
NSLocalizedString(@"The current locale is: %@",
@"String used to display the current locale."),
[curentLocale displayNameForKey:NSLocaleIdentifier
value:[curentLocale localeIdentifier]]];
[super viewDidLoad];
}
@end
The simpler example is setting the text of that third label (
labelToSetInCode
), so we will start with that first. Here we use a function that you have probably never seen before, NSLocalizedString. This function returns a localized version of a string based on the localized resources in the app.The first argument serves two purposes - it is both the default value and the lookup key for the localized versions of the string. The second argument doesn't serve any purpose when the app is running, but it is used as an informative comment about the string and its use for the purposes of translation.
For the other label, we need to do a bit more work to get the text. First off, we have to get the current locale, using theNSLocale class. We are able to pull the display name for the current locale out using the method
displayNameForKey:value:
. Then we piece it together with a string pulled out of NSLocalizedString
using the NSString method stringWithFormat:.Ok, now for the odd part of this whole process - we have to leave Xcode and run a command line app called
genstrings
. This app comes with Xcode, so you don't have to worry about not having it - but I still find it odd that it can't be run through Xcode itself. We have to run it against the ".m" files - in this case, all we really care about is "SOTC_LocalizationExampleViewController.m":mkuehl@Shrike SOTC-LocalizationExample $ genstrings Classes/*.m
Running this command creates a file called "Localizable.strings":
If you open it up, the contents might look quite familiar:
/* String used to display the current locale. */
"The current locale is: %@" = "The current locale is: %@";
/* This is a comment about my default value string. */
"This is my default value." = "This is my default value.";
"The current locale is: %@" = "The current locale is: %@";
/* This is a comment about my default value string. */
"This is my default value." = "This is my default value.";
The
genstrings
command essentially extracted the information for every NSLocalizedString
call. In this case, the key is equal to the value, but that is because we haven't translated anything yet.To do so, we first have to add this file to the Xcode project:
This file is created by
genstrings
in UTF-16 text encoding, so make sure that you tell Xcode that in the Add File dialog.Now that the strings file is in the project, bring up the Info window, and click "Make File Localizable".
We essentially want to do the same thing to this file as we did to the XIB file earlier. So click "Add Localization" and add "de" as we did earlier.
Now, just like with the XIB file, the "Localizable.strings" file will have a sub-tree in Xcode with "English" and "de" as the items. Now we just need to open up the "de" version and translate some strings:
/* String used to display the current locale. */
"The current locale is: %@" = "Das aktuelle Gebietsschema: %@";
/* This is a comment about my default value string. */
"This is my default value." = "Das ist mein Standard-Wert.";
"The current locale is: %@" = "Das aktuelle Gebietsschema: %@";
/* This is a comment about my default value string. */
"This is my default value." = "Das ist mein Standard-Wert.";
Now if we compile and run (and change our settings to German), we will get a German translated application!
Random thing to note - the iPhone likes to cache resources, so when you are debugging and it doesn't look like your app is using the correct resources, don't pull your hair out. All you need to do is delete the app off the iPhone, do a clean in Xcode, and then rebuild - everything should be ok and up to date again.
That covers it for a simple translation example for the iPhone. We covered localizing strings in code and in XIB files - but there are a couple advanced things we didn't cover today. For example, you can also localize other resources, such as images - but I'll leave that as an exercise to you, the reader! You can grab the a zip of the project we built today below, and feel free to leave any questions you might have in the comments.