VB6被很多程式員認為是一個過氣的開發工具,但它實在是微軟最經典的開發工具,沒有之一!編譯出的程式短小精悍,一般就幾十K,而且從XP以后的作業系統,均攜帶其運行時庫,只要程式只使用VB的標準控制元件,連安裝都不需要,可以直接運行。所以,不太復雜的GUI程式我都使用VB6開發,速度那怎叫一個快字了得!
最近,我做了一個Mini流圖軟體,但需要支持多國語言。查了一下網上的資料,方案感覺都很山寨。基本上都是頭疼醫頭,腳疼醫腳,沒有一個基于系統的全面調理方案。于是,仔細研究了VB6的資源檔案體系,發現原來微軟早就提供了完美的解決方案,只是中國的程式員大多不習慣這種貴族式的優雅。VC6也使用了這個機制,但GUI做得比VB差幾條街,一點兒也不直觀,智商低于120的程式員基本無緣此功能,但以后Visual Studio的VC一直繼承下來這個機制(雖然表達方式依舊那么爛),不像VB從.net開始拋棄了這一機制,使用了簡單粗暴的方法來解決。也許,簡單粗暴是這個社會的潮流,但我依然追求優雅。所以以下內容僅適用于對完美有變態追求的同學們。
VB6的資源檔案中一般包括字串、圖示、游標、位圖和用戶定義的位元組流,共5種型別的資源,每種資源的實體都有2個屬性,一個是資源ID,另一個就是語言ID。就是說,一個資源需要兩個ID共同標識。例如:一個資源ID為101的字串,其語言ID為1028的字串內容是臺灣中文,其語言ID為1033的字串內容是美式英文,其語言ID為1049的字串內容是俄文,其語言ID為2052的字串內容是中國中文,其語言ID為2057的字串內容是英式英文等等。如圖所示是VB6的字串資源編輯器,很直觀吧!開發VC的專案經理太自大!

