iOS 多國語言本地化與App內語言切換(Swift)

ZY_FlyWay發表於2018-11-26

前言

語言本地化 大家肯定都多少都聽過,今天我要分享的是快速實現語言本地化,與App內語言切換

核心內容主要是三個部分

  • storyboard/xib本地化

  • 純程式碼本地化

  • 語言切換

準備工作

專案中新增語言

storyboard/xib本地化

storyboard/xib做本地化Xcode基本上是一鍵搞定了。

很簡單

只要勾勾選選就可以了

這邊只涉及到一個更新的問題

通過 ibtools命令 可以使storyboard/xib生成新的程式碼

首先cd 到stroyboard/xib 目錄

執行ibtool xxx.storyboard --generate-strings-file new.strings

開啟new.strings 將新內容手動複製到原來的string上。

純程式碼本地化

建立string檔案

 

 

 

勾選語言,把幾種全部勾上,包括Base (為下文使用指令碼生成程式碼做準備)

參考此篇文章進行指令碼新增iOS中多語言本地化流程的優化

 

新增指令碼

將指令碼執行移動到編譯上方

 

移動位置

新增指令碼

1

2

3

4

5

6

7

8

9

10

11

12

13

14

# Localizable.strings檔案路徑

localizableFile="${SRCROOT}/${PROJECT_NAME}/Support/en.lproj/Localizable.strings"

# 生成的swift檔案路徑(根據個人習慣修改)

localizedFile="${SRCROOT}/${PROJECT_NAME}/Source/Utils/LocalizedUtils.swift"

# 將localizable.strings中的文字轉為swift格式的常量,存入一個臨時檔案

sed "s/^\"/  static var localized_/g" "${localizableFile}" | sed "s/\" = \"/: String { return \"/g" | sed "s/;$/.localized }/g" "${localizedFile}.tmp"

# 先將localized作為計算屬性輸出到目標檔案

echo -e "import Foundation\n\nextension String {\n  var localized: String { return NSLocalizedString(self, comment: self) }" "${localizedFile}"

# 再將臨時檔案中的常量增量輸出到目標檔案

cat "${localizedFile}.tmp" >> "${localizedFile}"

# 最後增量輸出一個"}"到目標檔案,完成輸出

echo -e "\n}" >> "${localizedFile}"

# 刪除臨時檔案

rm "${localizedFile}.tmp"

這裡需要注意的是幾個目錄需要對應好,否則會報錯

build一下就能自動生成相關程式碼 就可以直接用了,具體用法可以參考上面提到的那篇文章iOS中多語言本地化流程的優化

語言切換

語言切換的基本原理是使用Userdefault儲存當前選擇的語言,在設定的時候改變其內容即可

主要涉及到兩個問題

  • storyboard/xib如何切換語言

  • 如何重新整理介面

對於上面都算是正常的本地化的內容,基本上介紹本地化的教程都會有。

對於自動化指令碼這塊算是比較新穎。

但是,指令碼對於帶空格的字串生成的內容還是有問題,由於是使用sed命令,本人還不是很熟,只能想其他辦法,這時候Base.lproj就派上用場了

我們將空格都替換成下劃線,或者駝峰命名,在Base中一一對應,

在具體的en和zh中寫具體內容,這時Base的作用就是為了方便自動生成程式碼而已了。(如果不想搞亂Base,新建一個即可)

關於storyboard/xib切換語言

替換Bundle即可

自定義一個Bundle,重寫localizedString方法,每次都從Userdefault中獲取當前選擇語言,再使用方法替換將Bundle.main替換成自定義的Bundle

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

enum Language : String {

    case english = "en"

    case chinese = "zh-Hans"

}

 

/**

 *  當呼叫onLanguage後替換掉mainBundle為當前語言的bundle

 */

 

class BundleEx: Bundle {

     

    override func localizedString(forKey key: String, value: String?, table tableName: String?) -> String {

        if let bundle = Bundle.getLanguageBundel() {

            return bundle.localizedString(forKey: key, value: value, table: tableName)

        }else {

            return super.localizedString(forKey: key, value: value, table: tableName)

        }

    }

}

 

 

extension Bundle {

     

    private static var onLanguageDispatchOnce: ()->Void = {

        //替換Bundle.main為自定義的BundleEx

        object_setClass(Bundle.main, BundleEx.self)

    }

     

