NSLocalizedString and genstrings
There are many tutorials on the web explaining how to translate an iPhone or an iPad project. Some tutorials are written for Xcode3, some are for Xcode4. Most tutorials on the web use the macro
NSLocalizedString(<#key#>, <#comment#>). While localizing projects some challenges were encountered when using NSLocalizedString. We do the translations for German and Italian in-house, for other language the services of ICanLocalize (affiliate Link) are used. When project grow in size, during updates or in the maintenance phase NSLocalizedString has shown some limitations.
Lets start with an example to get everybody on the same page:
In your code the text of a label is set:
[self.theLabel setText:(NSLocalizedString(@"theKey", @"Hi"))];
Then genstrings is used to scan the implementations file (*.m) and to write its output to the language project folder (here: en.lproj)
genstrings -o en.lproj/ *.m
In the directory
en.lproj/ we then find a file called “Localizable.strings” with the contents similar to :
/* Hi */ "theKey" = "theKey";
/* Hi */ is taken from our source code. “Hi” should be string to display to the (english speaking) user. So we need to edit the string on the right hand side of the equals sign and make it the greeting,
= "theKey" has to become
/* Hi */ "theKey" = "Hi!";
So far so good
All this is fine if there are a few strings only or when you intend to never modify the strings. The moment you run gestrings again it will overwrite your modifications and you lose your work. If you write the output of genstrings to a different location you will have to manually merge the changes. Once your Localizable.strings file grows it becomes a nightmare trying to keep source code and Localizable.strings in sync. So we want to avoid that.
For several reasons changes to the
NSLocalizedString(<#key#>, <#comment#>) crop up. A typically workflow here is to write the program with the English language in mind, then to do the German and the Italian localization. Despite of repeated proof reading there is always a spelling error left. Other reasons for modifying the strings tends to come up during the review sessions with ICanLocalize (affiliate Link) when the translator has made suggestions that should flow back into the original text.
A big help comes from using
NSLocalizedStringWithDefaultValue(<#key#>, <#tbl#>, <#bundle#>, <#val#>, <#comment#>). This macro will let us set a default value in the LocalizedStrings file and will make the need for the initial edit of the value field to go away. Don’t be scared about all the fields, I’ll explain:
Putting all this it together it becomes:
NSLocalizedStringWithDefaultValue(@"theKey2", @"Localizable", [NSBundle mainBundle], @"Hi!", @"informal greeting"))
After running same genstrings command as used above there is now a subtle different in Localizable.strings
/* informal greeting */ "theKey2" = "Hi!";
Apart from the comment now being
/* informal greeting */ the “Hi!” is already there. There is no need to go to the Localizable.strings file, search for the correct line, modify the field form “theKey” to “Hi!”. genstrings did that for us based on the default value supplied with
Add to it, change it
Now assume you extend your program with new strings. You add the new strings into your source code and rerun genstrings. genstrings will add the new lines into the same Localizable.strings file while maintaining the existing values. Just ensure the values in the source code are what you need.
Maybe at a later stage the audience of your app has changed and you need to be more formal, i.e the “Hi!” should become “Dear Madam, Dear Sir”. The same mechanism applies: update the source code, run genstrings and the value for the greeting is changed plus the new string are inserted. There is no need to manually modify Localizable.strings. You make all modification in your source.
NSLocalizedStringWithDefaultValue became a great time saver and by using this instead of NSLocalizedString making changes to the translated strings is much easier.
Do you have hints that make localization easier? Let us know in the comments.