國際化及本地化概念
將標題取名爲國際化及本地化(internationalization and localization),是因爲這兩個概念是有差異的,而這個差異常常被我們忽略,以下是維基百科的解釋:
國際化是指在設計軟件,將軟件與特定語言及地區脫鉤的過程。當軟件被移植到不同的語言及地區時,軟件本身不用做內部工程上的改變或修正。本地化則是指當移植軟件時,加上與特定區域設置有關的信息和翻譯文件的過程。
國際化和本地化之間的區別雖然微妙,但卻很重要。國際化意味着產品有適用於任何地方的“潛力”;本地化則是爲了更適合於“特定”地方的使用,而另外增添的特色。用一項產品來說,國際化只需做一次,但本地化則要針對不同的區域各做一次。這兩者之間是互補的,並且兩者合起來才能讓一個系統適用於各地。
有些時候我們也會用國際化或者全球化代替這兩者含義。
作爲一款優秀的產品我們做多語言版本時不應僅僅考慮到翻譯這一層面,還有更多本地化相關內容需要我們注意,這篇文章主要涉及的也是本地化這一塊。
國際化工作流程
本篇文章主要介紹Internationalize和Test這兩步。
本文目錄爲:
1.增加多語言
2.UI元素的本地化
3.資源文件本地化
4.字符串相關的本地化
5.使用NSLocal進行本地化
6.從右到左語言的處理
7.本地化測試
增加多語言
1、在項目導航欄選擇項目(不是target)
2、在Localizations一欄,點擊“+”號,添加語言
每個條目都是由語言名稱和語言id構成,例如Chinese(Simplified)(zh-Hans)
, Japanese(ja)
至此我們的項目就開啓了對應語言的本地化支持。
3、在對話框中選中你想本地化的文件。
語言和區域的影響
通過觀察系統日曆,我們可以看到即使語言一樣,國家區域的不一致也會有一些約定上的區別,關於日期和時間的本地化會在下面介紹。1、語言設置:Setting -> General -> Language & Region
同樣的,關於Region和Calendar的設置也在該頁面。資源文件的本地化
storyboard, xib文件
對於sotryboard
和xib
文件的本地化是Xcode直接支持的。
在添加語言時會提示我們自動選擇創建本地化文件,如果是在添加語言之後創建的IB文件,可以通過xcode右側屬性欄中點擊Localize...
生成本地化文件。
圖片文件
1、方法一 對於圖片內容我們可以通過同IB文件的方式進行本地化,但是有一個限制就是圖片要是放到項目文件層級的,而不能放到Assets文件夾中。 好消息是Xcode 11將放開這種限制,對於Assets引入的圖片也可以做本地化處理。
2、方法二 除了Xcode本身支持的方式,我們還可以通過命名來區分圖片內容,把圖片名當做需要本地化的字符串,各個語言對應不同版本的圖片名,這樣也可以實現圖片文件的本地化。
音視頻及其他資源文件
如果是內置的像是音視頻,json或者其他類型的配置文件這類內容,可以使用圖片文件的方法二進行引入。
更多詳細的設置可以參考這個文章:iOS語言國際化/本地化-實踐總結
UI元素的本地化
使用Auto layout
Auto layout
是相對佈局,它有能力在語言和區域變化時進行自適應。以下有幾點使用auto layout的技巧:
1、移除寬度的約束 相同含義下不同語言寬度往往不一樣,應該讓控件能夠自適應。
2、使用內容內部大小
fields和label默認是自動調整大小的,如果一個顯示本地化內容的視圖需要這個功能,選擇該view,選擇Editor > Size To Fit Content
3、使用leading
和trailing
屬性
正常leading
和trailing
對應left
和right
,他們含義相同。但是有些國家,像是希伯來和阿拉伯的人使用習慣是從右往左。如果你是使用leading
和trailing
,在該環境下將自動對應right
和left
。
4、將視圖固定到相鄰視圖 就是定義相鄰約束,避免某一視圖變化導致重疊。
使用僞本地化發現問題
這個功能只支持使用storyboard
和xib
進行佈局的UI。
1、選中需要測試的.storyboard或者.xib文件
2、選擇菜單欄 View > Assistant Editor > Show Assistant Editor
字符串相關的本地化
使用Unicode字符串
對於所有面向用戶的字符串都要使用NSString, NSAttributedString
,對於Swift就是String, AttributedString
,因爲他們支持Unicode,Unicode是世界上所有書寫系統的字符編碼標準。
對於一些特殊的字符串需求:
1、訪問字符串中的字符
使用NSString中的rangeOfComposedCharacterSequenceAtIndex:
和 rangeOfComposedCharacterSequencesForRange:
方法,他們會確保你在取字符串時不會破壞原文本。看一個例子你可能會明白:
2、遍歷字符串
如果我們要遍歷展示下面的字符串:
可以通過enumerateSubstringsInRange:options:usingBlock:
方法,其中options參數如果傳遞NSStringEnumerationByComposedCharacterSequences
將會按照最小字符進行遍歷,如果選用NSStringEnumerationByWords
將會按照詞語進行遍歷。
以上例子使用該值遍歷的結果是:
更多關於字符串相關的本地化問題可以參照該條視頻: WWDC 2013 Making Your App World-Ready
3、關於人名,郵寄地址,電話號碼的檢測 因爲不同國家對於人名和電話號碼的規則差別較大,我們可以針對不同國家寫正則進行檢測,也可以使用蘋果提供的一個特殊含義字符的檢測類:NSDataDetector
支持檢測的類型包括日期,地址,鏈接,手機號,交通信息。
獲取當前語言
將語言設置爲English(United Kingdom),區域設置成United States,通過以下API獲取到:
//en
NSString *languageID = [[NSBundle mainBundle] preferredLocalizations].firstObject;
複製代碼
一般獲取語言所用的方式是通過Bundle也就是第一種方式。
使用NSLocal進行本地化
NSLocale對象封裝關於特定區域格式化標準的信息,包括日期,時間,測量,數字,貨幣等一系列內容。
將語言設置爲English(United Kingdom),區域設置成United States,通過以下API獲取到:
//en-GB_US
[NSLocale currentLocale].localeIdentifier;
//en
[NSLocale currentLocale].languageCode;
複製代碼
其中languageCode跟通過Bundle獲取到的是一樣的。
其中localIdentifier表示爲en-GB_US
,對應爲:語言id-國家id_區域碼
,這幾個內容都可以通過NSLocal對象取到。
獲取特定語言的引號
因爲每種語言對於引號的使用是不一樣的,我們可以通過NSLocal獲取到引號
//1.Get the language that the app is using.
NSString *languageID = [[NSBundle mainBundle] preferredLocalizations].firstObject;
//2.Get the associated locale object.
NSLocale *locale = [NSLocale localeWithLocaleIdentifier:languageID];
//3.Get the beginning and ending symbols for quotes from the locale object.
bQuote = [locale objectForKey:NSLocaleQuotationBeginDelimiterKey];
eQuote = [locale objectForKey:NSLocaleQuotationEndDelimiterKey];
//4.Format a string using the locale-sensitive quotes.
quotedString = [NSString stringWithFormat:@"%@%@%@", bQuote, myText, eQuote];
複製代碼
以下展示了不同區域對於myText
爲@"iPhone"
時的字符串效果。
字符串的本地化
1、創建格式化字符串
應該使用localizedStringWithFormat:
而不是stringWithFormat:
。
NSString *localizedString = [NSString localizedStringWithFormat:@"%3.2f", myNumber];
複製代碼
此方法會根據系統Local進行顯示。
日期時間本地化
2、日期和時間轉字符串
使用NSDateFormatter
表示NSDate
對象。推薦使用這個方法:localizedStringFromDate:dateStyle:timeStyle:
。
//14 Aug 2019 at 11:19
NSString *localizedDateTime = [NSDateFormatter localizedStringFromDate:[NSDate date] dateStyle:NSDateFormatterMediumStyle timeStyle:NSDateFormatterShortStyle];
複製代碼
下表展示了語言爲英語,區域是美國時的日期和時間格式:
下表展示了dateStyle
爲NSDateFormatterMediumStyle
,timeStyle
爲NSDateFormatterShortStyle
在不同語言和地區時的表現形式:
3、使用自定義日期和時間格式
//1.Create an NSDateFormatter object.
NSDateFormatter *dateFormatter = [NSDateFormatter new];
//2.get a localized format string from a template that you provide.
NSString *localeFormatString = [NSDateFormatter dateFormatFromTemplate:@"MMM d" options:0 locale:dateFormatter.locale];
//3.Set the format of the NSDateFormatter instance to the locale-sensitive format string.
dateFormatter.dateFormat = localeFormatString;
//4.Use the stringFromDate: method to get a localized string representation of the date.
NSString *localizedString = [dateFormatter stringFromDate:[NSDate date]];
複製代碼
在不同語言和區域下localizedString
對應的內容爲:
3、解析日期字符串
//1.Create a date formatter object.
NSDateFormatter *dateFormatter = [NSDateFormatter new];
//2.Set the formatter’s style to a preset style.
dateFormatter.dateStyle = NSDateFormatterMediumStyle;
//3.If the input string is not expected to contain a time, set the time style to none.
dateFormatter.timeStyle = NSDateFormatterNoStyle;
//4.Set the leniency to YES (enables the heuristics).
dateFormatter.lenient = YES;
//5.Convert the string to a date object.
NSDate *date = [dateFormatter dateFromString:inputString];
複製代碼
我們輸入的字符串是9/3/14
,dateStyle設爲NSDateFormatterShortStyle
,如果區域爲美國,我們得到的NSDate信息爲:2014-09-03 07:00:00 +0000
,如果區域爲德國,我們將得到2014-03-09 08:00:00 +0000
。
數字本地化
本地化設置會影響小數點符號,千分符,貨幣符等內容,比如數字1,234.56
在意大利應該表示爲1.234,56
,所以對於數字的格式化我們應該用NSNumberFormatter
處理。
注意:NSNumberFormatter不是現成安全的
1、將Number轉成本地化的字符串
可以使用NSNumberFormatter
的localizedStringFromNumber:numberStyle:
方式
NSString *localizedString = [NSNumberFormatter localizedStringFromNumber:myNumber numberStyle:NSNumberFormatterDecimalStyle];
複製代碼
以下是不同語言和區域關於數字的顯示效果,左側的style及numberStyle
:
//1.Create a number formatter object.
NSNumberFormatter *numberFormatter = [NSNumberFormatter new];
//2.Set the formatter’s style to a preset style.
numberFormatter.numberStyle = NSNumberFormatterDecimalStyle;
//3.Set the leniency to YES (enables the heuristics).
numberFormatter.lenient = YES;
//4.Convert the string to a number object.
NSNumber *number = [numberFormatter numberFromString:inputString];
複製代碼
3、通過NSCalendar計算日期 NSCalendar類封裝了日曆的所有區域差異和複雜性。說他具有複雜性是因爲在不同國家,一年之中的月份可能是12或者13,一月中的天數可能是5到31的任意值,每週第一天可能是週六,週日或者週一。可以看下錶
因此使用NSCalendar取這些值將會很方便。 獲取Calendar unit的方式爲//1.Create an NSDateComponents object.
NSDateComponents *components = [[NSCalendar currentCalendar]
components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit | NSEraCalendarUnit
fromDate:[NSDate date]];
//2.Access the values for day, month, year, and era.
NSInteger day = [components day];
NSInteger month = [components month];
NSInteger year = [components year];
NSInteger era = [components era];
複製代碼
4、監聽本地化信息或者時區修改
可以通過NSCurrentLocaleDidChangeNotification
監聽區域的改變
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(localeDidChange:) name:NSCurrentLocaleDidChangeNotification object:nil];
複製代碼
同樣的監聽時區變化可以通過NSSystemTimeZoneDidChangeNotification
。
從右到左語言的處理
創建從右到左語言的交互界面
支持從右到左向的語言,在約束層面應該使用Auto layout中的leading
和trailing
屬性,而不是right
和right
。可以通過以下對比看到區別
- 視頻控制和時間線指示器
- 圖片,除非他們傳達方向感,如箭頭
- 時鐘
- 樂譜
- 圖表(x軸和y軸總是在一致的方向)
獲取佈局的方向性
如果我們想獲取當前語言是否應該是從右到左項的語言可以通過以下方法:
//right-to-left language
if ([UIView userInterfaceLayoutDirectionForSemanticContentAttribute:view.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft) {
…
}
複製代碼
設置文本的對齊方式
在iOS中默認的文本對齊方式是“natural”,在OS X中默認方式是“left”。natural的含義就是會感覺語言的方向自動調整爲left或者right。
如果你想NSMutableParagraphStyle
對象的對齊方式爲自然的方向,可以:
[[(NSMutableParagraphStyle *)paraStyle setAlignment:NSNaturalTextAlignment];
複製代碼
對雙向文本的處理
雙向文本就是一段文本中及含有從右往左的文本還含有從左往右的文本。是不是感覺很詫異?因爲即使像阿拉伯和希伯來國家這些書寫習慣爲從右往左,但是對於數字和拉丁文是從左往右寫的。如果你使用的是標準控件,像Label,TextView,Textfiled他們會自動處理雙向文本內容。如果你是使用自定義控件,那這些問題就需要你手動處理。
向雙向文本添加Unicode標記
在某些特殊的時候,系統默認的行爲可能會導致一些不正確的結果,這時我們可以通過添加Unicode標記進行糾正。
例如,手機號在所有語言中都是從左往右讀的,如果一個需要本地化的字符串變量表示一個手機號,如果我們需要保證他是從左往右的順序展示,需要再字符串首部增加一個從左往右的嵌入字符(LRE):U+202A
,在字符尾部增加定向格式字符(PDF):U+202C
。
// Wrap the plus (+) prefix and phone number in left-to-right directional markers
NSString *phoneNumber = @"408-555-1212";
NSString *localizedPhoneNumber = [NSString stringWithFormat:@"\u202A%@\u202C", phoneNumber];
複製代碼
翻轉Cocoa Touch視圖
有些視圖是不應該翻轉的,在iOS9之後可以通過UIView的semanticContentAttribute
屬性手動指定視圖應該是從左到後還是從右往左的方式展示。
如果是想翻轉圖片可以通過UIImage
的imageFlippedForRightToLeftLayoutDirection
方法。
本地化測試
通過IB預覽測試本地化
這個功能只能在.storyboard和.xib文件實現。
選中preview之後我們可以通過其右下角的語言選項切換不同語言,然後我們可以實時觀察調換語言之後的效果。通過僞語言功能測試
通過Edit Schema > Run > Options
然後點開語言選項,除了各種系統支持語言外,翻到最下面可以看到這幾個選項。
2.Right-to-Left Pseudolanguage 將語言方向改成從右往左,也可以將語言改成阿拉伯文或者希伯來文。
3.Accented Pseudolanguage 帶重音符號。
4.Bounded String Pseudolanguage 帶邊界的字符串。
5.Right-to-Left Pseudolanguage With Right-to-Left Strings 同從右往左語言。