在 iOS 中本地化字符串:默认(后备)语言?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3263859/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 17:25:36  来源:igfitidea点击:

Localizing strings in iOS: default (fallback) language?

ioslocalizationinternationalization

提问by Volker Voecking

Is there a way to set a default language to be used when the device UI language is not supported by an app?

当应用程序不支持设备 UI 语言时,有没有办法设置要使用的默认语言?

Example: My app is localized into English and German:

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

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

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

Now, if I run the app on a device with UI language set to Italianthe app will use the key strings POWER_TO_THE_PEOPLE_BTNand POWER_PLUG_BTN.

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

There must be a way to specify a default (fallback) language to be used by the application in such a case.

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

From the above example it should be clear that using the English string as a key will not work.

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

The only option I see right now is to use NSLocalizedStringWithDefaultValueinstead of NSLocalizedString.

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

采纳答案by Sea Coast of Tibet

Perhaps this should help? -- iPhone: localization / internationalization default strings file

也许这应该有帮助?-- iPhone:本地化/国际化默认字符串文件

It should fallback to English by default. I've just switched my phone to a language into which my app is not localized, and the text was all in English, as expected.

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

Important:as @hyperspasm commented : To expand on/rephrase this, the fallback language is the language which was most recently chosen by the user in the device Settings, that is also represented in the app's bundle.

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

回答by Kent Nguyen

To avoid all those lengthy syntax and more having more descriptive var name for translators, I derived my own helper method L()for translation and falling back to English

为了避免所有那些冗长的语法和更多的翻译器具有更多描述性的 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;
}

My Localizable.stringswould look like this

我的Localizable.strings看起来像这样

"SOME_ACTION_BUTTON" = "Do action";

So in my code, i would use L(@"SOME_ACTION_BUTTON")to get the correct string

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

Though sometime the key is longer than the translation itself HELP_BUTTON_IN_NAV_BAR = 'Help'but it saves me a lot of time explaining what it is to whoever is helping me doing the translation

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

回答by Pattrawoot S.

You need to make sure that the value of CFBundleDevelopmentRegion in your Info.plist is the language region that you would like to fallback to. (e.g. "en")

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

回答by Makalele

@Bogus answer in Swift 4, works like a charm on iOS 11.1:

@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)
}

回答by Bogus

A fast way to do this without replacing any methods is "overriding" the NSLocalizedString define and using the methods that Apple usesfor this define to replace it and add the additional fallback logic in the "overridden" method.

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

#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;
}

回答by Siamaster

My solution thanks to https://stackoverflow.com/a/25928309/3664461

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

Global.h

全局.h

NSString * LString(NSString * translation_key);

Global.m

全球.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;
}

Usage:

用法:

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

This solution doesn't take the users preference order into consideration. Instead, it will always fallback to what you have under Base if the users first language is not localized. Also if a specific key is not localized for the current language, but it exists in the base localication, you will get the base localization.

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

Update:

更新:

Since iOS 9, region is included in the language locales. I updated the code to handle that.

从 iOS 9 开始,区域包含在语言区域设置中。我更新了代码来处理这个问题。

回答by beryllium

I've created category NSBundle+FallbackLanguageto support fallback language, you can check it out on the github folder. You only need to specify the array of supported languages in the implementation.

我已经创建NSBundle+FallbackLanguage了支持回退语言的类别,您可以在github 文件夹中查看。您只需要在实现中指定支持的语言数组。

NSBundle+FallbackLanguage.h

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

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

回答by Rikco

Based on Bodus solution (thx btw.) I created this category because you need the "fallbackString" too. So I have to check the current selected language of the device and compare it with my languages I want to support. Just import the header and you can use apples default macro

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

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

Works fine on iOS 9.x and 11.1.

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

NSString+Helper.h

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


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

回答by Dome Santoro

Old issue, but still an evergreen.

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

Here we are with a swift 4.2quick solution to force the app on an WHATEVER_THE_FALLBACK_LANGUAGE_WE_WANT_IT_TO_BEfallback.

在这里,我们提供了一个swift 4.2强制应用程序WHATEVER_THE_FALLBACK_LANGUAGE_WE_WANT_IT_TO_BE回退的快速解决方案。

The example forces to "en"

该示例强制“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: "")
  }

}