定場詩
鋤禾日當午,
碼農真辛苦。
對著小屏幕,
周一到周五。
額,突發奇想,想寫首詩,不要問我為什么這么有才華。
目標
這篇文章將教你怎么給你的App帶來國際化效果,也就是中文系統顯示中文、英文系統顯示英文。。。等等。
還會教你如何做出微信設置里面的切換App語言的效果。
一、國際化文件
其實國際化跟我們的NSDictionary差不多,我們要支持中文和英文,就會生成兩個國際化文件(.string為后綴名),然后在兩個國際化文件中,設置一個Key,在中文的國際化文件中這個Key對應的NSString值就是中文的,在英文的國際化文件中這個Key對應的NSString值就是英文的,這么說太不好理解了,我們先把國際化文件生成出來,然后使用一下,你就明白了。
首先新建一個項目,然后創建一個Localizable.strings文件。
這時找到這個新創建的文件,然后找到他的屬性欄,點擊他右側的,Localize...按鈕。
然后找到如下圖的地方,就可以添加國際化文件了
然后點擊步驟4的加號,添加語言文件,我們這里添加英文和簡體中文
在彈出的頁面上勾選全部,然后finish
這時我們展開Localizable.strings、Main.storyboard和LaunchScreen.storyboard,會看到這些文件。
我們這里需要著重注意的是Localizable.strings中的兩個文件,我們在代碼中使用的NSString字符串主要都是在這里面給他進行國際化的。
這里就是我們上面解釋過的一個Key對應中文、英文,在對應的語言系統下尋找對應的值。我們再試著打印一句話,這里NSString的使用就要換一種方式了。
要使用NSLocalizedString(,)這個方法,第一個參數就是我們所說的Key,第二個參數是備注,就是為了給自己一個標識,方便分辨而已。
打印出來是這樣的
二、搭建頁面
這里我們就用一個tabbarController上面帶幾個navigationController這樣的結構了。
首先寫個自己的tabbarController子類,因為我們在切換語言的時候,需要把整個App的頁面重新刷新下,又由于tabbarController是appDelegate中的window屬性的rootViewController,比如我們之前的App語言是中文的,現在切換成英文的,點擊按鈕,將window的rootViewController置空,再新聲明一個tabbarController,作為window的rootViewController就OK了。
這里我就不寫怎么寫頁面了,貼下appDelegate中代碼。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 創建主頁面
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.rootViewController = [[ZHTabBarController alloc] init];
[self.window makeKeyAndVisible];
return YES;
}
這里ZHTabBarController是繼承自UITabBarController的類,在初始化的時候就放進去三個UINavigationController。模擬器上運行結果如下(英文系統):
三、中英文切換
中英切換,就是讓App根據自身設置的語言去讀取對應的國際化文件。
在NSUserDefault中有一個字段:"AppleLanguages",這個字段就是負責存儲App語言的字段,默認這個字段會根據系統語言去變動,中文系統他就存儲中文,英文系統就存儲英文。試著打印下,結果就是英文。
在finder中查看我們的國際化文件
你會發現Localizable.strings文件在en.lproj和zh-Hanz.lproj中都存在,而"en"和"zh-Hanz"正代表英文和中文。我們試著把zh-Hanz存儲到AppleLanguages字段中。看看效果
那么我們就去寫切換語言功能吧。
在第三個頁面我們有兩個切換按鈕
- (IBAction)chineseBtnAction:(id)sender {
[self changeLanguageTo:@"zh-Hans"];
}
- (IBAction)englishBtnAction:(id)sender {
[self changeLanguageTo:@"en"];
}
- (void)changeLanguageTo:(NSString *)language {
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:language, nil] forKey:@"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];
// 我們要把系統windown的rootViewController替換掉
ZHTabBarController *tab = [[ZHTabBarController alloc] init];
[UIApplication sharedApplication].keyWindow.rootViewController = tab;
// 跳轉到設置頁
tab.selectedIndex = 2;
}
看看效果。
沒有生效,這是因為設置AppleLanguages字段的話,只會在下次啟動App才會生效,在App啟動后就已經生成了一個Bundle,里面識別好了對應著AppleLanguages的國際化文件,在App運行期間設置這個字段,是不生效的,所以我們去修改這個Bundle,寫一個NSBundle的擴展。
.h
#import <Foundation/Foundation.h>
@interface NSBundle (Language)
+ (void)setLanguage:(NSString *)language;
@end
.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)changeLanguageTo:(NSString *)language {
// 設置語言
[NSBundle setLanguage:language];
// 然后將設置好的語言存儲好,下次進來直接加載
[[NSUserDefaults standardUserDefaults] setObject:language forKey:@"myLanguage"];
[[NSUserDefaults standardUserDefaults] synchronize];
// 我們要把系統windown的rootViewController替換掉
ZHTabBarController *tab = [[ZHTabBarController alloc] init];
[UIApplication sharedApplication].keyWindow.rootViewController = tab;
// 跳轉到設置頁
tab.selectedIndex = 2;
}
appDelegate中的方法修改為:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([[NSUserDefaults standardUserDefaults] objectForKey:@"myLanguage"] && ![[[NSUserDefaults standardUserDefaults] objectForKey:@"myLanguage"] isEqualToString:@""]) {
[NSBundle setLanguage:[[NSUserDefaults standardUserDefaults] objectForKey:@"myLanguage"]];
}
// 創建主頁面
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.rootViewController = [[ZHTabBarController alloc] init];
[self.window makeKeyAndVisible];
return YES;
}
修改后的運行效果
而且再次運行還會保持上次的語言。
總結
綜合上面的實踐,我們發現修改AppleLanguages的方法僅適用于重新啟動才生效的語言切換,而修改bundle的方法,可以不用重啟應用直接生效。
覺得我講的比較亂的,可以下載demo看看源碼
https://github.com/ZhaoheMHz/ZHInternatinalDemo/tree/master