iOS如何在App內切換多語言

最近寫項目的時候遇到這樣一個需求,要求實現(xiàn)類似微信的多語言功能,

  • 可在App內立即切換多語言,不需要重啟App
  • 下次進入App要記住之前的設置

我在網(wǎng)上找到很多關于多語言的博客,但是絕大多數(shù)都是需要重啟App,或者下次進入App無法記住之前的設置,還有些就是直接貼了人家代碼過來根本不知道為什么這么寫。我在看過網(wǎng)上的博客之后,自己寫了一個demo,供大家參考。

下面這是效果圖:


Demo源代碼解析

1.項目配置


2.創(chuàng)建多語言源文件

選擇Strings File

給文件起個名,最好叫Localizable,因為我們后面獲取語言包就用這個名字



3.點擊項目中的文件



4.點擊本地化按鈕(Localize...)



5.出現(xiàn)彈窗,選擇本地化



6.把對應的語言勾選上


這時項目中會多出幾個文件,這些文件就是多語言的源文件


7.根據(jù)自身項目的需求,按照下面的格式寫入對應的語言對


至此,準備工作完成!


8.接下來我們要寫一個工具類,管理多語言的切換

下面是源碼:

//
//  NSBundle+Language.h
//  App內切換多語言
//
//  Created by 崇 on 2018.
//  Copyright ? 2018年 崇. All rights reserved.
//

#import <Foundation/Foundation.h>

#define GCLocalizedString(KEY) [[NSBundle mainBundle] localizedStringForKey:KEY value:nil table:@"Localizable"]

@interface NSBundle (Language)

+ (void)setLanguage:(NSString *)language;

@end
//
//  NSBundle+Language.m
//  App內切換多語言
//
//  Created by 崇 on 2018.
//  Copyright ? 2018年 ChinaChong. All rights reserved.
//

#import "NSBundle+Language.h"
#import <objc/runtime.h>

static NSString *const GCLanguageKey = @"AppLanguagesKey";

@interface BundleEx : NSBundle

@end

@implementation BundleEx

- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName {
    
    // 當前語言
    NSString *currentLanguage = [[NSUserDefaults standardUserDefaults] objectForKey:GCLanguageKey];
    
    // 設置默認語言
    currentLanguage = currentLanguage ? currentLanguage : @"zh-Hans";
    
    // 每次需要從語言包查詢語言鍵值對的時候,都按照當前語言取出當前語言包
    NSBundle *currentLanguageBundle = currentLanguage ? [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:currentLanguage ofType:@"lproj"]] : nil;
    
    // 下面return中普通 bundle 在調用 localizedStringForKey: 方法時不會循環(huán)調用,雖然我們重寫了 mainBundle 單例的 localizedStringForKey: 方法,但是我們只修改了 mainBundle 單例的isa指針指向,
    // 也就是說只有 mainBundle 單例在調用 localizedStringForKey: 方法時會走本方法,而其它普通 bundle 不會。
    return currentLanguageBundle ? [currentLanguageBundle localizedStringForKey:key value:value table:tableName] : [super localizedStringForKey:key value:value table:tableName];
}

@end

@implementation NSBundle (Language)

+ (void)load {
    static dispatch_once_t onceToken;
    
    // 保證只修改一次 mainBundle 單例的isa指針指向
    dispatch_once(&onceToken, ^{
        
        // 讓 mainBundle 單例的isa指針指向 BundleEx 類
        object_setClass([NSBundle mainBundle], [BundleEx class]);
    });
}

+ (void)setLanguage:(NSString *)language {
    
    // 將當前手動設置的語言存起來
    [[NSUserDefaults standardUserDefaults] setObject:language forKey:GCLanguageKey];
    [[NSUserDefaults standardUserDefaults] synchronize];
    
}

@end
工具類的大致思路:
  • 工具類本身是NSBundle的類目,用來增加一個切換多語言的方法,
    + (void)setLanguage:(NSString *)language

  • .h里面定義了一個宏GCLocalizedString(KEY),用來快速方便的調用方法localizedStringForKey:。

  • .m里定義了一個繼承自NSBundleBundleEx類,BundleEx類中重載的父類的localizedStringForKey:方法,代碼中的注釋解釋了為什么修改isa指向的類后不會循環(huán)調用,這里不再贅述。每一次查詢多語言鍵值對,都是通過當前語言獲取的語言包,所以App切換多語言是立即執(zhí)行的,不用重啟App。

  • .m中后面的部分+ (void)load 方法目的是讓程序在啟動后,[NSBundle mainBundle] 返回的單例屬于BundleEx類,這樣[NSBundle mainBundle] 返回的單例調用localizedStringForKey:方法就會走BundleEx類中重載的方法,確保取到正確的語言包。

  • + (void)setLanguage:(NSString *)language方法就是單純的存儲當前語言。

  • 工具類的精髓在于:

    1. 避免再寫一個單例,直接利用 mainBundle 這個現(xiàn)有的單例,減少內存占用。
    2. 修改 mainBundleisa指向,就會走我們重載的方法。
    3. 重載的localizedStringForKey:方法是其父類 NSbundle 的方法,用來獲取對應語言包中的語言鍵值對。


9.工具類的使用
//
//  ViewController.m
//  App內切換多語言
//
//  Created by 崇 on 2018.
//  Copyright ? 2018年 崇. All rights reserved.
//

#import "ViewController.h"
#import "NSBundle+Language.h"

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UILabel *label;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self updateLabel];
}

- (IBAction)switchToChinese:(id)sender {
    [NSBundle setLanguage:@"zh-Hans"];
    [self updateLabel];
}

- (IBAction)switchToEnglish:(id)sender {
    [NSBundle setLanguage:@"en"];
    [self updateLabel];
}


- (IBAction)switchToKorean:(id)sender {
    [NSBundle setLanguage:@"ko"];
    [self updateLabel];
}

- (void)updateLabel {
    self.label.text = GCLocalizedString(@"皇帝");
}


聲明:本文主要結構代碼是在網(wǎng)上眾多博客參考而來,加上自己理解后加上了一些自己的改動。并非我自己絕對的原創(chuàng),但是大多數(shù)的博客都粘貼復制相同的代碼,找不到是誰的原創(chuàng),如果真身看到了此篇博客還請指出,我會貼上您原作的地址!??????

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 1.ios高性能編程 (1).內層 最小的內層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結構(3).初始化時...
    歐辰_OSR閱讀 30,242評論 8 265
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,657評論 1 32
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,040評論 25 709
  • “孤獨”在中文字典里不知是褒義詞還是貶義詞,很多人以它寫文,有的歌頌,有的辯證,有的排斥,有的心痛。 保羅·奧斯特...
    楊揚呢閱讀 431評論 0 3
  • 跟我家貓,已經(jīng)在家宅了四天了。就一直不說話,要么玩手機,要么看電影,要么看著么么發(fā)呆,要么睡覺。 可能是又抽風,一...
    Annnnny閱讀 191評論 0 0

友情鏈接更多精彩內容