在做項目中 , 如果是人羣使用不那麼侷限的app就可能涉及到國際化問題 , 國際化 大致可以分爲 代碼部分國際化 , xib 和故事板國際化 , 獲取權限提示國際化 (訪問相冊權限 , 定位權限等等..)和應用內切換語言
前言 : 創建的string文件 , 如果是英文資源的文件 ,系統會生成一個en.lproj文件存放string文件 , 簡體中文則是 zh-Hans.lproj文件
一 . 首先 , 最簡單的 , 代碼部分權限
1.項目配置 —> command + N –>Strings文件 —>命名 : Localizable.strings (必須命名爲Localizable.strings , 因爲系統會自動加載這個名字的文件 . 也可以自己命名 , 但是個人覺得完全沒必要 ,就不說了)
2.配置需要支持的語言 , 此處我僅設置了英文 , 和中文 ,如需要可以設置多國語言
2.1選中創建好的 Localizable.strings 文件 ,右側點擊此按鈕
2.2 選擇添加英文和中文 , 添加 2次
2.3 選中需要國際化的模板 , Base基礎模板可以不選
3.可以開始配置對應的文本了 , 通常情況來講我們的App都是中文版的 , 所以這對於一箇中國人來講 , 是一件比較好的事 , 中文版的我們就可以不用配置了 . 英文版的 , 在對於的strings文件中 , 添加鍵值對如下 :
//需要注意的是 , 分號必須要寫
4. 可以開始寫代碼了 , 具體使用方法如下 :
// NSLocalizedString(<#key#>, <#comment#>) 此宏爲foundation框架定義 , 直接在key中書寫中文即可 , 當手機切換成英文版本時 , 程序會自動將key對應的中文換成中文 .如果是手機設置爲中文 , 或者其他語言 , 則還是顯示中文
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(150, 300, 100, 50)];
label.textColor = [UIColor redColor];
label.text = NSLocalizedString(@"你好世界",nil);
[self.view addSubview:label];
————————————————-
二 . 訪問權限提示國際化
1. 如上述一樣 , 配置語言 . 然後新建 InfoPlist.strings文件 , 同樣選中,並勾選英文版本和中文版 —-> 在info.plist文件中添加權限字段
在中文文件中 , 添加權限提示
在英文文件中 , 添加權限提示 value值
配置info.plist文件 , 對應的提示內容 不寫 , 因爲需要根據設置語言 , 加載 string文件中對應的語言
代碼中請求用戶權限
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//判斷用戶授權狀態
ABAuthorizationStatus state = ABAddressBookGetAuthorizationStatus();
//判斷 如果授權狀態是未決定的,則請求授權
if (state == kABAuthorizationStatusNotDetermined) {
//請求
//1.獲取通訊錄對象
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
//2.請求授權 (用戶決定後就會回調block)
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
if (granted) {
NSLog(@"用戶授權成功");
}else{
NSLog(@"用戶授權失敗");
}
});
}
return YES;
}
運行設置語言 中文
運行設置語言 英文
我試過使用info.plist原生國際化 , 但是一國際化後 , info.plist的目錄結構就發生變化 , 編譯報錯
我試着把info.plist文件還原到原來的目錄下 , 編譯不報錯了 . 運行彈框獲取權限時 , 國際化並沒有成功 . 不知道是不是不支持 …
———————————————
三 . 應用內修改語言邏輯
//注意 : 通常來說 , 項目中如果使用了國際化 , 那麼項目中語言會跟隨手機系統的語言變化而變化 ,程序會自動去讀取Userdefault中appleLanguage鍵的值作爲app的語言. 而咱們要做的 , 是讓應用內的語言不受手機系統的語言影響 , 而是用戶在應用內設定的語言爲準 , 並且實現應用內隨時切換整個應用的語言 (比如微信切換語言)
整體實現邏輯 :
common單例 , MYInternationalManager.h文件
#import <Foundation/Foundation.h>
@interface MYInternationalManager : NSObject
+ (instancetype)sharedInternationalManager;
//當前加載的bundle
@property (strong, nonatomic) NSBundle *languageBunle;
@end
common單例 , MYInternationalManager.m文件
#import "MYInternationalManager.h"
static MYInternationalManager *manager = nil;
@implementation MYInternationalManager
+ (instancetype)sharedInternationalManager
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[MYInternationalManager alloc]init];
});
return manager;
}
@end
2. 設置基類 MYBaseViewController
#import <UIKit/UIKit.h>
@interface MYBaseViewController : UIViewController
- (void)UIConfig;
@end
------------------------------------------------
#import "MYBaseViewController.h"
@interface MYBaseViewController ()
@end
@implementation MYBaseViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
//註冊通知
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(UIConfig) name:@"UIConfig" object:nil];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
//發出通知
[[NSNotificationCenter defaultCenter]postNotificationName:@"UIConfig" object:nil];
}
//父類方法 (主要抽取來作爲項目中需要國際化控件內容的賦值)
- (void)UIConfig
{
}
3 . 程序入口Appdelegate 判斷本地沙盒是否有存儲的 userLanguage (app應用中使用的語言) , 如果有 , app使用存儲的語言 . 如果沒有 , app使用手機系統語言 . 代碼如下
程序入口Appdelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//假設我們設置用戶在app中設定的語言 , 在沙盒中對應key值爲@"userLanguage"
//1.程序啓動 , 獲取用戶設置的app語言
NSString *userLanguage = [[NSUserDefaults standardUserDefaults]objectForKey:@"userLanguage"];
//2.如果沒有 , 則設定手機設置語言爲app語言(系統會自動完成,讀取@"appleLanguage") ,存儲手機設置語言爲app語言
if (!userLanguage.length) {
//2.1獲取手機設置語言
NSString *phoneLanguage = [[[NSUserDefaults standardUserDefaults]objectForKey:@"AppleLanguages"]lastObject];
//2.2 存儲app語言到沙盒
[[NSUserDefaults standardUserDefaults]setObject: phoneLanguage forKey:@"userLanguage"];
[[NSUserDefaults standardUserDefaults]synchronize];
//3.有app語言情況
}else{
//3.1設置app語言
//3.11獲取app語言對應的bundle
NSString *userLanguagePath = [[NSBundle mainBundle]pathForResource:userLanguage ofType:@"lproj"];
NSBundle *userLanguageBundle = [NSBundle bundleWithPath:userLanguagePath];
//存儲當前app內語言
[MYInternationalManager sharedInternationalManager].languageBunle = userLanguageBundle;
}
//設置根控制器
self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
OneViewController *oneVc = [[OneViewController alloc]init];
MYNavigationController *nav = [[MYNavigationController alloc]initWithRootViewController:oneVc];
self.window.rootViewController = nav;
[self.window makeKeyAndVisible];
return YES;
}
4. 配置PCH文件 , 方便簡化從當前加載bundle獲取需要的語言
開始驗證
顯示一級頁面
#import "MYBaseViewController.h"
//繼承該父類
@interface OneViewController : MYBaseViewController
@end
----------------------------------------------------
#import "OneViewController.h"
#import "SecondViewController.h"
@interface OneViewController ()
@property (nonatomic, strong) UILabel *label;
@end
@implementation OneViewController
//懶加載
- (UILabel *)label
{
if (!_label) {
_label = [[UILabel alloc]initWithFrame:CGRectMake(100, 200, 100, 50)];
_label.textColor = [UIColor redColor];
_label.font = [UIFont systemFontOfSize:25];
}
return _label;
}
- (void)viewDidLoad {
[super viewDidLoad]; //調用父類註冊通知
self.view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.label];
//按鈕
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setTitle:@"點擊進入" forState:UIControlStateNormal];
[button setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
button.frame = CGRectMake(200, 400, 100, 50);
[button addTarget:self action:@selector(push) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
//按鈕一
UIButton *button1 = [UIButton buttonWithType:UIButtonTypeCustom];
[button1 setTitle:@"切換中文" forState:UIControlStateNormal];
[button1 setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
button1.frame = CGRectMake(200,100, 100, 50);
[button1 addTarget:self action:@selector(changeChinese) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button1];
//按鈕二
UIButton *button2 = [UIButton buttonWithType:UIButtonTypeCustom];
[button2 setTitle:@"切換英文" forState:UIControlStateNormal];
[button2 setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
button2.frame = CGRectMake(200,200, 100, 50);
[button2 addTarget:self action:@selector(changeEnglish) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button2];
}
//切換中文
- (void)changeChinese
{
//對當前語言進行本地存儲
[[NSUserDefaults standardUserDefaults]setObject:@"zh-Hans" forKey:@"userLanguage"];
[[NSUserDefaults standardUserDefaults]synchronize];
//改變bundle //中文語言資源文件 zh-Hans.lproj
NSString *path = [[NSBundle mainBundle]pathForResource:@"zh-Hans" ofType:@"lproj"];
NSBundle *needBundle = [NSBundle bundleWithPath:path];
[MYInternationalManager sharedInternationalManager].languageBunle = needBundle;
//發出通知
[[NSNotificationCenter defaultCenter]postNotificationName:@"UIConfig" object:nil];
}
//切換英文
- (void)changeEnglish
{
//對當前語言進行本地存儲
[[NSUserDefaults standardUserDefaults]setObject:@"en" forKey:@"userLanguage"];
[[NSUserDefaults standardUserDefaults]synchronize];
//改變bundle //英文語言資源文件 en.lproj
NSString *path = [[NSBundle mainBundle]pathForResource:@"en" ofType:@"lproj"];
NSBundle *needBundle = [NSBundle bundleWithPath:path];
[MYInternationalManager sharedInternationalManager].languageBunle = needBundle;
//發出通知
[[NSNotificationCenter defaultCenter]postNotificationName:@"UIConfig" object:nil];
}
//賦值國際化語言
- (void)UIConfig
{
self.label.text = MY_LocalString(@"你好");
}
//進入下一頁
- (void)push
{
SecondViewController *secondVc = [[SecondViewController alloc]init];
[self.navigationController pushViewController:secondVc animated:YES];
}
@end
界面一圖 :
跳轉二級界面
#import "MYBaseViewController.h"
//繼承該父類
@interface SecondViewController : MYBaseViewController
@end
------------------------------------------------------
#import "SecondViewController.h"
@interface SecondViewController ()
@property (nonatomic, strong) UILabel *label;
@end
@implementation SecondViewController
//懶加載
- (UILabel *)label
{
if (!_label) {
_label = [[UILabel alloc]init];
_label.frame = CGRectMake(100, 100, 200, 100);
_label.font = [UIFont systemFontOfSize:25];
_label.textColor = [UIColor blueColor];
}
return _label;
}
- (void)viewDidLoad {
[super viewDidLoad]; //調用父類註冊通知
self.view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.label];
}
//重寫父類方法 , 賦值
- (void)UIConfig
{
self.label.text = MY_LocalString(@"真的會改變喔");
}
跳轉二級界面如下
string文件配置 , 也就是說 , 我可以在這兩種對應的語言間隨意切換 ,
實驗效果
初始狀態 , 模擬器默認英文 , 所以項目 幾個label都顯示英文 , 然後通過點擊切換 , 所有頁面的label都會隨之改變 .
寫在最後 :
整體邏輯 :
1. 當我們國際化一個項目後 , app里加載什麼語言的string文件是依據手機設置的系統語言而定 , 程序每次運行都會讀取手機系統語言 .
2. 當我們想要項目中的語言 , 不跟隨手機系統 , 而由我們自己決定時 , 我們就必須手動來實現這個功能 , 蘋果並未給我們API . 然而 ,存放國際化語言的文件 又存在於 Iproj文件中 , 所以我們可以拿到這個文件的bundle , 存放於公共全局單例中 , 並設置成宏 , 就像系統宏 NSLocalizedString(<#key#>, <#comment#>)一樣 , 然後所有的賦值 , 用這個宏來賦值 (如果是簡單的國際化 , 系統已經幫我們實現了從對應bundle中讀取值 , 這裏我們需要手動實現)
3. 通過點擊切換按鈕 ,改變當前需要讀取語言的bundle(也就是InternationalManager.languageBunlde的值), 發出通知 ,對涉及國際化的地方 , 進行重新賦值.
4. 代碼並未進行封裝和抽取
5. 糾正錯誤 ,獲取手機設置語言 : NSString *phoneLanguage = [[[NSUserDefaults standardUserDefaults]objectForKey:@”AppleLanguages”]lastObject]; 網上基本是通過array[0] , 個人覺得不是很準 , 因爲英文粗出是以en.Iproj存儲 , array[0]獲取的是 en - US
設置全局單例 –> 入口Appdelegate判斷是否本地有存儲的用戶設定的語言 . 如果有 , 則當前需要讀取的bundle就是用戶設置語言的bundle . 如果沒有 , 設置bundle爲系統語言資源的bundle , 並將語言存入沙盒 —->設置單例 屬性 存儲當前bundle值 , 因爲全局都需要調用此單例屬性 —>設置PCH文件 , 將從單例屬性bundle中讀取對應的語言value抽成宏 , 方便使用 —>設置基類 , viewDidLoad註冊通知 , viewWillApear 實現通知方法 , 展示頁面繼承該類 ,並重寫父類通知響應方法 —-> 展示頁面 , 重寫父類方法中實現 控件賦值