Multiple localized .strings files in iOS App Bundle

I have a rather complicated project, consisting of several large localized subprojects.

Most of my subprojects are localized through a single Localizable.strings file. This file is copied to the SubProjectName.bundle target, which is used with the SubProjectName.a static library in the main project. It works great.

However, one of my subprojects contains many localized .strings files. This project cannot read lines in any language other than English, regardless of how the device (or simulator) is configured.

For example, this line of code always returns the English line:

 [[NSBundle myResourcesBundle] localizedStringForKey:@"MY_TEST_STRING" value:@"" table:@"MyTable"] 

Where MyTable corresponds to the MyTable.strings file localized in several languages. When I look at the .app package, all localizations are there, sitting inside the "MyBundle.bundle" resource in the application.

The following code, however, correctly finds translations for a given string in all localizations:

 for (NSString *language in [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"]) { NSBundle *bundle = [NSBundle bundleWithPath:[[NSBundle myResourcesBundle] pathForResource:language ofType:@"lproj"]]; NSLog(@"%@: %@", language, NSLocalizedStringFromTableInBundle(@"MY_TEST_STRING", @"MyTable", bundle, nil)); } 

So, when the package is the actual folder MyBundle.bundle/<LanguageCode>.lproj , string search works. But obviously, this defeats the goal of the automatic search provided by iOS.

(Note that the [NSBundle myResourcesBundle] above is just a static convenience method for retrieving my custom package for a subproject).

-

Change I have already experimented with this, and if I delete the en.lproj folder from my subproject package, it correctly uses the device or simulator locale.

For example, I have:

 MyApp.app/ | - MyResources.bundle/ | - en.lproj/ | - zh-Hans.lproj/ 

When I install the simulator (or device) on Chinese Simplified, it searches for strings in en.lproj , although the language standard is zh-Hans . If I delete the en.lproj folder and restart the application, it correctly uses zh-Hans localization.

+7
source share
5 answers

I now have a hacker solution, but it would be useful if someone had a better answer (or an explanation of why the above does not work).

I expanded my NSBundle category to include a preferred language resource:

Headline

 @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 

Then I have a simple macro to get my localized strings:

 #define MyLocalizedDocumentation(key, comment, chapter) \ NSLocalizedStringFromTableInBundle((key),(chapter),[NSBundle myPreferredLanguageResourcesBundle],(comment)) 

This solution simply obtains the preferred language code from NSLocale , and then checks to see if a package exists for that language. If not, it returns to the main resource package (perhaps it should iterate over the NSLocale privileged index indexes to check if the package exists? Does anyone know?)

+8
source

I was able to reproduce and fix the problem, although this solution implies an error in the 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)); 

If the locale is set to fr_FR (that is, in French), the collection selected a row from the English string table - even the string "cannot find it" is not displayed.

Without changing the code, I was able to get the French string using the following structure instead:

 MyApp.app/ | - MyResources.bundle/ | - Resources/ | - en.lproj/ | - fr.lproj/ 

It looks like NSBundle is still expecting the old Mac OS X package structure instead of what iOS is supposed to use. Therefore, simply changing the structure of the packages should solve the problem ...

+13
source

To add to what David Doyle said.

Make sure that you have installed the available languages ​​in the information section of your project both in the kit itself and in the application itself. For example, if you support French and English in your application, make sure that your package and your application have French and English defined in the available localizations for your projects.

+9
source

It's unclear to understand your question, but I'm using 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 will first check Localizable.strings , if the key does not exist, it will return the key itself, then check and use MyTable.strings . Of course, you'd better name your key a prefix. for example, "KYName" = "Name"; .

If this is what you want, feel free to check THIS is the question I asked before .;)

+2
source

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 example, if we are going to find the key "title" = "Manager"; in Localizable.strings (fr), and if not, then the result for the key "header" will be the same as the key.

In this case, we can find the key "header" in localizable.string (en), but if it can be found in (fr), we can just use it.

+1
source