iOS - change language for user via buttons

前端 未结 1 1864
[愿得一人]
[愿得一人] 2020-12-20 04:40

Im making an interactive children\'s story that only uses custom graphics and narrations.

I have all graphics in both English and Spanish.

I dont have any n

相关标签:
1条回答
  • 2020-12-20 04:51

    OK, this was much harder than I had expected…


    Basically I am exchanging NSBundle's method that will be invoked by NSLocalizedString(…) by using a category on NSBundle and a technique called isa-swizzeling


    NSBundle+Language.h

    #import <Foundation/Foundation.h>
    
    @interface NSBundle (Language)
    +(void)setLanguage:(NSString*)language;
    
    @end
    

    NSBundle+Language.m

    #import "NSBundle+Language.h"
    #import <objc/runtime.h>
    
    static const char associatedLanguageBundle=0;
    
    @interface PrivateBundle : NSBundle
    @end
    
    @implementation PrivateBundle
    -(NSString*)localizedStringForKey:(NSString *)key
                                value:(NSString *)value
                                table:(NSString *)tableName
    {
        NSBundle* bundle=objc_getAssociatedObject(self, &associatedLanguageBundle);
        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],[PrivateBundle class]);
        });
    
        objc_setAssociatedObject([NSBundle mainBundle], &associatedLanguageBundle, language ?
                                 [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]] : nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    @end
    

    The AppDelegate will listen for LANGUAGE_WILL_CHANGE notifications, set the language and broadcast a notification LANGUAGE_DID_CHANGE

    AppDelegate.m

    #import "AppDelegate.h"
    #import "NSBundle+Language.h"
    
    @interface AppDelegate ()
    
    @end
    
    @implementation AppDelegate
    
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(languageWillChange:) name:@"LANGUAGE_WILL_CHANGE" object:nil];
    
        NSString *targetLang = [[NSUserDefaults standardUserDefaults] objectForKey:@"selectedLanguage"];
        [NSBundle setLanguage:targetLang?:@"en"];
        return YES;
    }
    
    -(void)languageWillChange:(NSNotification *) noti
    {
        NSString *targetLang = [noti object];
        [[NSUserDefaults standardUserDefaults] setObject:targetLang forKey:@"selectedLanguage"];
    
        [NSBundle setLanguage:targetLang];
    
        [[NSNotificationCenter defaultCenter] postNotificationName:@"LANGUAGE_DID_CHANGE" object:targetLang];
    }
    
    
    @end
    

    A BaseViewController will post LANGUAGE_WILL_CHANGE and listen for LANGUAGE_DID_CHANGE


    BaseViewController.h

    #import <UIKit/UIKit.h>
    
    @interface BaseViewController : UIViewController
    -(void) languageDidChange;
    - (IBAction)switchLanguage:(id)sender;
    @end
    

    BaseViewController.m

    #import "BaseViewController.h"
    
    @interface BaseViewController ()
    @property (weak, nonatomic) IBOutlet UIButton *englishButton;
    @property (weak, nonatomic) IBOutlet UIButton *spanishButton;
    
    @end
    
    @implementation BaseViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(languageDidChangeNotification:) name:@"LANGUAGE_DID_CHANGE" object:nil];
    }
    
    - (IBAction)switchLanguage:(id)sender {
    
        NSString *localString;
        if (self.englishButton == sender) {
            localString = @"en";
        } else if(self.spanishButton == sender){
            localString = @"es";
        }
    
        if (localString) {
            [[NSNotificationCenter defaultCenter] postNotificationName:@"LANGUAGE_WILL_CHANGE" object:localString];
        }
    }
    
    -(void)languageDidChangeNotification:(NSNotification *)notification
    {
        [self languageDidChange];
    }
    
    -(void)languageDidChange
    {
    
    }
    
    @end
    

    Now any view controller that subclasses BaseViewController can implement languageDidChange to call NSLocalizedString.


    ViewController.m

    #import "ViewController.h"
    
    @interface ViewController ()
    @property (weak, nonatomic) IBOutlet UILabel *label;
    @property (weak, nonatomic) IBOutlet UIImageView *imageView;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        [self languageDidChange];
    }
    
    
    -(void)languageDidChange
    {
        self.label.text = NSLocalizedString(@"Hello World", nil);
        self.imageView.image = [UIImage imageNamed:NSLocalizedString(@"image.png", nil)];
    }
    
    @end
    

    Ad you see, I am simply localising the image name, I added the images en_image.png and es_image.png to the Images asset bundle and map them in the localizable strings

    "image.png" = "en_image.png";
    

    and

    "image.png" = "es_image.png";
    

    Result

    enter image description here


    You'll find this example code here: https://github.com/vikingosegundo/ImmidiateLanguageChange

    0 讨论(0)
提交回复
热议问题