#import /** To localize `UIBarButtonSystemItemDone` use "Done" key in your localizable.strings: "Done" = "Localized_Done"; */ __attribute__((constructor)) static void Hack_NSBundle_localizedString () { typedef NSString *(*Func)(NSBundle *, SEL, NSString *, NSString *, NSString *); SEL sel = @selector(localizedStringForKey:value:table:); Method m = class_getInstanceMethod([NSBundle class], sel); Func imp = (Func)method_getImplementation(m); NSBundle *mainBundle = [NSBundle mainBundle]; #define DOUBLE_CALL #ifdef DOUBLE_CALL // This approach always calls the original implementation twice. // First it gets the "original" value (from a system framework) and uses it // as a default value for `localizedStringForKey` from app's bundle. // For non-app strings it always makes two calls to the original implementation // of `localizedStringForKey:value:table:`. method_setImplementation(m, imp_implementationWithBlock(^NSString *(NSBundle *bundle, NSString *key, NSString *value, NSString *table) { NSString *localized = imp(bundle, sel, key, value, table); if (bundle != mainBundle) { localized = imp(mainBundle, sel, key, localized, nil); } return localized; })); #else // This method looks into the app's strings first, and goes into a framework // only if the key was not found. // // It requires a trick with this `marker` value. // [[NSBundle mainBundle] localizedStringForKey:@"Non-existent-key" value:nil table:nil] // returns @"Non-existent-key" string but not `nil`. // // This fact makes it difficult to understand if the value for the key exists or not. method_setImplementation(m, imp_implementationWithBlock(^NSString *(NSBundle *bundle, NSString *key, NSString *value, NSString *table) { static NSString *marker = @"."; NSString *localized = imp(mainBundle, sel, key, marker, nil); if (bundle != mainBundle && [localized isEqualToString:marker]) { localized = imp(bundle, sel, key, value, nil); } return localized; })); #endif }