APP内切换语言
最近有需求需要做APP内切换语言,包括代码中设置的文本和storyboard中的文字
查了一些资料,主要参考:iOS App的国际化,以及App内的语言切换
里面包含了APP国际化的方法,这里主要记录下实现APP内切换语言的方法
在NSUserDefault中有一个字段:”AppleLanguages”,这个字段是一个数组,负责存储App支持的所有语言的字段,默认这个字段会根据系统语言去变动,中文系统就zh-Hans排在前面,英文系统就en排在前面。我们的项目包含中文和英文,试着打印下,结果如下。
AppDelegate.application(_:didFinishLaunchingWithOptions:)[96]:Optional(<__NSCFArray 0x170262dc0>(
zh-Hans-CN,
en-CN
)
)
(注意这里系统默认的带”-CN”,没有这个”-CN”也是可以的。)
所以我们的思路就是修改”AppleLanguages”字段使其根据我们的设置变化,例如设置英文:
UserDefaults.standard.set(["en"], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()
//重新加载sb,reload所有的viewcontroller
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let appDelegate = UIApplication.shared.delegate as? AppDelegate
appDelegate?.window?.rootViewController = storyboard.instantiateInitialViewController()
这样并不能立刻生效,必须在重新启动APP才能生效,因为语言文件是以bundle的形式在APP启动时就加载进去了,例如如我们都会有zh-Hans.lproj 和 en.lproj这样两个文件夹
所以我们现在的思路就是在APP运行时改变加载的bundle
根据上面的参考资料,主要代码如下
NSBundle+Language.h
#import <Foundation/Foundation.h>
@interface NSBundle (Language)
+ (void)setLanguage:(NSString *)language;
@end
头文件只是声明一个setLanguage方法
主要看下实现代码
#import "NSBundle+Language.h"
#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
这里主要是定义了一个NSBundle的子类,并重写了localizedStringForKey方法
然后是setLanguage方法的实现
我们先看setLanguage方法:
最主要的是object_setClass([NSBundle mainBundle], [BundleEx class]),这行代码将[NSBundle mainBundle]的类型变成了我们定义的BundleEx类,如此在系统调用localizedStringForKey方法时就会调用我们自己实现的代码
然后用我们设置的language创建一个bundle赋给了一个动态生成的属性_bundle
下面看我们自己实现的localizedStringForKey方法: 这里我们拿到我们动态生成的属性_bundle并使用它调用localizedStringForKey的方法来拿到我们想要的文字
所以最后设置英文的代码为:
Bundle.setLanguage("en")
UserDefaults.standard.set(["en"], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()
//重新加载sb,reload所有的viewcontroller
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let appDelegate = UIApplication.shared.delegate as? AppDelegate
appDelegate?.window?.rootViewController = storyboard.instantiateInitialViewController()
注:我们在设置”AppleLanguages”时没有加”-CN”,但是重启APP后依然有效