Skip to content

Instantly share code, notes, and snippets.

@storoj
Last active August 13, 2020 14:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save storoj/362b56d0ef5867a6a8b6d6a111edf0c4 to your computer and use it in GitHub Desktop.
Save storoj/362b56d0ef5867a6a8b6d6a111edf0c4 to your computer and use it in GitHub Desktop.
Make UIKit localizable via Localizable.strings
#import <objc/runtime.h>
/**
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
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment