关于iOS开发中的国际化(也可称为多语言)在网上的文章多如牛毛,不过总结起来就那么一回事,不是说他们写的很差我写的多好,而是说过于零散。git
如今,我将结合实际场景需求进行国际化作法详解。能够确定的是,Android的国际化作法大同小异,无非也就是各个语言版本的文件替换,咱们先来分析下真实的需求是怎么一回事。github
根据需求,我比较纠结的地方是,App的静态文本数据能够存两份在本地,也就是English一份Chinese Simplified一份,但请求的API是同时返回两份中英文数据or分中英文两个接口?若是是要一个接口同时返回了中英文两份数据,显然会加大数据包的大小,其次用户颇有可能从安装App的那天开始就再也不切换App语言,甚至平均几个星期才换一次,同时返回两份数据是否多余,可是这么作几乎能够达到“无感知”数据源切换,至关因而说,一旦用户选择好了要切换语言,“啪嗒”点了完成,立马pop掉当前页面,而后整个App的数据源中英文切换能够几乎用“瞬间完成”来形容。微信
若是是分中英文两个接口,实际上就会出现微信在进行语言切换时的loading菊花,由于要从新拉取英文版数据,不过好处是能够减小上一种作法的数据包总体大小。这两种作法我都有实践过,若是你的App是很是固定,不会频繁出现语言切换的需求,那么可使用第二种;若是App有一天以内可能会频繁切换屡次语言的状况,第一种无疑。app
通过一番探讨,虽然要供给拉美、北美和欧洲的同窗使用,可是不会出现频繁切换语言的状况,因此,最终咱们选择了第二种解决方案。先来看一张最终成果gif图,ide
从上图中能够看到其实并无对数据源进行切换,由于。。。。后台没写完?。post
不过也不影响咱们的讲解,首先,明确一个概念,咱们可以作的国际化语言支持iOS系统中自带的全部语言,只要你能在系统设置中找到的语言,就可以对你的App作对应版本的国际化适配;其次,每对App适配一种语言,就要单建立出一个语言文件(要否则会引发冲突)。OK,咱们正式进入讲解。大数据
我起名为languageTest
。ui
// AppDelegate.m #import "navOneViewController.h" #import "navTwoViewController.h" @interface AppDelegate () @property (nonatomic, strong) UITabBarController *rootTabBar; @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds]; self.rootTabBar = [[UITabBarController alloc]init]; self.rootTabBar.delegate = (id)self; self.window.rootViewController = self.rootTabBar; [[UITabBar appearance] setBarTintColor:[UIColor whiteColor]]; navOneViewController *navOneController = [navOneViewController new]; UINavigationController *nav1 = [[UINavigationController alloc] initWithRootViewController:navOneController]; nav1.title = @"首页"; navTwoViewController *navTwoController = [navTwoViewController new]; UINavigationController *nav2 = [[UINavigationController alloc] initWithRootViewController:navTwoController]; nav2.title = @"发现"; self.rootTabBar.viewControllers = @[nav1, nav2]; [self.window makeKeyAndVisible]; return YES; }
在Appdelegate
中,咱们建立一个具有基本展现功能的tabBar及挂载在其之上的VC,atom
// navOneViewController.m - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor blueColor]; UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 200, 20)]; [self.view addSubview:label]; label.font = [UIFont systemFontOfSize:25]; label.textColor = [UIColor whiteColor]; label.text = @"这是首页"; } // navTwoViewController.m - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor orangeColor]; UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 200, 20)]; [self.view addSubview:label]; label.font = [UIFont systemFontOfSize:25]; label.textColor = [UIColor whiteColor]; label.text = @"这是发现"; UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(100, 300, 100, 100)]; [self.view addSubview:button]; [button addTarget:self action:@selector(buttonClick) forControlEvents:UIControlEventTouchUpInside]; button.backgroundColor = [UIColor blueColor]; [button setTitle:@"改变语言" forState:UIControlStateNormal]; } - (void)buttonClick { }
在各自对应的VC中写下相关UI,并预留相关Button点击事件便可。spa
建立路径: file -> new -> file... -> String File,文件名严格命名为——“Localizable”,建立好该文件后,点击该文件,并打开Xcode的右边功能区(不知道应该叫啥),在Localization功能区勾选语言版本,若是此时你并未看到或者只有English可选,咱们须要到PROJECT -> info -> Localization,添加须要的语言。
添加完成后,会在以前建立的Localization.string文件下看到多出来的语言文件,我选择了English和Chinese Simplified。如今,咱们已经能够在对应生成的语言文件中进行须要多语言替换的字段编写了。
// Localizable.string/English文件 "home" = "home"; "homeString" = "I'm home"; "discover" = "discover"; "discoverString" = "I'm discover"; "change" = "change"; // Localizable.string/Chinese(Simplified)文件 "home" = "首页"; "homeString" = "我是首页"; "discover" = "发现"; "discoverString" = "我是发现"; "change" = "改变语言";
并新建一个pch文件,pch文件一样也是头文件,不过这是一个特殊头文件,是一个预编译文件,位于该文件中的全部内容,可以被其余全部源文件共享和访问,相信你也看出来了,若是在pch文件中写了大量的不是必须文件,则会延长编译期时间,咱们能够在.pch文件中放:
所以,咱们须要建立一个pch文件去存放接下来要在整个工程中都要用到的判断语言环境的中英文宏。建立一个pch文件的方式为,file -> new -> file... -> 搜“pch”关键字,建立它。
进入工程配置 -> TARGET -> Build Settings -> 搜pch关键词 -> 在“Apple LLVM 9.0 - Language”下的Prefix Header中,双击输入你的.pch文件路径,我写的是$(SRCROOT)/PrefixHeader.pch
,填写完毕,回车,会看到生成的绝对路径,肯定pch文件路径是否正确。一切都没问题后,编译经过便可。
在pch文件中,写入如下宏定义,
#define AppLanguage @"appLanguage" #define PJLocalString(key) \ [[NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%@",[[NSUserDefaults standardUserDefaults] objectForKey:@"appLanguage"]] ofType:@"lproj"]] localizedStringForKey:(key) value:@"" table:nil]
首先定义了一个AppLanguage
宏,推荐你们的命名更加多样化一些,由于OC并无namespace,若是咱们的命名过于简单,就会致使和Apple自己自定义的NSUserDefaults默认值产生冲突。
PJLocalString(key)
这个宏“定义”了一个更长的方法,咱们也都明确了一个概念,在iOS中的每一个国际化语言,就对应着一个文件,这个文件就保存在App沙盒的根目录中,咱们要作的就是在某个时机替换系统所采用的语言文件便可,而PJLocalString(key)
这个宏所作的事情,就是替换!先从NSUserDefaults中取出对应的语言key(en仍是zh-Hans),根据语言key去索引到对应的.lproj文件,最后把要替换的关键词传入,抛出找到的对应值(我以为找的这个过程用的结构应该不是hashmap,真的很快。?)
多语言文件有了,宏也有了,那怎么用呢?举个例子!
- (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor blueColor]; UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 200, 20)]; [self.view addSubview:label]; label.font = [UIFont systemFontOfSize:25]; label.textColor = [UIColor whiteColor]; label.text = PJLocalString(@"homeString"); }
只须要在多语言文字的地方调用PJLocalString()
宏,传入对应key便可。可是此时运行工程,会发现啥都没了,是由于咱们并未对NSUserDefaults中作当前语言的设置,这就致使了取出的值为nil。因此,还须要在AppDelegate
文件中设置初始语言,
if(![[NSUserDefaults standardUserDefaults] objectForKey:AppLanguage]){ [[NSUserDefaults standardUserDefaults] setObject:@"zh-Hans" forKey:AppLanguage]; [[NSUserDefaults standardUserDefaults] synchronize]; }
这样,咱们便可完成第一次进入App时初始化基础语言,若是咱们想要实时更改呢?这就须要用到了通知,使用通知机制去给监听语言设置改变的监听者进行相应的处理,
// 给Appdelegate.m更新如下方法 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { if(![[NSUserDefaults standardUserDefaults] objectForKey:AppLanguage]){ [[NSUserDefaults standardUserDefaults] setObject:@"zh-Hans" forKey:AppLanguage]; [[NSUserDefaults standardUserDefaults] synchronize]; } self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds]; self.rootTabBar = [[UITabBarController alloc]init]; self.rootTabBar.delegate = (id)self; self.window.rootViewController = self.rootTabBar; [[UITabBar appearance] setBarTintColor:[UIColor whiteColor]]; navOneViewController *navOneController = [navOneViewController new]; UINavigationController *nav1 = [[UINavigationController alloc] initWithRootViewController:navOneController]; nav1.title = PJLocalString(@"home"); navTwoViewController *navTwoController = [navTwoViewController new]; UINavigationController *nav2 = [[UINavigationController alloc] initWithRootViewController:navTwoController]; nav2.title = PJLocalString(@"discover"); self.rootTabBar.viewControllers = @[nav1, nav2]; // 新增监听方法 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeLanguage:) name:@"changeLanguage" object:nil]; [self.window makeKeyAndVisible]; return YES; } - (void)changeLanguage:(NSNotification *)notify { self.rootTabBar.viewControllers[0].tabBarItem.title = PJLocalString(@"home"); self.rootTabBar.viewControllers[1].tabBarItem.title = PJLocalString(@"discover"); } // navOneViewController.m更新如下方法 - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor blueColor]; UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 200, 20)]; [self.view addSubview:label]; label.font = [UIFont systemFontOfSize:25]; label.textColor = [UIColor whiteColor]; label.text = PJLocalString(@"homeString"); // 新增监听方法 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeLanguage:) name:@"changeLanguage" object:nil]; } - (void)changeLanguage:(NSNotification *)notify { self.label.text = PJLocalString(@"discoverString"); } // navTwoViewController.m更新如下方法 - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor orangeColor]; self.label = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 200, 20)]; [self.view addSubview:self.label]; self.label.font = [UIFont systemFontOfSize:25]; self.label.textColor = [UIColor whiteColor]; self.label.text = PJLocalString(@"discoverString"); self.button = [[UIButton alloc] initWithFrame:CGRectMake(100, 300, 100, 100)]; [self.view addSubview:self.button]; [self.button addTarget:self action:@selector(buttonClick) forControlEvents:UIControlEventTouchUpInside]; self.button.backgroundColor = [UIColor blueColor]; [self.button setTitle:PJLocalString(@"change") forState:UIControlStateNormal]; // 新增监听方法 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeLanguage:) name:@"changeLanguage" object:nil]; } - (void)changeLanguage:(NSNotification *)notify { self.label.text = PJLocalString(@"discoverString"); [self.button setTitle:PJLocalString(@"change") forState:UIControlStateNormal]; } - (void)buttonClick { [[NSUserDefaults standardUserDefaults] setObject:@"zh-Hans" forKey:AppLanguage]; [[NSUserDefaults standardUserDefaults] synchronize]; // 同步完NSUserDefault后,发送语言更改通知 [[NSNotificationCenter defaultCenter] postNotificationName:@"changeLanguage" object:nil]; }
编译运行吧,见证奇迹的时刻到了~点击“更改语言”button,怎么样,是否是瞬间全都改过了。。?
可是如今只完成了第一和第四个需求,咱们接着来完成第三个需求,“用户首次打开app时,app的语言与系统语言保持一致(系统语言为非简体中文,默认app都是英文)用户手动更改语言以后,以后都记忆用户选择的语言”。
分析一下,该需求的重点在于用户第一次打开App时总体App语言设置跟随系统语言设置,非简体中文以外的语言,都设置成英文,所以,咱们须要对AppDelegate.m
文件进行改造,
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // App第一次启动跟随系统语言设置 if(![[NSUserDefaults standardUserDefaults] boolForKey:@"firstLaunch"]){ [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"firstLaunch"]; NSArray *allLanguages = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"]; NSString *preferredLanguage = allLanguages[0]; if([preferredLanguage rangeOfString:@"zh-Hans"].location != NSNotFound) { [[NSUserDefaults standardUserDefaults] setObject:@"zh-Hans" forKey:AppLanguage]; } else { [[NSUserDefaults standardUserDefaults] setObject:@"en" forKey:AppLanguage]; } } ........
接下来完成最后一个需求,把App的名称也作国际化适配,若是你以前有过在info.plist
文件中修改过App的名字,咱们如今要作的事情一样也是更名字,并且是针对info.plist
整个文件作国际化,一样新建一个string file
文件,命名严格填写为infoPlist.strings
,而且在Xcode的右边拓展栏中选择Localizable
,点击生成English和Chinese Simplified多语言文件
// 在English中写下 CFBundleName = "your english name"; CFBundleDisplayName = "your english name"; // 在Chinese Simplified中写下 CFBundleName = "你的中文名"; CFBundleDisplayName = "你的中文名";
OK,以上就是本篇文章所要表达的全部内容,固然这些都是demo级别的code,若是此文对你有帮助,记得对其进行多多改造!
demo地址:
https://github.com/windstormeye/iOSMorePractices/tree/master/languageTest
原文连接:pjhubs.com