【问题标题】:Localizing strings in iOS: default (fallback) language?在 iOS 中本地化字符串:默认(后备)语言?
【发布时间】:2011-03-16 21:14:49
【问题描述】:

当应用不支持设备 UI 语言时,是否可以设置默认语言?

示例: 我的应用已本地化为英语和德语:

// en.lproj:
"POWER_TO_THE_PEOPLE_BTN" = "Power";
"POWER_PLUG_BTN" = "Power";

// de.lproj:
"POWER_TO_THE_PEOPLE_BTN"  = "Macht";
"POWER_PLUG_BTN" = "Spannung";

现在,如果我在 UI 语言设置为 Italian 的设备上运行应用程序,应用程序将使用密钥字符串 POWER_TO_THE_PEOPLE_BTNPOWER_PLUG_BTN

在这种情况下,必须有一种方法可以指定应用程序使用的默认(备用)语言。

从上面的例子可以看出,使用英文字符串作为key是行不通的。

我现在看到的唯一选择是使用NSLocalizedStringWithDefaultValue 而不是NSLocalizedString

【问题讨论】:

    标签: ios localization internationalization


    【解决方案1】:

    老问题,但仍然是常青树。

    这里我们使用swift 4.2 快速解决方案强制应用使用WHATEVER_THE_FALLBACK_LANGUAGE_WE_WANT_IT_TO_BE 回退。

    示例强制“en”

    extension String {
    
      var localized: String {
    
        var preferred = "-"
        if let pl = NSLocale.preferredLanguages.first, let pref = pl.split(separator: "-").first { preferred = String(pref) } //<- selected device language or "-"
    
        guard let _ = Bundle.main.path(forResource: preferred, ofType: "lproj") else {
            //PREFERRED ISN'T LISTED. FALLING BACK TO EN
            guard let en_path = Bundle.main.path(forResource: "en", ofType: "lproj"), let languageBundle = Bundle(path: en_path) else {
                //EN ISN'T LISTED. RETURNING UNINTERNATIONALIZED STRING
                return self
            }
            //EN EXISTS
            return languageBundle.localizedString(forKey: self, value: self, table: nil)
        }
        //PREFERRED IS LISTED. STRAIGHT I18N IS OKAY
        return NSLocalizedString(self, comment: "")
      }
    
    }
    

    【讨论】:

      【解决方案2】:

      在不替换任何方法的情况下快速执行此操作的方法是“覆盖” NSLocalizedString 定义并使用 Apple uses 对此定义的方法来替换它并在“覆盖”方法中添加额外的回退逻辑。

      #undef NSLocalizedString
      #define NSLocalizedString(key, comment) [self localizedStringForKey:(key) replaceValue:(comment)]
      
      + (NSString *)localizedStringForKey:(NSString *)key replaceValue:(NSString *)comment {
          NSString *fallbackLanguage = @"en";
          NSString *fallbackBundlePath = [[NSBundle mainBundle] pathForResource:fallbackLanguage ofType:@"lproj"];    
          NSBundle *fallbackBundle = [NSBundle bundleWithPath:fallbackBundlePath];
          NSString *fallbackString = [fallbackBundle localizedStringForKey:key value:comment table:nil];    
          NSString *localizedString = [[NSBundle mainBundle] localizedStringForKey:key value:fallbackString table:nil];
      
          return localizedString;
      }
      

      【讨论】:

      • 感谢分享。我确实喜欢您提供的解决方案。这是使用 iOS 蹩脚的后备系统的一种聪明方法,如果找不到字符串,它会返回密钥.... :P :)
      【解决方案3】:

      @Bogus 在 Swift 4 中的回答,在 iOS 11.1 上就像一个魅力:

      public func NSLocalizedString(_ key: String, tableName: String? = nil, bundle: Bundle = Bundle.main, value: String = "", comment: String) -> String {
          let fallbackLanguage = "en"
          guard let fallbackBundlePath = Bundle.main.path(forResource: fallbackLanguage, ofType: "lproj") else { return key }
          guard let fallbackBundle = Bundle(path: fallbackBundlePath) else { return key }
          let fallbackString = fallbackBundle.localizedString(forKey: key, value: comment, table: nil)
          return Bundle.main.localizedString(forKey: key, value: fallbackString, table: nil)
      }
      

      【讨论】:

        【解决方案4】:

        基于 Bodus 解决方案(顺便说一句。)我创建了这个类别,因为您也需要“fallbackString”。所以我必须检查设备当前选择的语言,并将其与我想要支持的语言进行比较。只需导入标头即可使用苹果默认宏

        NSString *myString = NSLocalizedString(@"My Ub0rstring", nil);
        

        在 iOS 9.x 和 11.1 上运行良好。

        NSString+Helper.h

        #import <Foundation/Foundation.h>
        
        #undef NSLocalizedString
        #define NSLocalizedString(key, comment) [NSString localizedStringForKey:(key) replaceValue:(comment)]
        
        @interface NSString (Helper)
        
        + (NSString *)localizedStringForKey:(NSString *)key replaceValue:(NSString *)comment;
        
        @end
        


        NSString+Helper.m

        #import "NSString+Helper.h"
        
        @implementation NSString (Helper)
        
        + (NSString *)localizedStringForKey:(NSString *)key replaceValue:(NSString *)comment
        {
            NSString *fallbackLanguage      = @"en";
            NSString *fallbackBundlePath    = [[NSBundle mainBundle] pathForResource:fallbackLanguage ofType:@"lproj"];
            NSBundle *fallbackBundle        = [NSBundle bundleWithPath:fallbackBundlePath];
            NSString *fallbackString        = [fallbackBundle localizedStringForKey:key value:comment table:nil];
            NSString *localizedString       = [[NSBundle mainBundle] localizedStringForKey:key value:fallbackString table:nil];
        
            NSString *language              = [[NSLocale preferredLanguages] firstObject];
            NSDictionary *languageDic       = [NSLocale componentsFromLocaleIdentifier:language];
            NSString *languageCode          = [languageDic objectForKey:@"kCFLocaleLanguageCodeKey"];
        
            if ([languageCode isEqualToString:@"de"] || [languageCode isEqualToString:@"en"]) {
                return localizedString;
            }
            else {
                return fallbackString;
            }
        }
        
        @end
        

        【讨论】:

          【解决方案5】:

          感谢https://stackoverflow.com/a/25928309/3664461我的解决方案

          全球.h

          NSString * LString(NSString * translation_key);
          

          全球.m

          NSString *LString(NSString *translation_key) {
            NSString *lString = nil;
            NSString *languageCode = nil;
          
            if ([UIDevice currentDevice].systemVersion.floatValue >= 9) {
              NSString *localeIdentifier = [[NSLocale preferredLanguages] objectAtIndex:0];
              NSDictionary *localeDic = [NSLocale componentsFromLocaleIdentifier:localeIdentifier];
              languageCode = [localeDic objectForKey:@"kCFLocaleLanguageCodeKey"];
            } else {
              languageCode = [[NSLocale preferredLanguages] objectAtIndex:0];
            }
          
            NSString *path = [[NSBundle mainBundle] pathForResource:languageCode ofType:@"lproj"];
            if (path != nil) {
              lString = NSLocalizedStringFromTableInBundle(translation_key, @"Localizable",
                                                       [NSBundle bundleWithPath:path], @"");
            }
          
             path = [[NSBundle mainBundle] pathForResource:@"Base" ofType:@"lproj"];
             lString = NSLocalizedStringFromTableInBundle(translation_key, @"Localizable",
                                                       [NSBundle bundleWithPath:path], @"");
            }
            return lString;
          }
          

          用法:

          #import "Global.h"
          printf(LString(@"MyKey").UTF8String);
          

          此解决方案不考虑用户的偏好顺序。相反,如果用户的第一语言未本地化,它将始终回退到您在 Base 下的内容。 此外,如果特定键未针对当前语言进行本地化,但存在于基础本地化中,您将获得基础本地化。

          更新:

          自 iOS 9 起,地区包含在语言区域设置中。我更新了代码来处理它。

          【讨论】:

            【解决方案6】:

            也许这应该有帮助? -- iPhone: localization / internationalization default strings file

            默认情况下它应该回退到英语。我刚刚将我的手机切换到我的应用程序未本地化的语言,并且文本都是英文的,正如预期的那样。

            重要提示: 正如@hyperspasm 评论的那样:为了扩展/改写这一点,后备语言是用户最近在设备设置中选择的语言,它也在应用程序的捆绑。

            【讨论】:

            • 备用语言是应用程序支持的[NSLocale preferredLanguages] 中的第一种语言。它并不总是英语。如果您在设置中更改语言,它将移至列表顶部。
            • 为了对此进行扩展/改写,备用语言是用户最近在“设置”中选择的语言,该语言也在应用程序包中表示。
            • 我们可以将后备语言设置为英语而不管最近选择的语言吗?
            【解决方案7】:

            您需要确保 Info.plist 中 CFBundleDevelopmentRegion 的值是您想要回退到的语言区域。 (例如“en”)

            【讨论】:

            • 这是(目前)除了其他人建议的拐杖(目前可能合适)之外唯一正确的解决方案。详情请查看这篇文章:maniak-dobrii.com/understanding-ios-internationalization
            • 这似乎不再起作用,测试了仅在设置中可用的芬兰语设备。该应用程序具有芬兰语和英语翻译,en 在 Info.plist 中为键 CFBundleDevelopmentRegion 定义,但芬兰语 .string 文件中不存在的键不会回退到英语版本。
            • 不适用于 iOS 11,抱歉。我得到的是密钥而不是翻译(来自 Base)。
            【解决方案8】:

            我创建了类别NSBundle+FallbackLanguage 来支持备用语言,您可以在github folder 上查看。您只需要在实现中指定支持的语言数组。

            NSBundle+FallbackLanguage.h

            #import <Foundation/Foundation.h>
            
            #undef NSLocalizedString
            #define NSLocalizedString(key, comment) [[NSBundle mainBundle] localizedStringForKey:(key) replaceValue:(comment)]
            
            @interface NSBundle (FallbackLanguage)
            
            - (NSString *)localizedStringForKey:(NSString *)key replaceValue:(NSString *)comment;
            
            @end
            

            NSBundle+FallbackLanguage.m

            #import "NSBundle+FallbackLanguage.h"
            
            @implementation NSBundle (FallbackLanguage)
            
            - (NSString *)localizedStringForKey:(NSString *)key replaceValue:(NSString *)comment {        
                NSString *language = [[NSLocale preferredLanguages] objectAtIndex:0];
                NSString *localizedString;
            
                if ([@[@"en", @"de", @"fr"] containsObject:language]){
                    localizedString = [[NSBundle mainBundle] localizedStringForKey:key value:@"" table:nil];
                }
                else{
                    NSString *fallbackLanguage = @"en";
                    NSString *falbackBundlePath = [[NSBundle mainBundle] pathForResource:fallbackLanguage ofType:@"lproj"];
                    NSBundle *fallbackBundle = [NSBundle bundleWithPath:falbackBundlePath];
                    NSString *fallbackString = [fallbackBundle localizedStringForKey:key value:comment table:nil];
                    localizedString = fallbackString;
                }
            
                return localizedString;
            }
            
            @end
            

            【讨论】:

            • 我对此做了一个即兴演奏,所以它回退到每个字符串而不是每个语言。当然,我可能完全错过了这一点,但它似乎在实践中有效。 github.com/drosenstark/categories
            • 这没有被调用。我已经在 NSBundle 上创建了类别并传递了支持的语言数组。
            【解决方案9】:

            为了避免所有那些冗长的语法和更多为翻译者提供更具描述性的 var 名称,我派生了我自己的帮助方法 L() 用于翻译并回退到英语

            NSString * L(NSString * translation_key) {
                NSString * s = NSLocalizedString(translation_key, nil);
                if (![[[NSLocale preferredLanguages] objectAtIndex:0] isEqualToString:@"en"] && [s isEqualToString:translation_key]) {
                NSString * path = [[NSBundle mainBundle] pathForResource:@"en" ofType:@"lproj"];
                NSBundle * languageBundle = [NSBundle bundleWithPath:path];
                s = [languageBundle localizedStringForKey:translation_key value:@"" table:nil];
                }
                return s;
            }
            

            我的Localizable.strings 看起来像这样

            "SOME_ACTION_BUTTON" = "Do action";
            

            所以在我的代码中,我会使用L(@"SOME_ACTION_BUTTON") 来获取正确的字符串

            虽然有时密钥比翻译本身长HELP_BUTTON_IN_NAV_BAR = 'Help',但它节省了我很多时间向帮助我翻译的人解释它是什么

            【讨论】:

            • 我加了它,但我现在后悔了。我已将手机设置为按以下顺序选择一种语言:瑞典语、法语、英语。我没有瑞典语的本地化,所以我希望它回退到英语,但它却回到了法语。查看我的解决方案。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-11-19
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多