    func onLanguage(){

        Bundle.onLanguageDispatchOnce()

    }

     

    class func getLanguageBundel() -> Bundle? {

        let languageBundlePath = Bundle.main.path(forResource: UserDefaults.standard[AppStatic.kCurrentLanguage] asString, ofType: "lproj")

//        print("path = \(languageBundlePath)")

        guard languageBundlePath != nil else {

            return nil

        }

        let languageBundle = Bundle.init(path: languageBundlePath!)

        guard languageBundle != nil else {

            return nil

        }

        return languageBundle!

         

    }

}

其中為Userdefault自定義了下標

1

2

3

4

5

6

7

8

    public subscript(key: String) -> Any? {

        get {

            return object(forKey: key)

        }

        set {

            set(newValue, forKey: key)

        }

    }

在讀取字串時執行一次

Bundle.main.onLanguage()

我就直接寫到了指令碼里,將指令碼修改如下(幾個檔案的路徑自己注意一下)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

# Localizable.strings檔案路徑

localizableFile="${SRCROOT}/Base.lproj/Localizable.strings"

# 生成的swift檔案路徑(根據個人習慣修改)

localizedFile="${SRCROOT}/Public/LocalizedUtils.swift"

# 將localizable.strings中的文字轉為swift格式的常量,存入一個臨時檔案

sed "s/^\"/  static var localized_/g" "${localizableFile}" | sed "s/\" = \"/: String { return \"/g" | sed "s/;$/.localized }/g" "${localizedFile}.tmp"

# 先將localized作為計算屬性輸出到目標檔案

echo -e "import Foundation\n\nextension String {\n  var localized: String { Bundle.main.onLanguage() \n return NSLocalizedString(self, comment: self) }" "${localizedFile}"

# 再將臨時檔案中的常量增量輸出到目標檔案

cat "${localizedFile}.tmp" >> "${localizedFile}"

# 最後增量輸出一個"}"到目標檔案,完成輸出

echo -e "\n}" >> "${localizedFile}"

# 刪除臨時檔案

rm "${localizedFile}.tmp"

關於重新整理介面

對於所有介面的重新整理最方便的就是重新設定rootViewController

將keyWindow先變黑,假裝loading個幾秒,再變回來即可。

如果需要再次回到之前所在頁面,再新增相應的跳轉VC的方法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

    func chooseLanguage() {

        DispatchQueue.global().async {

            let sheet = UIAlertController.init(title: String.localized_Choose_Language, message: nil, preferredStyle: .actionSheet)

             

            sheet.addAction(UIAlertAction.init(title: String.localized_English, style: .default, handler: { (action) in

                UserDefaults.standard[AppStatic.kCurrentLanguage] = Language.english.rawValue

                UIApplication.shared.keyWindow?.rootViewController = RedbotTabBar()

                UIApplication.shared.keyWindow?.alpha = 0

                AlertHelper.showHudWithMessage(message: "Setting Language...")

                DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+1, execute: {

                    UIView.animate(withDuration: 1, animations: {UIApplication.shared.keyWindow?.alpha = 1})

                    AlertHelper.hideHudMessage()

                })

                 

            }))

            sheet.addAction(UIAlertAction.init(title: String.localized_Chinese, style: .default, handler: { (action) in

                UserDefaults.standard[AppStatic.kCurrentLanguage] = Language.chinese.rawValue

                UIApplication.shared.keyWindow?.rootViewController = RedbotTabBar()

                UIApplication.shared.keyWindow?.alpha = 0

                AlertHelper.showHudWithMessage(message: "Setting Language...")

                DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+1, execute: {

                    UIView.animate(withDuration: 1, animations: {UIApplication.shared.keyWindow?.alpha = 1})

                    AlertHelper.hideHudMessage()

                })

            }))

            sheet.addAction(UIAlertAction.init(title: String.localized_Cancel, style: .cancel, handler: nil))

            self.present(sheet, animated: true, completion: nil)

        }

    }

至此App的語言切換與本地化就都講完了,是不是很簡單呢~~

後記

對於普通的小專案本地化的內容其實遠沒有那麼複雜,需要替換的內容也很少,只要新增過一次語言,再新增新語言就非常簡單了。

相關文章