iOS App Bundle中的多个本地化.strings文件

sim*_*eon 8 localization uikit nsbundle nslocalizedstring ios

我有一个相当复杂的项目,包括几个大型的本地化子项目.

我的大多数子项目都是通过一个Localizable.strings文件进行本地化的.此文件将复制到SubProjectName.bundle目标中,该目标与SubProjectName.a主项目中的静态库一起使用.这很好用.

但是,我的一个子项目包含许多本地化的.strings文件.无论设备(或模拟器)的配置方式如何,此项目都无法读取除英语之外的任何语言的字符串.

例如,这行代码始终返回英文字符串:

[[NSBundle myResourcesBundle] localizedStringForKey:@"MY_TEST_STRING" value:@"" table:@"MyTable"]
Run Code Online (Sandbox Code Playgroud)

其中MyTable对应于本地化为多种语言的MyTable.strings文件.当我查看.app包时,所有本地化都在那里,坐在应用程序内的"MyBundle.bundle"资源中.

但是,以下代码在所有本地化中正确查找给定字符串的翻译:

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));
}
Run Code Online (Sandbox Code Playgroud)

因此,当bundle是实际MyBundle.bundle/<LanguageCode>.lproj文件夹时,字符串查找起作用.但显然这违背了iOS提供的自动查找的目的.

(注意,[NSBundle myResourcesBundle]上面只是一个静态便捷方法来获取子项目的自定义包).

-

编辑:我一直在试验这个,如果我en.lproj从子项目的包中删除文件夹,那么它正确使用设备或模拟器的区域设置.

例如,我有:

MyApp.app/
 |
  - MyResources.bundle/
      |
       - en.lproj/
      |
       - zh-Hans.lproj/
Run Code Online (Sandbox Code Playgroud)

当我将模拟器(或设备)设置为简体中文时,它会查找字符串,en.lproj即使语言环境是zh-Hans.如果我删除en.lproj文件夹并重新启动应用程序,它会正确使用zh-Hans本地化.

Dav*_*yle 14

我能够重现并解决问题,尽管解决方案确实意味着NSBundle中存在一个错误.

我使用以下捆绑结构复制它:

MyApp.app/
 |
  - MyResources.bundle/
      |
       - en.lproj/
      |
       - fr.lproj/
Run Code Online (Sandbox Code Playgroud)

和代码:

  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));
Run Code Online (Sandbox Code Playgroud)

将语言环境设置为fr_FR(即法语)时,该包从英语字符串表中选择了字符串 - 即使字符串"找不到它"也不会出现.

在不更改代码的情况下,我能够使用以下结构获取法语字符串:

MyApp.app/
 |
  - MyResources.bundle/
      |
       - Resources/
          |
           - en.lproj/
          |
           - fr.lproj/
Run Code Online (Sandbox Code Playgroud)

看起来NSBundle仍然期望旧的Mac OS X捆绑结构而不是iOS应该使用的.所以简单的束结构变化应该可以解决问题......


小智 10

添加David Doyle所说的内容.

确保在捆绑包和应用程序本身的项目的信息部分中设置可用的语言.例如,如果您在应用中支持法语和英语,请确保您的应用包和应用都具有在项目的可用本地化中定义的法语和英语语言.

  • 这应该是公认的答案.如果应用程序不支持Fr语言环境,那么也不会为自定义包加载该语言环境,即使它可能包含fr.lproj目录.如果应用程序支持Fr,则从自定义捆绑包加载Fr. 没有必要通过添加'Resources'目录来更改bundle内部结构. (2认同)

sim*_*eon 8

我现在有一个hacky解决方案,但如果有人有更好的答案(或解释为什么上述不起作用),我将不胜感激.

我扩展了我的NSBundle类别以包含首选语言资源:

@interface NSBundle (MyBundle)

+ (NSBundle*) myResourcesBundle;
+ (NSBundle*) myPreferredLanguageResourcesBundle;

@end
Run Code Online (Sandbox Code Playgroud)

履行

@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
Run Code Online (Sandbox Code Playgroud)

然后我有一个简单的宏来获取我的本地化字符串:

#define MyLocalizedDocumentation(key, comment, chapter) \
  NSLocalizedStringFromTableInBundle((key),(chapter),[NSBundle myPreferredLanguageResourcesBundle],(comment))
Run Code Online (Sandbox Code Playgroud)

此解决方案只是从中获取首选语言代码NSLocale,然后检查是否存在该语言的包.如果没有,它会回退到主资源包(也许它应该遍历NSLocale preferredLanguage索引来检查一个包是否存在?有人知道吗?)