你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> 詳述iOS國際化

詳述iOS國際化

編輯:IOS開發基礎

1421201876116084.jpg

本文是投稿文章,作者:龔凱
原文:iOS國際化

在真正將國際化實踐前,只知道通過NSLocalizedString方法將相應語言的字符串加載進來即可。但最近公司項目的新需求增加英文版本,並支持應用內無死角切換~,這才跳過各種坑實現了應用內切換語言,並記錄至此。


環境

系統環境: iOS7 - iOS9

開發環境: Swift2 & Xcode7

DEMO: LocalDemo

locale_0.gif

這個Demo的功能主要是切換語言後相應的界面文字&圖片以及搜索引擎都會隨語言變化。我們會圍繞這個DEMO進行講解,讀者可以先下載這個Demo運行看下效果再往下


iOS國際化原理分析

國際化其實都大同小異,其核心思想就是為每種語言單獨定義一份資源。

iOS就是通過xxx.lproj目錄來定義每個語言的資源,這裡的資源可以是圖片,文本,Storyboard,Xib等。我們可以看看LocalDemo源代碼的物理目錄結構

Base,暫時無需理會

010.png

English

011.png

中文

012.png

每種語言都有自己的 語言代碼.lproj文件夾,加載資源時只需要加載相應語言文件夾下的資源就OK,這步可以系統為我們完成,也可以手動去做。

項目源代碼中如果有多個不同目錄的國際化資源,則會有產生多個xxx.lproj,但在編譯打包後,會集中放在app的根目錄中的xxx.lproj中,不信你看~

013.png

開始國際化


首先點擊項目->PROJECT->Info->Localizations中添加要支持的語言.

015.png

此處Use Base Internationalization開啟狀態下,每個國際化資源文件會有個Base選項,主要針對String,Storyboard,Xib作為一個基礎的模板,像後述storyboard國際化中方案二就是基於Base StoryBoard進行改動。

在點擊+ 添加相應語言時會彈出以下對話框,意思是為現有的資源添加語言文件,我們點擊Finish就行了.

016.png

文本的國際化


主要針對代碼中的字符串進行國際化,比如說一些消息,UI標題等。

我們通過一個Localizable.strings文件來存儲每個語言的文本,它是iOS默認加載的文件,如果想用自定義名稱命名,在使用NSLocalizedString方法時指定tableName為自定義名稱就好了,但你的應用規模不是很大就不要分模塊搞特殊了。

每個資源文件如果想為一種語言添加支持,通過其屬性面板中的Localization添加相應語言就行了,此時Localizable.strings處於可展開狀態,子級有著相應語言的副本。我們把相應語言的文本放在副本裡面就行了.

016.png

此處Base與前面提過到的開啟Use Base Internationalization是有關聯的,只有開啟了全局Use Base Internationalization此處才會顯示。那為什麼這裡沒有勾選Base?Base做為一個基礎模板,作用於Strings文件是沒有太大意義的,另外去掉Base意義著在Base.lproj中少了一個strings文件,APP大小也所有下降,這點對於圖片的Base更是如此.

在上圖可以看到其實就是為每一套語言新建一份strings,其內容采用"key" = "value";的格式,注意有;號

我們在代碼中這樣寫就行了

NSLocalizedString("首頁",comment: "")
NSLocalizedString("好友",comment: "")
NSLocalizedString("我",comment: "")

另外中文strings【Localizable.strings(Simplified)】可以不要的(可以理解為中文為APP的默認語言),因為key就是value,當找不到相應的語言strings或value時會直接返回key。nice!這樣一來我們做文本的國際化就只要維護一個英文副本strings就O了

圖片的國際化


二種方案,通過原生支持與自定義命名

注意,新版Xcode中Images.xcassets不支持國際化(屬性頁面中沒有Localization),Xcode5以前是支持的

  • 方案一:自定義文本命名

017.png

利用文本國際化的方式,在代碼中調用

UIImage(named: NSLocalizedString("search_logo",comment: ""))

不推薦,一是因為做法太low了,工作量明顯加大。二是不能在Storyboard或XIB中使用

  • 方案二:原生支持

018.png

同上,Base副本去掉。另外需要注意的是,使用這種方式,在XIB或Storyboard中引用圖片時如果只使用名稱是實時顯示不了的,一定要加上後綴名。如avater.png

使用方式不變,iOS會自動找相應語言(xxx.lproj)下的圖片

UIImage(named: "avater")

對於圖片的放置,正確姿態應該是需要國際化的圖片放在自定義Group裡面,不需要國際化的圖片放在Images.xcassets

Storyboard&XIB的國際化


前面的兩種資源國際化比較簡單,但Storyboard國際化就稍微麻煩了點。同樣它也有二種方案

  • 方案一:每種語言定制一套Storyboard

019.png

在上圖我們可以看到,每種語言都可以切換為strings或Storyboard(默認為strings)。如果選用Interface Builder Storyboard方案,那麼每種語言都有一套相應的Storyboard,各個語言Storyboard間的界面改動不關聯.

  • 方案二:基於基礎的Base StoryBoard以及每種語言一套strings

020.png

