最近一個一直在迭代的老項目收到一份新的開發需求,項目需要做國際化適配,簡體中文+英文。由於項目中采用了storyboard和純代碼兩種布局方式,所以國際化也要同時實現。上網查了些資料,實現了更改系統語言后,修改app內語言的問題。具體國際化方式可以參考下文:
這篇文章講的比較詳細,很容易實現。
這個需求實現后不久,產品又給我提了一個需求,讓我要在app內實現語言切換。還好之前的國際化也做了些准備,不慌不慌。
接下來就是方案的選定,通過廣泛查閱資料,得出兩個備選方案:
方案一:在原國際化版本的基礎上做修改,在info.plist文件中新增key="appLanguage"的鍵值對,保存用戶設定的語言類別。通過切換語言類別來改變語言。(例子:微信)
優點:之前有國際化操作的基礎,執行起來並不復雜。
缺點:切換完語言后,需要重新創建app keywindow的跟控制器,會有個跳轉的過程,用戶體驗不好。
方案二:切換語言后,發送通知,每個控制器收到通知后,更改語言。(例子:新浪微博)
優點:很自然的切換語言,選擇語言后即可切換,不需要重置根控制器,用戶體驗好。
缺點:每個控制器都得注冊接收通知,工作量太大,而且storyboard也得單獨處理。
綜合兩個方案的優缺點,我們選擇方案一。
中英切換,就是讓App根據自身設置的語言去讀取對應的國際化文件。在NSUserDefault中有一個字段:"AppleLanguages",這個字段就是負責存儲App語言的字段,默認這個字段會根據系統語言去變動,中文系統他就存儲中文,英文系統就存儲英文。
廢話少說,切換語言的過程上代:
// NTVLocalized.h #import <Foundation/Foundation.h> static NSString * const AppLanguage = @"appLanguage"; @interface NTVLocalized : NSObject + (NTVLocalized *)sharedInstance; //初始化多語言功能 - (void)initLanguage; //當前語言 - (NSString *)currentLanguage; //設置要轉換的語言 - (void)setLanguage:(NSString *)language; //設置為系統語言 - (void)systemLanguage; @end
// NTVLocalized.m #import "NTVLocalized.h" @implementation NTVLocalized + (NTVLocalized *)sharedInstance { static NTVLocalized *instance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[NTVLocalized alloc] init]; }); return instance; } - (void)initLanguage{ NSString *language=[self currentLanguage]; if (language.length>0) { NSLog(@"自設置語言:%@",language); }else{ [self systemLanguage]; } } - (NSString *)currentLanguage{ NSString *language=[[NSUserDefaults standardUserDefaults]objectForKey:AppLanguage]; return language; } - (void)setLanguage:(NSString *)language{ [[NSUserDefaults standardUserDefaults] setObject:language forKey:AppLanguage]; } - (void)systemLanguage{ NSString *languageCode = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"][0]; NSLog(@"系統語言:%@",languageCode); if([languageCode hasPrefix:@"zh-Hans"]){ languageCode = @"zh-Hans";//簡體中文 }else if([languageCode hasPrefix:@"en"]){ languageCode = @"en";//英語 } [self setLanguage:languageCode]; } @end
當語言設置完成后,需要重新設置keywindow的rootViewController才可以實現語言的切換。
然而這樣設置后,我們發現只有NSLocalizedString(key, comment)設置的語言才能正常顯示我們需要的語言,storyBoard和xib配置的頁面語言不跟着切換。
設置AppleLanguages字段的話,只會在下次啟動App才會生效,在App啟動后就已經生成了一個Bundle,里面識別好了對應着AppleLanguages的國際化文件,在App運行期間設置這個字段,是不生效的,所以我們去修改這個Bundle,寫一個NSBundle的擴展。
// 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 _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
重新寫一下設置語言的方法:
- (void)setLanguage:(NSString *)language{ [NSBundle setLanguage:language]; [[NSUserDefaults standardUserDefaults] setObject:language forKey:AppLanguage]; [[NSUserDefaults standardUserDefaults] synchronize]; }
綜上所述,只是修改appleLanguage,在不重啟應用的情況下,不能修改語言。所以我們選擇修改bundle的方法。
代碼在github上可以下載到:
https://github.com/FrankiezZZ/NTVLocalized
歡迎各位小伙伴加入iOS交流群:140147825