簡介
國際化(internationalisation,i18n):一個程序或軟件可給特定的人群使用而無須修改或重新編譯源代碼。對于 iOS 來說就是,使App能夠適用于不同的語言,地區,文化的過程。
本地化 (localisation,l10n):一個程序或軟件在支持國際化的基礎上,給定程序特定區域的語言信息使其在信息的輸入輸出等處理上適應特定區域人群的使用。對于 iOS 來說就是,將App的內容翻譯為多種語言的過程。
總的來說,國際化是開發者的任務,是一個一般化的過程;而本地化則是翻譯者所做的事情,是一個具體的過程。國際化的運作為本地化工作提供了可能。一個軟件的國際化實現需要本地化的支持。
用戶設置對于國際化的影響
在 iOS 設置應用中的通用->語言與地區
中可以分別設置語言,地區和日歷。一個支持國際化的應用會根據語言的設置選擇相應的本地化包,語言的改變會導致系統重新啟動,以便所有應用重新應用新的語言設置。地區主要影響日期,時間,數字,貨幣等數據的格式。用戶可以通過日歷選擇不同的歷法。地區和日歷的設置都是即時的,不需要重新啟動系統。另外,在語言與地區中還可以設置首選語言順序列表,應用會以此為依據選擇本地化語言包(按順序找第一個應用支持的本地化語言包)。
使應用支持國際化
使用Base Internationalization國際化界面文本
從Xcode5之后,工程默認支持Base Internationalization。
Base Internationalization將界面文本與.storyboard和.xib文件分離,使得項目只需要一套.storyboard和.xib文件。在.storyboard和.xib文件中使用你設置的開發語言填寫界面文本,這套.storyboard和.xib文件就叫做Base Internationalization。之后在本地化時,將以開發語言為資源翻譯成多種語言,這些語言以.strings格式的文本文件對應相應的.storyboard和.xib文件。
一般情況下,每種語言相對應的文件會放在相應的語言包下,比如en.lproj、zh.lproj等。但是所有的.storyboard和.xib文件會放在一個名為Base.lproj的文件夾內,這就是為了共用同一套.storyboard和.xib文件,這樣,在其他語言包內就可以只存放相應的.strings格式的文本文件了。由于.storyboard和.xib文件中已經使用開發語言填寫了界面文本,所以在開發語言的語言包內就不需要再有相對應的.strings文件了。
關于開發語言的設置:
- 在項目的.xcodeproj文件中打開project.pbxproj
- 搜索developmentRegion,并將該key的值改為所要設置的開發語言對應的語言ID,比如en、zh等
-
保存,效果如下圖所示
使用Auto Layout輔助國際化界面文本
因為所支持的語言所占界面空間不同,Auto Layout可以使一套界面適配布局不同的文字。使用時注意:
- 顯示文字的組件不使用固定寬度的約束,否則在某些語言下文字可能顯示不完全,text fields和labels默認的行為都是自動適配到內容的合適的尺寸,完全可以使用這種方式。
- 添加水平間距約束時,使用leading和trailing,這樣針對左右順序不同的語言可互換左右間距值。
- 因為語言不同視圖組件所占空間不同,所以約束應與相鄰視圖組件建立,這樣當語言改變時,其他視圖組件也會自動適配。
國際化代碼中的文本
在項目中,一些在界面上顯示的文本是需要在代碼中提供的(比如錯誤信息提示),而對這部分文本國際化的方式是將文本寫入.strings文件中,之后使用宏NSLocalizedString
去從文件中獲取。在.strings文件中鍵值對的格式為
"key1" = "value1";
"key2" = "value2";
使用NSLocalizedString
去取值的方式為
NSLocalizedString("key1", "comment");
NSLocalizedStringFromTable("key1", "tableName", "comment");
其中標準的NSLocalizedString
函數會從main bundle中的Localizable.strings文件中找到相應鍵的值,而comment是對該鍵值對的解釋。同理NSLocalizedStringFromTable
函數的第二個參數可以指定其他.strings文件的文件名。
國際化數據格式
不同的國家和地區有著不同的日期,時間,貨幣,數值等的格式,所以要在代碼中根據用戶設置的地區來正確的格式化這些數據。
格式化時用到NSLocale類,NSLocale類封裝了某一個地區的相應的格式化信息,如果要獲得用戶當前設置地區的NSLocale實例可以使用[NSLocale currentLocale]
或者[NSLocale autoupdatingCurrentLocale]
,兩者的區別是,后一個類方法返回的值會根據用戶設置的改變而改變,而前者不會??梢酝ㄟ^NSLocal查看很多這一地區的數據格式化信息,例如(其他key值請查閱相關文檔):
NSNumber *metricSystem = [[NSLocale currentLocale] objectForKey:NSLocaleUsesMetricSystem];
NSString *currencySymbol = [[NSLocale currentLocale] objectForKey:NSLocaleCurrencySymbol];
// 用當前地區的語言顯示語言ID
NSLocale *zhHansLocal = [NSLocale localeWithLocaleIdentifier:@"zh-Hans"];
NSString *currentLocalZH = [zhHansLocal displayNameForKey:NSLocaleIdentifier value:@"zh-Hant-TW"];
NSString *currentLocalEN = [zhHansLocal displayNameForKey:NSLocaleIdentifier value:@"en-US"];
// 獲取該地區所使用的引號
NSString *bQuote = [locale objectForKey:NSLocaleQuotationBeginDelimiterKey];
NSString *eQuote = [locale objectForKey:NSLocaleQuotationEndDelimiterKey];
// 國際化大小寫轉換
NSString *localizedUppercaseString = [string uppercaseStringWithLocale:[NSLocale currentLocale]];
NSString *localizedlowercaseString = [string lowercaseStringWithLocale:[NSLocale currentLocale]];
NSString *localizedCapitalizedString = [string capitalizedStringWithLocale:[NSLocale currentLocale]];
當使用[NSString stringWithFormat]
去拼裝字符串時,如果傳入的參數帶有數值,日期等需要格式化的內容,最好使用以下方式(更好的方式是使用formatter進行轉換):
// 使[NSLocale systemLocale]
NSString *localizedString = [NSString localizedStringWithFormat:@"%3.2f", myNumber];
NSString *localizedString = [[NSString alloc] initWithFormat:@"%@" locale:[NSLocale localeWithLocaleIdentifier:@"zh"], [NSDate date]];
使用formatter格式化日期和時間時的國際化
// 使用預設的格式
NSString *localizedDateTime = [NSDateFormatter localizedStringFromDate:[NSDate date] dateStyle:NSDateFormatterMediumStyle timeStyle:NSDateFormatterShortStyle];
// 使用自定義的格式
NSDateFormatter *dateFormatter = [NSDateFormatter new];
NSString *localeFormatString = [NSDateFormatter dateFormatFromTemplate:@"dMMM" options:0 locale:dateFormatter.locale];
dateFormatter.dateFormat = localeFormatString;
NSString *localizedString = [dateFormatter stringFromDate:[NSDate date]];
對于數值型數據的格式化主要包括小數、千位分割、貨幣、百分比。同樣也需要支持國際化。
NSString *localizedString = [NSNumberFormatter localizedStringFromNumber:myNumber numberStyle:NSNumberFormatterDecimalStyle];
同理對于NSCalendar的使用也受NSLocale的影響。
地區和時區的設置發生變化的通知分別是NSCurrentLocaleDidChangeNotification
和NSSystemTimeZoneDidChangeNotification
國際化支持相反的語言方向
有一些語言,比如阿拉伯語等,方向是從右向左的。使用
Base Internationalization和Auto Layout在大部分情況下可以很好的支持這些語言,一些不支持的情況,可以在代碼中進行如下判斷:
if ([UIView userInterfaceLayoutDirectionForSemanticContentAttribute:view.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft) {
…
}
本地化應用
當我們使應用支持國際化后,就可以配合翻譯人員完成應用本地化的工作了。大致的過程是,通過 Xcode 將開發語言的文本資源導出為 XLIFF (XML Localisation Interchange File Format) 文件,翻譯人員在完成該文件內的相應翻譯內容后,再通過 Xcode 將文件導入,這時所翻譯的語言資源就會被加入到工程中了。
導出 XLIFF
- 在 Xcode 中選中工程或 target。
- 選擇 Editor > Export For Localization。
之后 Xcode 會將 .xliff 文件導出到你指定的目錄中。如果你在工程的 Localizations 設置項中還沒有添加過其他的語言,而只有開發語言(如 English)一種,那么導出的文件為 en.xliff。這時的 en.xliff 文件中并沒有指明要翻譯的目標語言,而這會導致在翻譯完成后文件無法正常導入,需要在文件源碼中添加target-language=""
字段。更好的做法是,在創建工程時就把要從開發語言翻譯的其他語言(如 Chinese)添加到 Localizations 中,這樣導出的文件就為 zh.xliff,且其中已標明目標語言,可以正常的導入。另一個要注意的地方是,.strings
文件支持國際化時,開始的開發語言版本不要放到Base.lproj文件夾中,Base.lproj文件夾中存放的全部是界面文件,而是應直接放入對應的開發語言的.lproj文件中。
命令行方式:
xcodebuild -exportLocalizations -localizationPath <dirpath> -project <projectname> [[-exportLanguage <targetlanguage>]]
在等待翻譯資源的時候,你并不希望界面或者僅僅是界面中的文本再發生變化,這個時候可以使用 Xcode 的一項特性 Locking Views。可以只針對某一 view 修改,在該 view 的 Identity inspector 中修改 Lock 項。也可設置整個 nib 文件的該屬性,選中對應 nib 文件后,修改 Editor > Localization Locking 菜單項。
導入 XLIFF
- 在 Xcode 中選中工程或 target。
- 選擇 Editor > Import Localizations。
Xcode 會從 XLIFF 文件中解析出翻譯內容的 .strings 文件,然后放到對應語言的 .lproj 目錄中,而在工程中,nib 文件和 .strings 文件都以組的形式管理。
命令行方式:
xcodebuild -importLocalizations -localizationPath <filepath> -project <projectname>
其他資源
除了文本資源外,其他的資源文件,比如圖片、音視頻文件等可能也需要針對不同地區使用不同內容。
在國際化資源文件后,就可以將本地化版本的文件加到對應的 .lproj 目錄中。
最佳實踐
對于大部分的國內開發者來說,其應用主要針對使用中文的用戶,而鑒于翻譯資源的有限,可能針對其他地區用戶僅能支持國際通用的英文。在這種情況下,最合適的流程是,在開發過程中,開發者使用中文填寫界面文本,而之后由翻譯人員翻譯為英文,最后使應用在所有不匹配中文地區的設備上,都能使用英文資源。
要達到上述效果,首先要設置工程的開發語言為中文,如上文所述,修改工程文件中的字段 developmentRegion 為 zh,同時確保需要翻譯的英文已添加在列表中。在完成國際化的工作后,就可導出 en.xliff 文件交由翻譯人員翻譯,并最終導入。最后一步,是要確認 Info.plist 文件中的 CFBundleDevelopmentRegion 字段設置為 en,該字段決定在用戶當前地區未匹配到翻譯資源時,使用應用已有的哪一套翻譯資源作為界面文本顯示。
需要注意的一點是,盡管文檔中提到:
You can choose from more than 100 different languages and dialects designated by regions to localize your app. However, the more general you make your localized resources, the more regions you can support with a single set of resources. This can save a lot of space in your app bundle and help reduce localization costs. For example, if you don’t need to distinguish between different regions that use the English language, you can add English to support users in the United States, United Kingdom, and Australia. Even if you provide region-specific resources always provide a complete set of language-specific resources for all the languages you support.
但當用戶設備的語言設置為繁體中文時,并不會匹配到中文(zh)的語言資源,所以在上述配置后,最終在繁體中文的設備上,應用會使用英文資源。如果想要所有中文設備都顯示作為開發語言的簡體中文,最后需要多添加一種語言 zh-Hant,但不需要進一步翻譯,直接以開發語言為模板生成即可。
測試
在 Xcode 中,可以使用預覽功能,在不運行應用的情況下,檢測國際化和本地化的結果。選擇一個 .storyboard 或 .xib 文件,打開輔助編輯器,在相關文件中選擇 Preview,通過右下角語言選項,可以在國際化后選擇 Double-Length Pseudolocalizations 進行檢測,本地化后選擇對應語言進行檢測。
預覽過后,可以通過設置應用啟動時的參數,在設備上測試國際化和本地化的結果,而不需要修改設備的設置。編輯 Scheme,選擇 Run -> Options,在 Application Language 一項中,可以選擇對應語言資源,Double-Length Pseudolocalizations 以及 Right to Left Pseudolocalizations。勾選 Show non-localized strings 項,可以檢測未本地化的文本,其會以大寫形式顯示。