iOS項目的國際化

轉載自:http://www.jianshu.com/p/5ae148570b98

1.使用InfoPlist.string爲你的應用名、權限提醒等配置信息做國際化

大家都知道,我們使用Localizable.strings文件爲代碼中的字符串做國際化,但是實際上還有InfoPlist.strings。系統會自動識別InfoPlist.strings來對項目的一些配置信息做國際化,例如:

CFBundleDisplayName 應用名稱

NSHumanReadableCopyright CopyRight

NSCameraUsageDescription 相機權限開啓時候彈框的提示文字

NSContactsUsageDescription 通訊錄權限開啓時候彈框的提示文字

NSLocationWhenInUseUsageDescription 定位權限開啓時候彈框的提示文字

NSMicrophoneUsageDescription 麥克風權限開啓時候彈框的提示文字

NSPhotoLibraryUsageDescription 相冊權限開啓時候彈框的提示文字

NSRemindersUsageDescription    推送權限開啓時候彈框的提示文字

......

諸如以上這些配置,無法直接在InfoPlist中直接配置(要做國際化),所以使用InfoPlist.strings來配置。

使用的方法很簡單,新建一個InfoPlist.strings文件,並且在右側勾選對應的語言即可。

Tip:如果想要對應用名稱做國際化,需要在項目配置裏添加參數Application has localized display nameYES

2.關於Base Internationlization選項的正確理解以及誤區

開啓項目的國際化選項會有一個開關Use Base Internationlization,在Localizable.strings中也會有(Base)文件。一開始我很自以爲是地以爲Base的意思是如果項目並沒有對某個語言做國際化,那麼就使用Base語言,然而經過試驗,發現事實並非是這樣的,只是自己自作聰明罷了。下面看一下官方對Base Internationlization的解釋:

Base internationalization separates user-facing strings from .storyboard and .xib files. It relieves localizers of the need to modify .storyboard and .xib files in Interface Builder. Instead, an app has just one set of .storyboard and .xib files where strings are in the development language—the language that you used to create the .storyboard and .xib files. These .storyboard and .xib files are called the base internationalization. When you export localizations, the development language strings are the source that is translated into multiple languages. When you import localizations, Xcode generates language-specific strings files for each .storyboard and .xib file. The strings files don’t appear in the project navigator until you import localizations or add languages.
In Xcode 5 and later, base internationalization is enabled by default. If you have an older project, enable base internationalization before continuing, as described in Enabling Base Internationalization.

簡單的說,實際上Base Internationlization是爲xib和storyboard來服務的。

這也說明了一個問題,很多你原以爲的東西事實上並不是一回事,時間久了你會自然而然地認爲那是真理而不是當初的揣測,等真正暴露出爲難了就很難發現了。

那麼iOS系統中語言選取的原則究竟是什麼呢?

我舉個例子:假設我的應用使用的開發語言是英語,同時對法語、日語、阿拉伯語做了國際化。然而我的系統語言設置的是中文,那麼打開應用會顯示什麼語言呢? 答案是根據你係統的首選語言列表而定,在系統設置-語言設置的界面,有一欄列表叫做首選語言列表,假設你當前系統語言是中文,但是之前一次選擇過阿拉伯文爲系統語言,那麼此事打開你的應用,將會顯示阿拉伯文。

iOS對應用語言的判定遵循的原則是:

1 查看應用是否針對系統語言做國際化,如有,選擇系統語言

2.遍歷系統對首選語言,如果存在對首選語言有做國際化,選擇該首選語言

3.全部沒有對情況下使用development language

3.針對不同界面開發模式使用不同的國際化方案

純代碼手寫UI

沒什麼好說的,一個Localizable.strings解決所有問題。

使用NSLocalizedString(@"KEY", nil)的宏方法,大部分教程中並沒有提過這個方法的第二個參數是何含義,其實大部分情況下也不會用到,所以可以自己再定一個宏來除卻不必要的麻煩:

#define LOCAL_LANG(LangKey) NSLocalizedString(LangKey, nil)

