關於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