基於一個基礎的Storyboard,可以看作是一個基礎的模板,Storyboard裡面所有的文本類資源(如UILabel的text)都會被放在相應語言的strings裡面。此時我們為Storyboard裡的字符類資源作國際化只需要編輯相應語言的strings就行了

首選方案二。因為采用方案一,意義著你每改動一個界面元素就得去相應語言Storyboard一一改動,那跟為每個語言新起一個項目是一樣的道理。但是采用方案二,我們只需改動Base Storyboard就行了.

注意,方案二中相應語言的strings一旦生成後,Base Storyboard有任何編輯都不會影響到strings,這就意味著如果我們刪除或添加了一個UILabel的text,strings也不能同步改動。

還好,Xcode為我們提供了ibtool工具來生成Storyboard的strings文件。

ibtool Main.storyboard --generate-strings-file ./NewTemp.string

但是ibtool生成的strings文件是BaseStoryboard的strings(默認語言的strings),且會把我們原來的strings替換掉。所以我們要做的就是把新生成的strings與舊的strings進行沖突處理(新的附加上,刪除掉的注釋掉),這一切可以用這個pythoy腳本來實現,見AutoGenStrings.py。然後我們將借助Xcode 中 Run Script來運行這段腳本。這樣每次Build時都會保證語言strings與Base Storyboard保持一致。

021.png

應用內切換語言


應用啟動時,首先會讀取NSUserDefaults中的key為AppleLanguages的內容,該key返回一個String數組,存儲著APP支持的語言列表,數組的第一項為APP當前默認的語言。

在安裝後第一次打開APP時,會自動初始化該key為當前系統的語言編碼,如簡體中文就是zh-Hans。

//獲取APP當前語言
(NSUserDefaults.standardUserDefaults().valueForKey("AppleLanguages") as! Array)[0]

那麼我們要實現語言切換改變AppleLanguages的值即可,但是這裡有一個坑,因為蘋果沒提供給我們直接修改APP默認語言的API,我們只能通過NSUserDefaults手動去操作,且AppleLanguages的值改變後APP得重新啟動後才會生效(才會讀取相應語言的lproj中的資源,意義著就算你改了,資源還是加載的APP啟動時lproj中的資源),猜測應該是框架層在第一次加載時對AppleLanguages的值進行了內存緩沖

//設置APP當前語言
var def = NSUserDefaults.standardUserDefaults()
def.setValue([“zh-Hans”], forKey:"AppleLanguages")
def.synchronize()

那麼問題來了,如何做到改變AppleLanguages的值就加載相應語言的lproj資源?

其實,APP中的Storyboard的加載,圖片與字符串的加載都是在NSBundle.mainBundle()上操作的,那麼我們只要在語言切換後把NSBundle.mainBundle()替換成當前語言的bundle就行了,這樣系統通過NSBundle.mainBundle()去加載資源時實則是加載的當前語言bundle中的資源

lproj目錄可以用一個NSBundle表示:

import Foundation

/**
*  當調用onLanguage後替換掉mainBundle為當前語言的bundle
*/
private  let _bundle:UnsafePointer=  unsafeBitCast(0,UnsafePointer.self)
class BundleEx: NSBundle {
    override func localizedStringForKey(key: String, value: String?, table tableName: String?) -> String {
        if let bundle = languageBundle() {
            return bundle.localizedStringForKey(key, value: value, table: tableName)
        }else{
            return super.localizedStringForKey(key, value: value, table: tableName)
        }
    }
}

extension NSBundle{
    private struct Static {
        static var onceToken : dispatch_once_t = 0
    }
    func onLanguage(){
        //替換NSBundle.mainBundle()為自定義的BundleEx
        dispatch_once(&Static.onceToken) {
            object_setClass(NSBundle.mainBundle(), BundleEx.self)
        }
    }
    
    //當前語言的bundle
    func languageBundle()->NSBundle?{
        return Languager.standardLanguager().currentLanguageBundle
    }
}

以上Languager是 iOS-i18n 開源庫的一部分,我把項目中國際化部分封裝了下,有興趣的童鞋可以去看看

其他


  • 設置運行語言環境

有時我們第一次安裝APP時不想默認跟隨系統,那麼可以通過Xcode的scheme來指定特定語言

022.png

Storyboard實時預覽,直接上圖~

024.png

  • IB中UIImageView國際化無效

解決辦法就是為UIImageView擴展一個方法,然後通過IB中的User Defined Runtime Attributes把imageName傳進去

extension UIImageView{
    var locale:String{
        get{
            return ""
        }
        set(newlocale){
            self.image = localizedImage(newlocale)
        }
    }
}
  • IB中UITextView國際化無效

解決辦法和UIImageView類似,擴展一個方法,然後把self.text做為key去strings文件中拿相應語言的value

extension UITextView{
    var locale:Bool{
        get{
            return true
        }
        set(newlocale){
            self.text = localized(self.text)
        }
    }
}
  • LaunchScreen.xib的國際化

很遺憾,到目前為止,還不支持LaunchScreen.xib的國際化,我們只能通過自定義一個LaunchViewController來完成此需求,但也有些不足,就是應用啟動時會黑屏一段時間,所以建議啟動頁面不要弄國際化.

參考:


iOS國際化——通過腳本使storyboard翻譯自增

Working with Localization

How to force NSLocalizedString to use a specific language

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved