iOS的多语言和Android比起来感觉上还是更方便一点,毕竟XCode的操作感更强一点,但是会辛苦PM同学啦。。
首先,多语言这个事儿是分为两种的,(1) 是App内有切换语言的入口,比如东航的app就可以在app内切换语言,不管你的系统语言是神马;(2) 就是跟着系统settings里面的language走。当然这两种实现其实是相斥的,如果App内有语言切换入口,那么就不应该随着系统语言改变而改变啦。
1. 如何跟着系统语言切换
首先我们先来看第一种,根据Settings里面的语言设置自动更改App内的语言。
- 插一句如何知道系统当前的语言:
NSArray *appLanguages = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"];
NSString *languageName = [appLanguages objectAtIndex:0];
输出:
zh-Hans-CN
主要需要更改语言的主要分三大类:
(1)系统提示、App名称等
(2)字符串
(3)xib
(4)图片、音乐、视频等
但这些能够实现随语言切换最基础的基础,是要先在Target的地方给App设置支持的语言,如下图所示:
※App名称等
App的各种信息其实都是存在InfoPlist里面滴,所以其实这些系统信息的多语言化,就是InfoPlist的多语言化。
首先先新建一个文件,类型是Strings File:
然后重命名为InfoPlist.strings,然后选中文件后,在右侧选中Localization。
然后我们再次点击这个文件,右侧的Localization就变为了下图左侧的样子,勾选一下没有选中的Chinese,我们的文件左边就会有一个小三角,展开可以看到两个文件,一个是English的,一个是Chinese的。
然后我们就可以分别编辑一下这两个文件,例如设置App名称在两种语言下不一样,就可以分别写入:
// English
CFBundleDisplayName = "Example 1";
// Simplified Chinese
CFBundleDisplayName = "样例1";
然后切换语言你就会发现,App名称会自动变化啦~~
一个小小的注意事项:
如果是debug并且和真机连着调试ing,切换语言会导致App crash,并且如果不断开调试,不断打开不断crash。。这个非常正常,用户切换语言的话,所有App都会重新启动,所以只要断掉调试,就可以正常打开了,也不会有crash,只是切换语言会重新看到splash罢了。
如果我们把上面的两个InfoPlist文件show in finder,会发现其实这两个文件在不同的文件夹下,如下图所示:
其实多语言就是把不同的语言文件分开放入不同的folder酱紫,但Xcode会帮你做,这点还是蛮方便滴。
※字符串
字符串其实和上面的App信息非常类似,毕竟其实App info也是字符串显示出来的。第一步仍旧是新建一个Strings File,然后选择右侧的Localization,并且勾选你需要的语言,最后会生成多个语言的文件,分别编辑即可。
这个文件的编辑形式就是key value键值对,左边是key,然后右边的value就是字符串的内容,每对儿最后加个分号。我们在程序中只需要传入Key,App就会自动根据当前语言文件里面的键值对找到相应的value。
self.textField.text = NSLocalizedString(@"text_content", nil);
第一个参数是key,第二个是comment就是注释,用于说明这个key的,可以传nil。
如果key写错了会怎么样呢?
我试着把key写成了text_contentt,发现界面显示的就是text_contentt,不会显示空白,会直接把key作为value。
#define NSLocalizedString(key, comment) \
[NSBundle.mainBundle localizedStringForKey:(key) value:@"" table:nil]
#define NSLocalizedStringFromTable(key, tbl, comment) \
[NSBundle.mainBundle localizedStringForKey:(key) value:@"" table:(tbl)]
#define NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) \
[bundle localizedStringForKey:(key) value:@"" table:(tbl)]
#define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment) \
[bundle localizedStringForKey:(key) value:(val) table:(tbl)]
使用NSLocalizedString(key, comment)读取字符串只能读取主bundle中Localizable.strings文件内的key value,如果你的文件不叫Localizable.strings,假设叫donepage.strings,那么你要引入的时候就应该用下面的语句:
NSLocalizedStringFromTable(@"text_content",@"donepage", nil)
如果你用了一个专门的语言包bundle,则可以使用下面的两种NSLocalizedStringFromTableInBundle & NSLocalizedStringWithDefaultValue来设置~
(P.S. 其实我感觉语言包还挺好的,方便多个app复用。)
※xib
布局的多语言会有一点不一样,如果你希望改界面的字符串有很多种办法,可以用代码NSLocalizedString来区分,还可以直接用xib做。
类似上面的操作,xib文件的右侧选则Localization,然后也会生成多个文件,如下图所示:
此时如果你打开下面的文件,会发现里面也是key value对,只是key其实是系统自动根据xib对应生成的,不可以自己修改哦,我们只需要改value就可以啦~
比较神奇的是,xib不仅支持文字的多语言,还支持不同语言使用不同的xib,你只需要按照下图的设置将string改为xib,即会生成不同的xib文件,来替代Strings File。
这个时候我们的文件就会变成下面这样啦,然后你就可以改布局了~
其实这个超方便,毕竟不同语言可能长度不一样会对布局美观产生影响,对部分语言做特殊的布局可以更好的适配。
※图片等其他资源
以前版本的Xcode对多语言图片、音频等资源的支持好像木有现在这么好,现在只要和字符串类似,同样是从右侧的Localization开启并勾选你想设置的语言,就会生成多个文件,然后你只要用show in finder然后替换你想换掉的语言的图片就可以啦,使用的时候仍旧是用[UIImage imageNamed:]就行,和正常使用没有任何区别。
其实音频之类的也可以多语言化~ 虽然我还木有尝试。。但其实原理都是一样的,同名文件放入不同语言的folder,当然这个只对本地音频有效啦,如果从网上去下载音频,这种时候就需要判断当前语言了。
2. App内设置语言切换
第一部分的系统语言切换其实非常方便,而且修改语言以后App还会自动重启。但是有些App是可以与系统语言不一致的,支持App内独立设置语言,这种时候如何实现呢?
App内切换语言的重点就是切换bundle,从不同语言的资源包拿文件,如果图片之类的需要也是酱紫做。注意哦,如果App内有设置入口,不用在target的地方增加语言以免矛盾,那个只是系统切换语言用的。
以字符串为例,先建几个bundle,例如下图:
然后写一个Manager来进行语言偏好的保存及修改:(这里用UserDefault存实际可以用MMKV哈)
// .h文件
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
static NSString * const kChineseSimple = @"zh-Hans";
static NSString * const kChineseTraditional = @"zh-Hant";
static NSString * const kEnglishUS = @"en";
static NSString * const kNotificationUserLanguageChanged = @"UserLanguageChanged";
@interface LanguageManager : NSObject
+ (instancetype)sharedInstance;
- (NSString *)getStringForKey:(NSString *)key;
- (void)setNewLanguage:(NSString *)language;
@end
NS_ASSUME_NONNULL_END
// .m文件
#import "LanguageManager.h"
static NSString * const kUserLanguage = @"UserLanguage";
@implementation LanguageManager
+ (instancetype)sharedInstance {
static LanguageManager *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[LanguageManager alloc] init];
});
return instance;
}
- (NSBundle *)bundle {
NSString * setLanguage = [[NSUserDefaults standardUserDefaults] objectForKey:kUserLanguage];
if (setLanguage == nil) {
setLanguage = kChineseSimple;
}
NSString * bundlePath = [[NSBundle mainBundle] pathForResource:setLanguage ofType:@"lproj"];
return [NSBundle bundleWithPath:bundlePath];
}
- (NSString *)getStringForKey:(NSString *)key {
NSBundle * bundle = [[LanguageManager sharedInstance] bundle];
if (bundle) {
return NSLocalizedStringFromTableInBundle(key, @"Localizable", bundle, nil);
}
return NSLocalizedString(key, nil);
}
- (void)setNewLanguage:(NSString *)language {
NSString * setLanguage = [[NSUserDefaults standardUserDefaults] objectForKey:kUserLanguage];
if ([language isEqualToString:setLanguage]) {
return;
}
else if ([language isEqualToString:kChineseSimple]) {
[[NSUserDefaults standardUserDefaults] setObject:kChineseSimple forKey:kUserLanguage];
[[NSUserDefaults standardUserDefaults] synchronize];
}
else if ([language isEqualToString:kChineseTraditional]) {
[[NSUserDefaults standardUserDefaults] setObject:kChineseTraditional forKey:kUserLanguage];
[[NSUserDefaults standardUserDefaults] synchronize];
}
else if ([language isEqualToString:kEnglishUS]) {
[[NSUserDefaults standardUserDefaults] setObject:kEnglishUS forKey:kUserLanguage];
[[NSUserDefaults standardUserDefaults] synchronize];
}
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationUserLanguageChanged object:nil];
}
@end
然后页面只要使用manager的方法来设置字符串,并且监听语言切换的notification,并进行update字符串即可。
(这里其实还可以利用runtime给对象增加自动监听notification并且update的方法,但这里就简单的把职责交给页面啦)
@implementation MainViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self updateLanguage];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateLanguage) name:kNotificationUserLanguageChanged object:nil];
}
- (void)updateLanguage {
self.textField.text = [[LanguageManager sharedInstance] getStringForKey:@"text_content"];
}
- (IBAction)chineseAction:(id)sender {
[[LanguageManager sharedInstance] setNewLanguage:kChineseSimple];
}
- (IBAction)englishAction:(id)sender {
[[LanguageManager sharedInstance] setNewLanguage:kEnglishUS];
}
实现的效果如下图:
如果你希望图片等资源文件也跟着语言切换变动,则也需要manager提供图片设置的接口,从相应的bundle里面拿资源,并且页面监听到notification以后重新设置一遍图片。
Reference:
1.https://www.jb51.net/article/96872.htm
2.https://www.jianshu.com/p/55e5732e9b5b