Localizing strings in iOS: default (fallback) language?

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

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.strings would look like this

"SOME_ACTION_BUTTON" = "Do action";

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

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


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


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

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.


@Bogus answer in Swift 4, works like a charm on 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)
}

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

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