專案國際化 , 應用內修改app語言
在做專案中 , 如果是人群使用不那麼侷限的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 實現通知方法 , 展示頁面繼承該類 ,並重寫父類通知響應方法 —-> 展示頁面 , 重寫父類方法中實現 控制元件賦值