简体   繁体   English

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

[英]Multiple Localized .strings Files in iOS App Bundle

I have a fairly complicated project, consisting of several large localized sub-projects. 我有一个相当复杂的项目,包括几个大型的本地化子项目。

Most of my sub-projects are localized through a single Localizable.strings file. 我的大多数子项目都是通过一个Localizable.strings文件进行本地化的 This file is copied into a SubProjectName.bundle target, which is used in conjunction with a SubProjectName.a static library in the main project. 此文件将复制到SubProjectName.bundle目标中,该目标与主项目中的SubProjectName.a静态库一起使用。 This works fine. 这很好用。

However, one of my sub-projects contains many localized .strings files. 但是,我的一个子项目包含许多本地化的.strings文件。 This project fails to read strings 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 string: 例如,这行代码始终返回英文字符串:

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

Where MyTable corresponds to a MyTable.strings file localized into several languages. 其中MyTable对应于本地化为多种语言的MyTable.strings文件。 When I peek into the .app package, all the localizations are there, sitting inside the "MyBundle.bundle" resource within the app. 当我查看.app包时,所有本地化都在那里,坐在应用程序内的“MyBundle.bundle”资源中。

The following code, however, correctly finds the 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 bundle is the actual MyBundle.bundle/<LanguageCode>.lproj folder, the string lookup works. 因此,当bundle是实际的MyBundle.bundle/<LanguageCode>.lproj文件夹时,字符串查找起作用。 But obviously this defeats the purpose of the automatic lookup provided by iOS. 但显然这违背了iOS提供的自动查找的目的。

(Note that [NSBundle myResourcesBundle] above is simply a static convenience method to fetch my custom bundle for the sub-project). (注意,上面的[NSBundle myResourcesBundle]只是一个静态便捷方法,用于获取子项目的自定义包)。

-- -

Edit : I've been experimenting with this some more, and if I delete the en.lproj folder from my sub-project's bundle, then it correctly uses the locale of the device or simulator. 编辑 :我一直在试验这个,如果我从子项目的包中删除了en.lproj文件夹,那么它正确地使用了设备或模拟器的语言环境。

For example, I have: 例如,我有:

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

When I set the simulator (or device) to Chinese Simplified it looks up strings in en.lproj even though the locale is zh-Hans . 当我将模拟器(或设备)设置为简体中文时, 它会在en.lproj查找字符串,即使该区域设置是zh-Hans If I delete the en.lproj folder and restart the app it correctly uses the zh-Hans localization. 如果我删除en.lproj文件夹并重新启动应用程序,它会正确使用zh-Hans本地化。

I was able to reproduce and fix the issue, though the solution does imply there's a bug in NSBundle. 我能够重现并解决问题,尽管解决方案确实意味着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 (ie French) the bundle picked the string from the English strings table - even the string "can't find it" doesn't appear. 将语言环境设置为fr_FR(即法语)时,该包从英语字符串表中选择了字符串 - 即使字符串“找不到它”也不会出现。

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. 看起来NSBundle仍然期望旧的Mac OS X捆绑结构而不是iOS应该使用的。 So a simple change of bundle structure should solve the problem... 所以简单的束结构变化应该可以解决问题......

To add to what David Doyle said. 添加David Doyle所说的内容。

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

I expanded my NSBundle category to include a preferred language resource: 我扩展了我的NSBundle类别以包含首选语言资源:

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

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 . 这将首先检查Localizable.strings ,如果密钥不存在,它将返回密钥本身,然后检查并使用MyTable.strings Of course, you'd better name your key with a prefix. 当然,你最好用前缀命名你的密钥。 eg "KYName" = "Name"; 例如"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"; 所以,例如,如果我们要找到一个关键的“title”=“经理”; in Localizable.strings (fr) and if not there then the result for the key "title" will be just same as the key. 在Localizable.strings(fr)中,如果不存在,那么键“title”的结果将与键相同。

In this case we can find the key "title" in localizable.string (en) but if can find in (fr) we can just use it. 在这种情况下,我们可以在localizable.string(en)中找到关键字“title”,但如果可以在(fr)中找到,我们就可以使用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM