Tuesday, October 04, 2005

Localized strings with a specified localization

I have been searching for a way to get a localized string for a specified localization for some time now. Finally I have found the solution. Well to be honest, I was given the solution by a certain Douglas Davidson on the CocoaDev mailing list. Thank you very much Douglas!

Here's the problem: Assume you have an application which has multiple localizations, but needs to access the localizations besides the preferred localization of the user at runtime. A simple example could be an App that I am currently working on, which among other things prepares exams for teachers. Those exams could be in a different language than the one the program is currently running in. I would like to use something similar to NSLocalizedString for the static text on the exam.

After finding out this wasn't provided by NSBundle in Cocoa yet, I have decided to make a category which adds support for this.
The important thing here is the macro NSLocalizedStringForLocalization. This is essentially the same as NSLocalizedString, except that you can specify the localization you want. Check out the code:
NSBundle_Extensions.h
NSBundle_Extensions.m

[update] The strings dictionary is now cached
[update 2] Fixed bug explained in comments by Jonathon

4 comments:

Jonathon Mah said...

Hi Damien,

The first line should probably be:

NSString *localizedStringsPath = [self pathForResource:tableName ofType:@"strings" inDirectory:nil forLocalization:localizationDescription];

If you call this method a few times, it would be best to cache the localization dictionaries instead of recreating them each time too.

Damien said...

Thanks, that's absolutely right. I also thought about caching the dictionary, but how would you do that in a category? Would you work with a static variable then?

Jonathon Mah said...

Yes, you could add a line like:

static NSMutableDictionary *QSLocalizedStringsCache = nil;

in your method declaration (in the .m file). Then in your method, first set it to a valid NSMutableDictionary if it is nil (so this will be executed only once). Then the way I'd do it is have the keys be NSBundles and each value a NSMutableDictionary. This second dictionary would have table names as keys and the table dictionary as its value. So then you could look it up like:

NSDictionary *cachedStringsDict [[QSLocalizedStringsCache objectForKey:self] objectForKey:tableName];

I'm not entirely sure if NSBundles would work well as keys -- that probably needs to be checked.

[Update: I just saw the way you are doing it. Two things: initialize stringsDictionary to nil when you declare it, otherwise it might be garbage. And secondly, this will only cache one strings dictionary, so if you call the same method again on another bundle or with another table name, it won't be loaded.]

Jonathon Mah said...

Oh, and sorry I took a while to reply. If you want, you can e-mail me [at] JonathonMah.com in case I mis a reply of yours.