I have a fairly complicated project, consisting of several large localized sub-projects.
Most of my sub-projects are localized through a single Localizable.s
To add to what David Doyle said.
Make sure you set the available languages in the info section of your project in both the bundle and the application itself. So for instance, if you support french and english in your app, make sure both your bundle and your app have the French and English languages defined in the available localizations for your projects.
I now have a hacky solution to this, but would appreciate if someone has a better answer (or explanation for why the above doesn't work).
I expanded my NSBundle category to include a preferred language resource:
Header
@interface NSBundle (MyBundle)
+ (NSBundle*) myResourcesBundle;
+ (NSBundle*) myPreferredLanguageResourcesBundle;
@end
Implementation
@implementation NSBundle (MyBundle)
+ (NSBundle*) myResourcesBundle
{
static dispatch_once_t onceToken;
static NSBundle *myLibraryResourcesBundle = nil;
dispatch_once(&onceToken, ^
{
myLibraryResourcesBundle = [NSBundle bundleWithURL:[[NSBundle mainBundle] URLForResource:@"MyResources" withExtension:@"bundle"]];
});
return myLibraryResourcesBundle;
}
+ (NSBundle*) myPreferredLanguageResourcesBundle
{
static dispatch_once_t onceToken;
static NSBundle *myLanguageResourcesBundle = nil;
dispatch_once(&onceToken, ^
{
NSString *language = [[[NSBundle myResourcesBundle] preferredLocalizations] firstObject];
myLanguageResourcesBundle = [NSBundle bundleWithPath:[[NSBundle myResourcesBundle] pathForResource:language ofType:@"lproj"]];
if( myLanguageResourcesBundle == nil )
{
myLanguageResourcesBundle = [NSBundle myResourcesBundle];
}
});
return myLanguageResourcesBundle;
}
@end
I then have a simple macro for getting my localized strings:
#define MyLocalizedDocumentation(key, comment, chapter) \
NSLocalizedStringFromTableInBundle((key),(chapter),[NSBundle myPreferredLanguageResourcesBundle],(comment))
This solution simply gets the preferred language code from NSLocale
and then checks to see if a bundle exists for that language. If not, it falls back to the main resource bundle (perhaps it should iterate through the NSLocale preferredLanguage indices to check if a bundle exists? Does anyone know?)
I was able to reproduce and fix the issue, though the solution does imply there's a bug in NSBundle.
I reproduced it with the following bundle structure:
MyApp.app/
|
- MyResources.bundle/
|
- en.lproj/
|
- fr.lproj/
and code:
NSLog(@"A key: %@", NSLocalizedString(@"A key", nil));
NSBundle *bundle = [NSBundle bundleWithPath: [[NSBundle mainBundle] pathForResource: @"MyResources" ofType: @"bundle"]];
NSLog(@"Current locale: %@", [[NSLocale currentLocale] localeIdentifier]);
NSLog(@"Bundle localizations: %@", [bundle localizations]);
NSLog(@"Key from bundle: %@", [bundle localizedStringForKey: @"A key" value: @"Can't find it." table: nil]);
NSLog(@"Key using bundle macro: %@", NSLocalizedStringFromTableInBundle(@"A key",
nil,
bundle,
nil));
With the locale set to fr_FR (i.e. French) the bundle picked the string from the English strings table - even the string "can't find it" doesn't appear.
Without changing the code, I was able to get the French string using the following structure for the bundle instead:
MyApp.app/
|
- MyResources.bundle/
|
- Resources/
|
- en.lproj/
|
- fr.lproj/
It looks like NSBundle still expects the old Mac OS X bundle structure instead of what iOS is supposed to use. So a simple change of bundle structure should solve the problem...
Not clearly understand your question, but I use this macro to use multiple localized string files:
#define CustomLocalizedString(key, comment) \
[[[NSBundle mainBundle] localizedStringForKey:(key) value:nil table:nil] isEqualToString:(key)] ? \
[[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:@"MyTable"] : \
[[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil]
or you can try
[[NSBundle mainBundle] localizedStringForKey:(key) value:[[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:@"MyTable"] table:nil]
This'll check Localizable.strings first, if the key does not exist, it'll return the key itself, then check and use MyTable.strings. Of course, you'd better name your key with a prefix. e.g. "KYName" = "Name";
.
If it is what you want, feel free to check THIS question I've asked before. ;)
I have another solution.
#define localizedString(key) [NSLocalizedString(key, nil) isEqualToString:key] ? \
[[NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"en" ofType:@"lproj"]] localizedStringForKey:key value:@"" table:nil] : \
NSLocalizedString(key, nil)
so for instance if we're going to find a key "title" = "Manager"; in Localizable.strings (fr) and if not there then the result for the key "title" will be just same as the key.
In this case we can find the key "title" in localizable.string (en) but if can find in (fr) we can just use it.