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

时间:2012-12-09 07:10:25

标签: ios localization uikit nsbundle nslocalizedstring

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

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

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

例如,这行代码总是返回英文字符串:

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

其中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));
}

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

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

-

修改:我已经对此进行了一些实验,如果我从子项目包中删除了en.lproj文件夹,那么它正确使用设备或模拟器的区域设置。

例如,我有:

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

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

5 个答案:

答案 0 :(得分:14)

我能够重现并解决问题,虽然解决方案确实意味着NSBundle中存在错误。

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

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

和代码:

  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));

将语言环境设置为fr_FR(即法语),该包从英语字符串表中选取字符串 - 即使字符串“找不到它”也不会出现。

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

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

看起来NSBundle仍然期望旧的Mac OS X捆绑结构而不是iOS应该使用的。因此,简单的束结构更改应该可以解决问题...

答案 1 :(得分:10)

添加David Doyle所说的内容。

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

答案 2 :(得分:8)

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

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

标题

@interface NSBundle (MyBundle)

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

@end

实施

@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

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

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

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

答案 3 :(得分:3)

不清楚您的问题,但我使用此宏来使用多个本地化的字符串文件:

#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]

或者你可以尝试

[[NSBundle mainBundle] localizedStringForKey:(key) value:[[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:@"MyTable"] table:nil]

首先检查 Localizable.strings ,如果密钥不存在,它将自动返回密钥,然后检查并使用 MyTable.strings 。当然,你最好用前缀命名你的密钥。例如"KYName" = "Name";

如果这是您想要的,请随时查看我之前询问过的THIS问题。 ;)

答案 4 :(得分:1)

我有另一个解决方案。

#define localizedString(key)    [NSLocalizedString(key, nil) isEqualToString:key] ? \
                            [[NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"en" ofType:@"lproj"]]  localizedStringForKey:key value:@"" table:nil] : \
                            NSLocalizedString(key, nil)

所以,例如,如果我们要找到一个关键的“title”=“经理”;在Localizable.strings(fr)中,如果不存在,那么键“title”的结果将与键相同。

在这种情况下,我们可以在localizable.string(en)中找到键“title”,但如果可以在(fr)中找到,我们就可以使用它。