iOS - change language for user via buttons

2019-01-27 01:42发布

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 non-custom buttons, no text fields, not a single "standard" Apple app UI element anywhere. Everything is a custom PNG file.

Im simply looking for a method to allow the user to change from English to Spanish or vice-versa.

I created a custom English and a Spanish button already, which will highlight when pressed.

The app will (from what Ive read need to be restarted) and the app will then present its Spanish pages (and the Spanish text embedded in the PNG). Spanish narration will ensue.

At any point the user can elect to hit the English language button, and return to English if so desired.

Tabtale's "Mermaid Princess" app does this extremely well, and is far more than I need, but is a good example of what Im after. You click a button, change your region, the app restarts, and all the custom text and narrations result for the region youve selected.

I only have two regions (languages) so my need is much simpler.

But how in the world to actually pull this off ?

Cheers !

1条回答
爱情/是我丢掉的垃圾
2楼-- · 2019-01-27 02:00

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

查看更多
登录 后发表回答