當然這個第二個參數並非沒有用,Tip4來闡述這第二個參數的作用。

使用Storyboard

之前講Base Internationlization的時候就提過,它主要是爲Storyboard和xib文件服務的。開啓之後,在xib和storyboard右側有localize選項,相應的處理方法和strings文件一樣,選擇國際化後會生成對應的strings文件,Xcode會抽出xib和Storyboard中所用使用的字符,界面上顯示的內容會作爲basekeyvalue,修改其他語言的.strings文件就給Storyboard做了國際化。

使用Xib

Storyboard和Xib在做國際化中有一個很大的區別在於,一個Storyboard中可以包含多個視圖,多個視圖的字符串都可以集中在一個Storyboard.strings中,但是Xib往往是一個視圖。當然Xib也可以使用同樣的方法做國際化,但是如果一個項目中有30個xib,項目需要有8種語言,那就意味着需要維護30*8=240個strings文件,然而很多情況下每個文件裏只有少數幾個字符,這樣的維護成本太大了。

針對Xib的國際化,可以使用如下方法:

直接在xib中用Localization.strings中的鍵作爲值使用,然後對於需要國際化的view,統一調用如下方法:

+ (void)replaceTextWithLocalizedTextInSubviewsForView:(UIView*)targetView {
    for (UIView* view in targetView.subviews) {
        if (view.subviews.count > 0) {
            [self replaceTextWithLocalizedTextInSubviewsForView:view];
        }
        if ([view isKindOfClass:[UILabel class]]) {
            UILabel* label = (UILabel*)view;
            label.text = LOCAL_LANG(label.text);
            [label sizeToFit];
        }
        if ([view isKindOfClass:[UIButton class]]) {
            UIButton* button = (UIButton*)view;
            [button setTitle:LOCAL_LANG(button.titleLabel.text) forState:UIControlStateNormal];
        }
        if ([view isKindOfClass:[UITextField class]]) {
            UITextField* textField = (UITextField*)view;
            [textField setPlaceholder:LOCAL_LANG(textField.placeholder)];
        }
    }
}

這樣所有Xib中要翻譯的字符又都統一到了localizable.strings中了,然而這必然存在不少的弊端,譬如無法及時預覽。具體見Tip7

當然,開發者遇到這樣的困難蘋果公司不可能沒有想到,之前使用這樣略黑科技的方法雖然解決了問題,但是其實蘋果官方有更徹底的解決方案。具體見Tip5

4.使用genstrings幫助你生成Localizable.strings

genstrings是一個命令行工具,可以自動檢測國際化相關的宏,從而生成對應的Localizable.strings文件,對應的命令很簡單

find . -name *.m | xargs genstrings -o en.lproj

NSLocalizedString(@"KEY", nil)的後一個參數實際上就是服務於genstrings的,它的意義就是備註,使用genstrings工具可以按照第一個參數爲KEYVALUE,第二個參數爲Comment來生成對應的鍵值對

比如NSLocalizedString(@"Hello",@"This is a comment")就能生成

/ This is a comment /

"Hello" = "Hello";

具體的使用方法和效果可以參見視頻https://www.youtube.com/watch?v=1XxxmnDL_ls

關於代碼中字符串的國際化可以參考博客:字符串本地化,裏面有不錯好的實踐

5.國際化資源的導出和導入

沒錯!Xcode自帶國際化資源的導入和導出功能!

不信?

點開Editor瞧瞧!

有多少公司是直接把Localizable.strings文件拿出來給翻譯人員翻譯的?我司甚至爲了方便翻譯寫了個自動抓取放到網頁上填表的工具,但是實際上Xcode已經給我們做了很多。

使用Xcode導出的國際化資源是.xliff格式的文件,很多第三方的翻譯軟件都是支持這個格式的文件的。它能把所有需要國際化的.strings文件全部倒出成對應語言的.xliff文件,翻譯公司使用翻譯軟件生成其他語言的.xliff文件,然後開發者可以使用Xcode自帶的導入功能將其導入。