當程式請求資源時,只需要輸入一個資源ID引數,運行時庫會根據系統語言ID自動回傳對應的資源。對于急功近利的同學們,這些基本就夠用了。因為專案經理說做8國語言,大部分程式員不會考慮第9國人民的感受。后面將分享VB6資源檔案體系的讀取策略以便能優雅的解決第9國人民的問題。
在這之前,得先普及一下Windows內核的多國語言知識。微軟從NT開始使用Unicode內核,其好處就是大大降低全球部署的成本,這是微軟稱霸全球的重要技術支持手段之一。對使用者而言,最直觀的好處就是在輸出的一個字串里面可以同時包含多個國家的文字。雖然這種需求很少見,但切換語言這種事在Unicode技術的支持下,那就都不是事!
雖然Windows在發行時,都指明了語言版本,但實際上,任何一個語言的Windows都支持任意微軟支持的語言。所以在語言上,Windows有兩種級別的語言,一種是安裝級(Installed),即系統(System)語言;另一種是支持級(Supported),即用戶(User)語言。
一般情況下,系統語言和用戶語言是一樣的。那二般是個什么情況?舉個例子,在一個國際學校,老師是美國人,他當然安裝英文版Windows,但他的學生分別是法國人、德國人、中國人、韓國人,就是沒有美國人!為了考慮學生的愛國情懷,老師給他們每個人分配一個登錄賬號,登錄后,可以修改控制面板中的地區和語言,以便符合每個學生的習慣。這樣,在每個學生的登錄會話環境中,系統語言都是英語,用戶語言是用戶在控制面板中設定的語言。
OK,現在來看看VB6的運行時庫對資源請求是如何動作的。當程式請求資源時,只需要輸入一個資源ID引數,運行時庫首先使用用戶語言ID與請求的資源ID所擁有的所有語言ID進行匹配,如果成功,就把請求的資源ID和匹配成功的語言ID對應的資源回傳;如果不成功,再使用系統語言ID與請求的資源ID所擁有的所有語言ID進行匹配,如果成功,就把請求的資源ID和匹配成功的語言ID對應的資源回傳;如果還不成功,則把請求的資源ID所擁有的所有語言ID中ID值最小的那個對應的資源回傳。
看那個例子,為了方便講解,我加了一個資源ID是99的字串,里面的內容是語言ID值。如果登錄會話環境的用戶語言和系統語言都沒有匹配上,則回傳中文(臺灣)的字串資源,因為1028是所有提供的語言ID中最小的!
說到這里,大家可能很快想到,ID值最小的一定是0,那0是怎么定義的呢?那我們得先看看微軟是如何定義語言ID的:
#define MAKELANGID(PrimaryLanguage, SubLanguage) ((((WORD) (SubLanguage)) << 10) | (WORD) (PrimaryLanguage))
意思是語言ID由1個16位的字表示(最多支持65536種語言),其中低10位為主語言ID(最多支持1024種主語言),高6位為子語言ID(每個主語言最多有64種子語言)。
主語言和子語言策略是表達以下情況:如西班牙語,世界上母語是西班牙語的國家有20多個,比英語還多,這個要問那位15世紀才華橫溢、精力充沛的伊莎貝拉女王,是她指使哥倫布同學全球殖民而導致我們現在的問題。這20多個國家的西班牙文叫子語言,但他們都屬于西班牙語區,這個區稱之為主語言。
主語言ID為0被微軟稱之為中性語言(Neutral),它居然也有子語言,分別是SUBLANG_NEUTRAL(0)、SUBLANG_DEFAULT(1)和SUBLANG_SYS_DEFAULT(2),分別對應3種中性語言Neutral、Neutral Default和Neutral System Default。
所以,前面講的是個家庭版,現在看看微軟的運行時庫對資源請求動作的專業版。當程式請求資源時,只需要輸入一個資源ID引數,運行時庫首先使用用戶語言ID與請求的資源ID所擁有的所有語言ID進行匹配,如果成功,就把請求的資源ID和匹配成功的語言ID對應的資源回傳;如果不成功,則回傳主語言ID為0且子語言ID也為0的資源;如果沒找到,再使用系統語言ID與請求的資源ID所擁有的所有語言ID進行匹配,如果成功,就把請求的資源ID和匹配成功的語言ID對應的資源回傳;如果還不成功,則把請求的資源ID所擁有的所有語言ID中ID值最小的那個對應的資源回傳。
所以,使用VB6多國語言資源檔案的最佳實踐是:根據軟體主要投放語言區域,把那個語言設定為Neutral Default或Neutral System Default(這兩個效果一樣,如果設定為Neutral,則系統語言將失去一次匹配的機會)。例如:軟體準備投放歐洲市場,專案只準備了英、法、德、意4國語言,你可以把英語設定為Neutral Default。當用戶語言和系統語言都不是法、德、意時,均使用英語;如果不這么配置,就會使用語言ID最小的那個語言(德語)。顯然,在歐洲,認識英文的人應該比認識德文的人多,這樣的用戶體驗更好。
當然,最好的方案還是提供所有語言!其實,也不是很難!關鍵是工欲善其事,必先利其器!
2001年,有個叫Phil Jollans(http://www.jollans.com)的德國小伙子做了一個叫做World Resource String Editor的VB6插件,可以直接從excel中匯入字串資源!有圖有真相:

我就是用這個工具,只需要填寫中國(中文)那一列的內容,把檔案分發給各個翻譯組,再匯總匯入,作業之輕松令人發指!什么?西班牙語有20個國家?來吧,我把西班牙(西班牙)那一列復制20列,再把子語言ID一換,輕松支持所有西班牙地區的國家。羽扇綸巾,檣櫓灰飛煙滅的感覺!滿足需求都弱爆了,預測需求才能立于不敗之地。
但預測需求也只是個中級水平,只有引領需求才是最高境界!那什么才是最高境界呢?說到這里,上面已經解決了80%的問題,但只花費了20%的功力,下面的內容解決剩下20%的問題,但需要80%的功力。
其實,微軟的整個策略還是對需求了解得不夠充分,略顯簡單粗暴。例如:比利時、瑞士等幾個歐洲小國也說法語,它對法國的標準法語進行了一些擴展,就像方言一樣,懂比利時法語的人肯定也懂法國的標準法語,這是個非常自然的需求。所以,資源提取的完整最佳策略應該是這樣的:
當程式請求資源時,只需要輸入一個資源ID引數,運行時庫首先使用用戶語言ID與請求的資源ID所擁有的所有語言ID進行匹配,如果成功,就把請求的資源ID和匹配成功的語言ID對應的資源回傳;如果不成功,就把用戶語言ID的主語言ID與請求的資源ID所擁有的所有語言ID的主語言ID進行匹配,如果成功,就把請求的資源ID和匹配成功的主語言ID里面子語言ID最小的那個語言ID對應的資源回傳;如果不成功,則回傳主語言ID為0且子語言ID也為0的資源;如果沒找到,再使用系統語言ID與請求的資源ID所擁有的所有語言ID進行匹配,如果成功,就把請求的資源ID和匹配成功的語言ID對應的資源回傳;如果不成功,就把系統語言ID的主語言ID與請求的資源ID所擁有的所有語言ID的主語言ID進行匹配,如果成功,就把請求的資源ID和匹配成功的主語言ID里面子語言ID最小的那個語言ID對應的資源回傳;如果還不成功,則把請求的資源ID所擁有的所有語言ID中ID值最小的那個對應的資源回傳。我已經竭盡畢生才華企圖讓阿甘都能看懂了,如果還看不懂,就洗洗睡吧,不求甚解才是真正的幸福。
好吧,終極對決如期而至。這么屌暴天的需求怎么優雅地實作呢?翻閱MSDN上所有關于多國語言資源方面的資料,發現一套修改資源的API:BeginUpdateResource, UpdateResource和EndUpdateResource,可以修改資源的語言ID,但還是太復雜,嚴重損壞VB6的簡單美形象。換一個思路,既然改資源那么累,能不能改用戶語言呢?又發現了這么一個函式SetLocaleInfo,可以設定用戶語言。哈哈,這樣的話,大體思路就出來了:程式啟動后,首先呼叫一個調整函式:這個函式首先獲取用戶語言,與已有的資源語言比對,如果有完全匹配的,就可以退出了;如果沒有完全匹配的,再使用用戶語言的主語言與已有的資源語言的主語言比對,如果有匹配的,就用SetLocaleInfo把用戶語言設定為匹配了主語言的那個語言ID。這樣,資源里只需提供一種主語言的標準語言即可支持使用這個主語言的所有國家。系統語言的修改非常麻煩,要呼叫一系列API,最致命的是需要重啟作業系統,這個基本上就放棄了。但也無大礙,因為用戶語言才是最重要的依據,匹配到系統語言上多少有點掩耳盜鈴的感覺。
但是,SetLocaleInfo可是真真切切地修改了用戶語言,即使你退出前再把原來的用戶語言改回去,也會影響到其它在你之后啟動的應用,更何況你的程式有可能崩潰,連改回去的機會都沒有。
再經過仔細搜索,又發現這么一個函式SetThreadLocale,引數更簡單,最重要的是其修改的語言資訊只在本執行緒內起作用,并不真正地修改用戶語言。這下大家滿足了吧!
有意見,發郵件[email protected],標題注明圖乃大。
uj5u.com熱心網友回復:
uj5u.com熱心網友回復:
牛!向樓主學習這種學習的精神。uj5u.com熱心網友回復:
Mark .......學習一下。
uj5u.com熱心網友回復:
真的沒用過。也沒看到過這個功能,樓主用心了。頂一下樓主。uj5u.com熱心網友回復:
看了下,是用資源實作的。其實默認的vb應用程式向導就支持這個方式,只要勾選其中的多國語言就可以生成這么個框架。uj5u.com熱心網友回復:
百度翻譯API呼叫一下,連打字都省了uj5u.com熱心網友回復:
LOOK LOOKuj5u.com熱心網友回復:
包子混起,哈哈uj5u.com熱心網友回復:
mark!mark!uj5u.com熱心網友回復:
look lookuj5u.com熱心網友回復:
厲害厲害厲害uj5u.com熱心網友回復:
留個腳印,學習uj5u.com熱心網友回復:
厲害厲害厲害轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/72163.html
標籤:資源
