【问题标题】:Multiple Localized .strings Files in iOS App BundleiOS App Bundle 中的多个本地化 .strings 文件
【发布时间】:2012-11-26 22:50:28
【问题描述】:

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

我的大部分子项目都通过一个 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));
}

因此,当捆绑包是实际的 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 本地化。

【问题讨论】:

    标签: ios localization uikit nsbundle nslocalizedstring


    【解决方案1】:

    我能够重现并修复该问题,尽管该解决方案确实暗示 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 应该使用的结构。所以一个简单的bundle结构改变应该可以解决这个问题......

    【讨论】:

    • 哇——伟大的侦探工作!我正在尝试实施您的答案。您是如何在保持项目本地化的同时维护捆绑包中的 Resources/ 文件夹结构的?当我将 .strings 添加到 Resources/ 文件夹并将其作为文件夹引用放入 Xcode 时,我无法选择 .strings 文件进行本地化。
    • 老实说,我被骗了:)。我将包作为 .bundle 文件夹添加到项目中,它按预期工作,然后在 Finder 中重新组织了包文件夹。 Xcode 没有参与,只是添加了原始包。
    • 谢谢大卫。我会将赏金奖励给你,因为你似乎已经弄清楚它为什么会这样做,即使没有办法解决它。我会接受我的 hack-workaround 作为答案,因为这是解决问题的一种方法。
    【解决方案2】:

    补充大卫·多伊尔所说的内容。

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

    【讨论】:

    • 这应该是公认的答案。如果应用程序不支持 Fr 区域设置,则不会为自定义包加载该区域设置,即使它可能包含 fr.lproj 目录。如果应用程序支持 Fr,则从自定义包中加载 Fr。无需通过添加“资源”目录来更改捆绑包内部结构。
    • 还想补充一点,如果您的项目配置中有多种语言,但没有真正本地化文件(在所需语言附近有 0 Files Localized 消息),您将无法从您的自定义捆绑包(我自己检查过)。
    【解决方案3】:

    我现在对此有一个 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 首选语言索引以检查包是否存在?有人知道吗?)

    【讨论】:

    • 这个答案不允许部分本地化(即十个翻译的 .strings 文件)。
    • 这有帮助,但为什么不能正常工作?找到原因了吗?
    • @Anastasia 见上面 David Doyle 的回答。这似乎是 NSBundle 中的一个错误。
    • 自 2015 年起,这将不再有效。您需要将NSString *language = [NSLocale preferredLanguages][0]; 更改为:[[[NSBundle mainBundle] preferredLocalizations] firstObject];
    • @AlexZavatone 知道为什么它停止工作了吗? (它没有被列为已弃用)。 preferredLocalizations 根据区域设置按优先顺序列出包中包含 的本地化,而preferredLanguages 按优先顺序列出所有语言,无论包中包含什么。无论如何,我已经编辑了我的答案,因为即使preferredLanguages 仍然有效,preferredLocalizations 也更好用。
    【解决方案4】:

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

    #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,如果key不存在,它会自己返回key,然后检查并使用MyTable.strings。当然,您最好使用前缀命名您的密钥。例如"KYName" = "Name";.

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

    【讨论】:

    • 当我使用时出现问题: [MyCustomBundle 本地化StringForKey:(key) value:@"" table:@"MyCustomStringsFile"] — 我总是得到英文结果(如,来自 en.lproj / 文件夹)。即使存在其他语言并且设备已正确配置为该语言。
    • 不确定这是否能解决上述问题,但很高兴我找到了,因为它解决了我遇到的问题!
    • @LoriHC 很高兴听到这个消息:)
    【解决方案5】:

    我有另一个解决方案。

    #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" = "Manager";在 Localizable.strings (fr) 中,如果不存在,则键“title”的结果将与键相同。

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

    【讨论】:

      猜你喜欢
      • 2012-03-09
      • 2016-02-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-07
      • 2011-11-23
      相关资源
      最近更新 更多