不止如此!導入的時候系統還會提供工具檢驗是否存在沒有翻譯的字段,檢查diff。



所以如果使用了Xcode自帶的導入導出工具,那麼之前提到的Xib情況下生成的.strings文件維護困難的問題也隨之解決了。

6.修改xcscheme來幫助你對特定對國家語言和環境做調試

不少同學可能會有這樣的體驗,假設你開發的默認語言是英文,但是想測試下中文環境下的情況,但是即使系統語言是中文,你使用開發證書編譯調試的時候依舊會顯示英文的界面,這是爲什麼呢?一張圖就能解釋這個問題:


調試的時候選擇Edit Scheme可以手動選擇需要調試的語言和地區環境,這樣針對特定語言做調試的時候你再也不需要手動修改本地語言環境了。

7.使用Pseudolocalizations提前發現界面適配問題(國際化適配測試)

之前提過,如果使用Tip3所提過的xib全局替換的黑科技會帶來一定的弊端,其中之一就是無法實時預覽。那麼就來講一講如何對不同的國家語言的界面做實時預覽。步驟如下

1.選擇對應的已經做了國際化的.xib或者.storyboard文件

2.View -> Assistant Editor -> Show Assistant Editor

3.點擊Assistant Editor的左上角按鈕點擊選擇最後一個選項preview

4.此時可以看到該界面文件的預覽圖,右下角會有語言選擇,同時會有一個選項:Double-Length Pseudo-Language檢驗如果是雙倍長度的

能否完整顯示

5.當然這個功能還可以預覽對不同尺寸屏幕的適配

8.保證你的UILabel和UITextField等完美適應不同語言的不同長度

  • 利用好AutoLayout讓你的UILabel根據內容自適應長度

  • 如果對UILabel或者UITextField有硬性的MaxWidth設定,達到了最大寬度但是無法顯示全,可以採用AutoShrink的方案。

針對UILabel可以在xib或者storyboard中設置AutoShrink屬性,該屬性的作用是如果Label內容顯示不下,使用何種策略來適應,

iOS提供了多種選項,包括按比例縮放、最小字體、減小字間距。當然在代碼中可以統一設置UILabel的adjustsFontSizeToFitWidth、

minimumScaleFactor、allowsDefaultTighteningForTruncation等屬性,這些屬性默認都是NO。

針對UITextField,也有一個Min Font Size的屬性可以調整

  • 配合Pseudolocalizations提早發現界面溢出的問題

  • 特別需要注意的地方可以提前跟翻譯人員說明,控制字符長度

9.讓你的應用適配不同的佈局方向

事先聲明,這是個大坑

獲取大部分開發者都不會遇到過佈局方向這個術語,提起這個我突然想問個問題:有沒有人有過疑問,在AutoLayoutleadingtrailingleftright到底有什麼區別?明明設置的時候表現是一模一樣的。

在Xcode中,leadingtrailing是被推薦的,最好的證明就是即使一般人永遠都不會在意他們的區別,拖出來的佈局永遠都不會出現leftright的字段,爲什麼呢?官方的解釋是這樣的:

When adding constraints, use the attributes leading and trailing for horizontal constraints.
For left-to-right languages, such as English, the attributes leading and trailing are
equivalent to left and right. For right-to-left language, such as Hebrew or Arabic, leading
and trailing are equivalent to right and left. The leading and trailing attributes are the
default values for horizontal constraints.

用中文講就是leadingtrailing會根據屏幕的佈局方向做不同的表現,在從左到右的佈局中,它的效果會和leftright一樣,在從右到左的佈局中(比如阿拉伯語、錫伯語)leading相當於righttrailing相當於left

想看看具體效果如何,可以把系統語言設爲阿拉伯文看看效果。

值得注意的是:iOS9在原先基礎上對從右到左的佈局做了更徹底的適配,這會是的使用iOS8的SDK編譯的包在iOS9阿拉伯環境下會有奇怪的表現,最簡單的解決方法就是使用iOS9的SDK重新編譯。

然而,在開發中,你會遇到各種各樣的情況,下面我列舉幾種容易遇到的問題:

