你好,歡迎來到IOS教程網

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

iOS國際化教程

編輯:IOS開發基礎

Demo同步更新到Swift2.3

本文地址: http://mokai.me/2015/10/iOS-i18n/

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

環境

系統環境: iOS7 or later
開發環境: Swift2.3 & Xcode7.3.1
DEMO: LocalDemo

140025-d272de73f305edfc.gif

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

iOS國際化原理分析

國際化其實都大同小異,其核心思想就是為每種語言單獨定義一份資源
iOS就是通過xxx.lproj目錄來定義每個語言的資源,這裡的資源可以是圖片,文本,Storyboard,Xib等。我們可以看看LocalDemo源代碼的物理目錄結構

Base,暫時無需理會

140025-6d76e9ed4869aac6.jpg

English

140025-4cb3dd5552ce9f17.jpg

中文

140025-79de220b3d755bbb.jpg

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

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

140025-a4430317a1e37607.jpg

開始國際化

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

140025-69b8d995c32c673b.jpg

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

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

140025-cbb92d4ecb1559d3.jpg

文本的國際化

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

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

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

140025-a6cdceae1d7a9794.jpg

此處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以前是支持的

方案一:自定義文本命名

140025-c031a84535395a19.jpg

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

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

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

  • 方案二:原生支持

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

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

      UIImage(named: "avater")

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

  • 140025-0daf31f4df3fa62c.jpg

Storyboard&XIB的國際化

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

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

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

  • 140025-9862b26235644e46.jpg

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

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

  • 140025-e771d8ce73afbfc0.jpg

首選方案二。因為采用方案一,意義著你每改動一個界面元素就得去相應語言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保持一致

140025-82b040d5aa9e9197.jpg

應用內切換語言

應用啟動時,首先會讀取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
    }
}

其他

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

  • 140025-d5b75d56b98e8aac.jpg

  • Storyboard實時預覽
    直接上圖~

  • 140025-147618efb2ff3b2c.jpg

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

    extension UIImageView{
        var local: String {
            get{
                return ""
            }
            set(newlocal) {
                self.image = localizedImage(newlocal)
            }
        }
    }
  • 140025-e97737223904604a.jpg

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

    extension UITextView {
        var local: Bool {
            get{
                return true
            }
            set(newlocale) {
                self.text = localized(self.text)
            }
        }
    }
  • 140025-beeb4742b103cd43.jpg

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

參考

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

  • Working with Localization

  • How to force NSLocalizedString to use a specific language


小小廣告

本人目前是一名自由職業者,接受移動兩端的項目開發,如果你有需求或者有資源請速與我聯系吧,QQ865425695

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