How to force NSLocalizedString to use a specific l

2018-12-31 01:34发布

On iPhone NSLocalizedString returns the string in the language of the iPhone. Is it possible to force NSLocalizedString to use a specific language to have the app in a different language than the device ?

25条回答
无色无味的生活
2楼-- · 2018-12-31 02:04

I had the same problem recently and I didn't want to start and patch my entire NSLocalizedString nor force the app to restart for the new language to work. I wanted everything to work as-is.

My solution was to dynamically change the main bundle's class and load the appropriate bundle there:

Header file

@interface NSBundle (Language)
+(void)setLanguage:(NSString*)language;
@end

Implementation

#import <objc/runtime.h>

static const char _bundle=0;

@interface BundleEx : NSBundle
@end

@implementation BundleEx
-(NSString*)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName
{
    NSBundle* bundle=objc_getAssociatedObject(self, &_bundle);
    return bundle ? [bundle localizedStringForKey:key value:value table:tableName] : [super localizedStringForKey:key value:value table:tableName];
}
@end

@implementation NSBundle (Language)
+(void)setLanguage:(NSString*)language
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
    {
        object_setClass([NSBundle mainBundle],[BundleEx class]);
    });
    objc_setAssociatedObject([NSBundle mainBundle], &_bundle, language ? [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]] : nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end

So basically, when your app starts and before you load your first controller, simply call:

[NSBundle setLanguage:@"en"];

When your user changes his preferred language in your setting screen, simply call it again:

[NSBundle setLanguage:@"fr"];

To reset back to system defaults, simply pass nil:

[NSBundle setLanguage:nil];

Enjoy...

For those who need a Swift version:

var bundleKey: UInt8 = 0

class AnyLanguageBundle: Bundle {

    override func localizedString(forKey key: String,
                                  value: String?,
                                  table tableName: String?) -> String {

        guard let path = objc_getAssociatedObject(self, &bundleKey) as? String,
              let bundle = Bundle(path: path) else {

            return super.localizedString(forKey: key, value: value, table: tableName)
            }

        return bundle.localizedString(forKey: key, value: value, table: tableName)
    }
}

extension Bundle {

    class func setLanguage(_ language: String) {

        defer {

            object_setClass(Bundle.main, AnyLanguageBundle.self)
        }

        objc_setAssociatedObject(Bundle.main, &bundleKey,    Bundle.main.path(forResource: language, ofType: "lproj"), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
}
查看更多
不流泪的眼
3楼-- · 2018-12-31 02:04

As said earlier, just do:

[[NSUserDefaults standardUserDefaults] setObject: [NSArray arrayWithObjects:@"el", nil] forKey:@"AppleLanguages"];

But to avoid having to restart the app, put the line in the main method of main.m, just before UIApplicationMain(...).

查看更多
心情的温度
4楼-- · 2018-12-31 02:06

Maybe you should complement with this (on .pch file after #import ):

extern NSBundle* bundle; // Declared on Language.m

#ifdef NSLocalizedString
    #undef NSLocalizedString
    // Delete this line to avoid warning
    #warning "Undefining NSLocalizedString"
#endif

#define NSLocalizedString(key, comment) \
    [bundle localizedStringForKey:(key) value:@"" table:nil]
查看更多
ら面具成の殇う
5楼-- · 2018-12-31 02:06

You can do something like this:

NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"Localizable" ofType:@"strings" inDirectory:nil forLocalization:@"es"];


NSBundle *spanishBundle = [[NSBundle alloc] initWithPath:[bundlePath stringByDeletingLastPathComponent]];

NSLocalizedStringFromTableInBundle(@"House", nil, spanishBundle, nil):
查看更多
后来的你喜欢了谁
6楼-- · 2018-12-31 02:08

As Brian Webster mentions, the language needs to be set "sometime early in your application's startup". I thought applicationDidFinishLaunching: of the AppDelegate should be a suitable place to do it, since it's where I do all other initialization.

But as William Denniss mentions, that seems to have an effect only after the app is restarted, which is kind of useless.

It seems to work fine if I put the code in the main function, though:

int main(int argc, char *argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    // Force language to Swedish.
    [[NSUserDefaults standardUserDefaults]
     setObject:[NSArray arrayWithObject:@"sv"]
     forKey:@"AppleLanguages"];

    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}

I'd appreciate any comments on this.

查看更多
初与友歌
7楼-- · 2018-12-31 02:08

In a nutshell :

Localize your application

It's the first thing you have to do is to localise your app with at least two languages (english and french in this example).

Override NSLocalizedString

In your code, instead of using NSLocalizedString(key, comment), use a macro MYLocalizedString(key, comment) defined like this : #define MYLocalizedString(key, comment) [[MYLocalizationSystem sharedInstance] localizedStringForKey:(key) value:(comment)];

This MYLocalizationSystem singleton will :

  • Set langage by setting the right localized NSBundle user asks for
  • Returns the localized NSString according to this previously set language

Set user language

When user changed application language in french, call [[MYLocalizationSystem sharedInstance] setLanguage:@"fr"];

- (void)setLanguage:(NSString *)lang
{
    NSString *path = [[NSBundle mainBundle] pathForResource:lang ofType:@"lproj"];
    if (!path)
    {
        _bundle = [NSBundle mainBundle];
        NSLog(@"Warning: No lproj for %@, system default set instead !", lang);
        return;
    }

    _bundle = [NSBundle bundleWithPath:path];
}

In this example this method set localized bundle to fr.lproj

Return localized string

Once you've set the localized bundle, you'll be able to get the right localised string from him with this method :

- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value
{
    // bundle was initialized with [NSBundle mainBundle] as default and modified in setLanguage method
    return [self.bundle localizedStringForKey:key value:value table:nil];
}

Hope this will help you.

You'll find more details in this article from NSWinery.io

查看更多
登录 后发表回答