本文部分轉載于 Internationalization Tutorial for iOS [2014 Edition],由 iOS應用國際化教程(2014版)翻譯。
國際化 vs 本地化(Internationalization vs Localization)
在你開始學習本教程之前,很重要的一點是了解國際化和本地化的不同之處,很多人經常會把這兩個概念搞混。
簡單說,國際化是一個應用程式國際相容性設計的過程,比如:
- 以使用者母語處理文本輸入和輸出;
- 處理不同的日期、時間以及數字格式;
- 利用适當的曆法和時區處理資料。
國際化是一項你和開發者通過利用系統提供的API來實作的活動,并在代碼上做一些補充和修改,進而讓應用的中文版、阿拉伯語版本和英文版一樣好。
相比之下,本地化僅僅是把應用的使用者界面和資源翻譯成不同的語言,這是你可以也應該交個别人做的工作,除非你能精通app應該支援的每種語言。
多語言本地化設計的内容
多語言本地化主要本地化四個方面的問題:
- 本地化字元串
- 本地化 Storyboards
- 本地化圖檔、素材
- 本地化應用程式名稱
現在開始(Getting Started)
第一步是 download iLikeIt項目,我們将會在整個教程中使用它。
在Xcode中打開該項目,并在模拟器上運作,你将會看到以下界面:
從截圖中看出,你需要本地化四個項目:
UI元素: Hello Label
UI元素: You like? Button
銷售資料文本: Yesterday you sold 1000000 apps
圖檔文本: I LIKE IT
花一小會兒時間浏覽檔案和檔案夾來熟悉下項目結構。 Main.storyboard包含單個螢幕,它是ViewController類的執行個體。
從代碼中分離文本
目前,應用展示的所有文本都是以寫死字元串存在于Main.storyboard和ViewController裡。為了本地化這些字元串,你需要把它們放在一個單獨的檔案中。
你将會在包中簡單地引用這些字元串,而不是在你的方法中進行寫死。
Xcode使用帶有 “.strings” 擴充名的檔案來儲存和檢索app中使用的所有字元串,以支援每種語言。根據iOS裝置目前使用的語言,代碼中一個簡單的方法調用将會查找并傳回要求的字元數串。
試試看,打開 File>New>File,選擇Resource中Strings Fils,如圖:
點選下一步,為檔案命名為
Localizable.strings
,然後點選save。
注意:Localizable.strings是iOS用來本地化文本預設的檔案名稱。請抑制以其他内容給它命名的沖動,否則以後你每次引用本地化字元串的時候要一次次輸入.strings 檔案名。
現在,你已經建立了Localizable.strings檔案,你需要添加所有的文本–目前寫死在app中的文本。你需要遵從一個特定但簡單的格式:
"KEY" = "CONTENT";
這些鍵/内容對功能就像NSDictionary ,慣例是使用預設的内容翻譯作為内容的鍵:比如You Like?,你應該寫:
"You like?" = "You like?";
Key/content對也可以包含格式化字元串:
"Yesterday you sold %@ apps" = "Yesterday you sold %@ apps";
現在切換至ViewController.m,找到viewDidLoad方法,現在app會為likeButton和salesCountLabel設定文本,展示如下:
_salesCountLabel.text = [NSString stringWithFormat:@"Yesterday you sold %@ apps", @()];
[_likeButton setTitle:@"You like?" forState:UIControlStateNormal];
取而代之的方法是,你需要從此前建立的Localizable.strings檔案中讀入字元串。用一個名為NSLocalizedString的宏修改這兩行代碼如下所示:
_salesCountLabel.text = [NSString stringWithFormat:NSLocalizedString(@"Yesterday you sold %@ apps", nil), @()];
[_likeButton setTitle:NSLocalizedString(@"You like?", nil) forState:UIControlStateNormal];
宏包将一個稍長的代碼片段包裹為一個更易于管理的長度,它使用#define指令建立。如果你想知道NSLocalizedString宏是什麼,可按住Control鍵并在NSLocalizedString點選,可以看到它的定義如下:
#define NSLocalizedString(key, comment)
[[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil]
在目前的語言中,NSLocalizedString宏使用localizedStringForKey方法查找給定鍵值的字元串。它為table name傳遞nil,是以它使用預設的字元串檔案名(Localizable.strings)。更多細節,可檢視蘋果的 NSBundle Class Reference。
注意:這個宏把注釋作為一個參數,但似乎沒什麼用。不像此前那樣需要手動把每個key/value對鍵入Localizable.strings,你還可以使用iOS SDK帶的一個叫做genstrings的工具來自動處理(非常适用于大型項目)。
如果使用這個方法,你可以為每個字元串加上一個注釋,注釋會顯示在預設的字元串邊上,作為translator的輔助。比如你可以添加一個注釋指出字元串在哪裡使用。
已經有了足夠的背景資訊,現在開始吧!
建立并運作你的項目,并且它應該像之前一樣在主螢幕上展示相同的文本,但是西班牙文在哪裡?現在你的應用已經進行了本地化設定,添加翻譯是小事一樁。
添加西班牙語本地化(Adding a Spanish Localization)
想要添加支援另一種語言,你可以點選左窗格中的iLikeIt項目檔案夾,在旁邊的窗格中選擇Project(不是 Target),然後在Info标簽下你會看到一個Localizations分區。點選點選“+”,然後選擇Spanish (es)。
下個螢幕會詢問你哪些檔案需要本地化。選中所有檔案并點選Finish。注意:Localizable.strings沒有展示在這個清單中,不過不用驚慌!
在這一點上,Xcode已經在幕後設定好了一些目錄,針對你選擇的語言,這些目錄包含不同版本的InfoPlist.strings和Main.storyboard。你可以使用Finder打開項目檔案夾看看,你會看到如圖所示:
看到en.lproj和es.lproj了嗎?它們包含檔案的特定語言版本。
en是English的本地化代碼,es是Spanish的本地化代碼。關于其他語言,可參看完整的語言代碼清單。
從現在開始,當你的app想要獲得某個檔案的英文版,它就會去en.lproj中查找,而當它想要某個檔案的西班牙語版時,它就會去es.lproj找。非常簡單!把你的資源檔案放在合适的檔案夾裡,剩下的事情就交給iOS負責了。
但是等等,Localizable.strings呢?想讓Xcode知道你想将它本地化,那你可以在左窗格裡選中檔案,然後在右窗格中打開File Inspector。
你會看到一個Localize标簽,點選并選擇英語(因為目前是英語),最後點選Localize。
現在File Inspector面闆将會展示檔案所屬的語言。目前,正如你所看到的,檔案隻針對英文進行本地化。可通過點選Spanish左邊的框框添加西班牙語本地化。
回到左窗格并點選Localizable.strings旁邊的箭頭,它會顯示出子元素。現在檔案已經有了兩個版本:一個是英文本,一個是西班牙語版。
想修改西班牙語的文本,可選擇Localizable.strings (Spanish),并用如下内容替代它的内容:
"Yesterday you sold %@ apps" = "Ayer le vendió %@ aplicaciones";
"You like?" = "~Es bueno?~";
恭喜!現在你的應用支援兩種語言了!
為了測試和驗證所有事情都正常工作,你可以在模拟器/裝置上把展示語言更改為Spanish,方法是打開設定應用,然後選擇:General -> International -> Language -> Espanol.
如果 Xcode debugger仍在運作中,你可以在Xcode中點選“stop”,然後點選“Build & Run”,你将會看到:
地區vs語言(Locale vs Language)
100萬是個相當不錯的銷售資料,我們可以為它添加一些格式讓它看起來更好!
打開ViewController.m并将為_salesCountLabel設定文本的代碼行替換為:
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSString *numberString = [numberFormatter stringFromNumber:@()];
_salesCountLabel.text = [NSString stringWithFormat:NSLocalizedString(@"Yesterday you sold %@ apps", nil), numberString];
編譯并運作應用程式,那麼數字就會更容易辨認一些。
這對美國人來說非常棒,但在西班牙,100萬寫作“1.000.000″而不是“1,000,000″。在西班牙文環境下運作應用程式,你将會看到逗号用來隔開0。在iOS中,數字格式化是基于地區/國家,而不是語言,是以為了了解某個西班牙人如何檢視銷售資料,可打開Settings.ap,并通過導航改變區域:General -> International -> Region Format -> Spanish -> Spain
在此編譯和運作應用程式,現在你會看到正确的數字格式:
一點額外的前期工作,NSNumberFormatter會自動為合适的區域格式化你的數字。可能的情況下,請拒絕重新發明輪子,因為在iOS上,通常按照蘋果的方式做才能有回報。
國際化Storyboards(Internationalizing Storyboards)
Storyboard中的元素,比如标簽、按鈕以及圖檔可以在代碼中或者直接在storyboard中設定。在設定文本程式設計時,你已經學會了如何支援多種語言,但是螢幕頂部的“Hello”标簽沒有IBOutlet,隻能在Main.storyboard中設定它的文本。
你可以添加一個IBOutlet,将其連接配接到Main.storyboard中的label上,然後使用NSLocalizedString設定其文本屬性,就像使用likeButton和 salesCountLabel那樣,但是這裡有一個本地化storyboard元素更簡單的方法,不需要任何附加代碼。
打開Main.storyboard左側的小三角形,你會看到Main.storyboard (Base)和Main.storyboard (Spanish)。點選Main.storyboard (Spanish)打開編輯器,你會看到storyboard中的本地化文本。你已經有了一個Hello标簽入口,如下:
/* Class = "IBUILabel"; text = "Hello"; ObjectID = "pUp-yc-27W"; */
"pUp-yc-27W.text" = "Hello";
用西班牙語翻譯的“Hola”替換兩個“Hello”:
/* Class = "IBUILabel"; text = "Hola"; ObjectID = "pUp-yc-27W"; */
"pUp-yc-27W.text" = "Hola";
注意:絕對不要直接改變自動生成的ObjectID,也不要複制和粘貼上邊的代碼行,因為标簽的ObjectID可能已經跟上邊展示的不一樣了。
圖檔的國際化(Internationalizing Images)
由于應用程式使用了包含英國文本的圖檔,是以你需要把圖檔本地化,因為零零散散的英文文本會讓你的西班牙應用看起來很不專業,并且也有損于應用的整體易用性和市場潛力。
本地化圖檔,首先需要下載下傳西班牙語版本的圖檔(在大多數浏覽器上是右鍵點選>儲存為):
打開Images.xcassets,拖放此前下載下傳的圖檔megusta.png并添加至左側的圖檔清單,進而把圖檔添加至資産Asset catalog。Asset catalogs不能被國際化,是以你需要有一個方法來本地化圖檔。
打開Localizable.strings (English) ,并添加如下内容:
"imageName" = "ilike";
把以下代碼添加至Localizable.strings (Spanish)檔案:
"imageName" = "megusta";
從現在開始,你将使用imageName key來檢索本地化版本的圖檔。打開ViewController.m并把如下代碼添加到viewDidLoad方法中:
[_imageView setImage:[UIImage imageNamed:NSLocalizedString(@"imageName", nil)]];
如果需要,将模拟器/裝置切換到西班牙語,編譯并運作,然後你會看到本地化圖檔的展示。
現在你已經有了将應用程式針對多種不同語言本地化所需的所有工具。
額外獎勵
作為最後的獎勵,我們來本地化應用的名字。Info.plist有一個特殊的檔案(InfoPlist.strings),你可以在裡邊用字元串覆寫其他語言。為給應用程式一個不同的西班牙語名字,可打開Supporting Files > InfoPlist.strings (Spanish),并插入以下代碼:
"CFBundleDisplayName" = "Me Gusta";
它改變了應用的名稱,像Springboard上展示的那樣。