iOS - 通过按钮更改用户语言

3

我正在制作一个互动的儿童故事,只使用自定义的图形和叙述。

我已经拥有了所有英语和西班牙语的图形。

我没有任何非自定义按钮,没有文本字段,在任何地方都没有“标准”的Apple应用程序UI元素。一切都是自定义的PNG文件。

我只是在寻找一种方法让用户从英语切换到西班牙语或反之亦然。

我已经创建了自定义的英语和西班牙语按钮,按下时会高亮显示。

应用程序将(根据我的阅读需求)重新启动,然后呈现其西班牙语页面(以及嵌入在PNG中的西班牙语文本)。接着将开始播放西班牙语旁白。

在任何时候,用户都可以选择点击英语语言按钮,如果需要的话返回到英语。

Tabtale的“美人鱼公主”应用程序做得非常好,远远超出了我的需求,但是它是我要实现的一个很好的例子。您单击一个按钮,更改您的区域,应用程序重新启动,并为您选择的区域提供所有自定义文本和旁白。

我只有两个区域(语言),因此我的需求要简单得多。

但是如何实际实现呢?

干杯!


1
如果您的项目中所有资产都是自定义PNG文件,那么您不可以在应用程序委托中设置一个属性来定义所选语言,并相应地加载/更改资产吗? - Sudeep
1个回答

4

好的,这个任务比我想象的要难得多...


基本上,我正在使用NSBundle上的类别和一种名为 isa-swizzeling 的技术来交换即将被NSLocalizedString(…)调用的方法。


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

AppDelegate会监听 LANGUAGE_WILL_CHANGE 通知,设置语言并广播一个 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

一个BaseViewController将会发布LANGUAGE_WILL_CHANGE并监听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

现在,任何继承自BaseViewController的视图控制器都可以实现languageDidChange方法来调用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

如您所见,我只是将图片名称本地化,我将图像en_image.pnges_image.png添加到图像资源包中,并在本地化字符串中映射它们。

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

并且。
"image.png" = "es_image.png";

结果

这里输入图片描述


您可以在此处找到示例代码:https://github.com/vikingosegundo/ImmidiateLanguageChange


绝对棒极了,我一百万年也想不出来!不幸的是,这对我来说太复杂了,我能付钱让你帮我实现吗? - Magna Chisel
抱歉,我现在不可用。但是我的Github上的示例项目包含您需要的所有内容。只需将其用作蓝图即可。请接受我的答案。 - vikingosegundo
在AppDelegate中执行setLanguage方法是否会设置整个设备的语言?(我的意思是所有其他应用程序也是如此吗?) - abhimuralidharan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接