iOS - 項目國際化 , 應用內修改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 實現通知方法 , 展示頁面繼承該類 ,並重寫父類通知響應方法 —-> 展示頁面 , 重寫父類方法中實現 控件賦值


缺陷 : 以後只要涉及國際化語言切換的控件賦值 , 都必須抽取在父類響應的通知方法中

參考資料: http://www.cocoachina.com/ios/20151120/14258.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章