1.不是所有的界面你都希望它要根據系統的屏幕方向做適配

舉個簡單的例子,如果你的主頁是一個內容寬度爲屏幕的2.5倍的scrollView,那麼在從右到左的的環境中,整個界面就完全亂了套了(你可以想象一下)。

最簡單的解決方案,如果這個界面你不希望它根據系統的佈局方向做適配,那麼你就把它的所有的leadingtrailing約束換成leftright,乾脆的做法就是使用源碼方式打開xib文件,做全局替換,對應的,很多界面還是根據系統自動適配方向對於當地人更友好,那就繼續使用leadingleft,所以需要靈活使用。

2.系統的界面適配並不會自動給你的UILabel和UITextField做Alignment的適配

當然,我不確定這個玩意兒能否可以全局設置,之前花了一點時間還是沒有找到最佳的方案。的確,坑爹的就是即使界面佈局左右顛倒了,但是界面元素中的內容缺沒有做更深層次的適配,這點很多時候不得不手動適配。所以你需要手動獲取當前系統語言,並做相應處理。可以使用AOP做全局的替換,也可以手動調用Category中的方法去做fix,具體我就不貼代碼了

3.參數的位置

假設一段字符串需要顯示:"Hello %@",name,然而在其他語言中可能是%@,balabalala,name,怎麼辦!?

一個處理方案就是在localizable.strings做參數話而不是直接在代碼中,這是一個好的習慣。當然在阿拉伯環境中還是會遇到一個坑爹的情況,因爲阿拉伯文是從右到左的閱讀順序,而中文、英文不是。舉個最坑爹的你無法相信的例子:

"一段阿拉伯文 %@",name" 和 "%@ 一段阿拉伯文",name"在界面上顯示的結果是一樣的,name永遠在最右邊,然而實際的要求就是name需要在最左邊。

後來爲了解決這個參數的問題,我使用了一個很黑很黑的方法:

- (void)fixArgumentPositionIfNeed {
    //如果不是阿拉伯文
    return;
    NSString *replacedStr = [NSString stringWithFormat:@"ل%@",self.text];
    NSMutableAttributedString *attributeReplacedStr = [[NSMutableAttributedString alloc] initWithString:replacedStr     attributes:nil];
    [attributeReplacedStr addAttributes:@{NSForegroundColorAttributeName:[UIColor clearColor]} range:NSMakeRange(0, 1)];
    [self setAttributedText:attributeReplacedStr];
}

思路就是在字符串的前面再加入一個阿拉伯文字符,然後再把它設爲隱藏。。。。。

10.手動獲取系統語言和地區環境

儘管iOS中很多國際化的問題都是直接項目適配並且系統幫忙處理的,但是還是會有不少情況需要手動獲取系統當前的語言環境,並針對當前語言環境做判斷做相應的處理。具體獲取語言的代碼如下:

+ (NSString *)systemLanguage {
    NSUserDefaults* defs = [NSUserDefaults standardUserDefaults];
    NSArray* languages = [defs objectForKey:@"AppleLanguages"];
    NSString* preferredLang = [languages objectAtIndex:0];
    return preferredLang;
}

其中languages是一個數組,數組中成員就是你的首選語言,其中第一個就是系統當前語言

不過不知大家是否還記得,iOS9出來後微博、微信等應用(這些應用都有一個共同的特點:可以在應用內手動設置系統語言)都出現了語言錯亂的問題,問題的根源就是iOS9系統中這個方法取出來的值變了。

在iOS9以前,如果當前語言版本是英文,那就是en,阿拉伯文就是ar,中文就是zh-Hans,然而在iOS9中,假設我當前的語言是簡體中文,當前的地區是埃及,取出來的值就是:zh-Hans-EG,沒錯,在iOS9新的API中,這個方法返回的參數會帶上地區的後綴。所以原先對語言的判斷就不能單單的isEquasToString了,起碼要使用hasPrefix方法或者做一下split

發佈了44 篇原創文章 · 獲贊 1 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章