淺談靈活的WPF程序多語言支持
<!--[if !supportLists]--> <!--[endif]-->
微軟的WPF程序多語言支持官方解決方案:使用Resource,并把Resource按語言編譯成獨(dú)立DLL,程序會(huì)根據(jù)系統(tǒng)當(dāng)前語言設(shè)置,自動(dòng)加載最合適的資源。(這種方法靈活性較差,而且不能滿足多樣的需求,于是網(wǎng)上各種多語言方案紛至沓來。)這里有一篇對(duì)官方方案的進(jìn)一步解釋。
使用XML保存語言文件:放進(jìn)來只是因?yàn)榫W(wǎng)上的確有這么個(gè)解釋方案,雖然沒有什么實(shí)用價(jià)值……,Resource本來就是XML,還用自己定義一個(gè)XML,還XMLDataProvider,還XML-based Data Binding,看著都累……
使用Project Resource的:和上面的類似,不過把字符串全放在Project Resource里,然后用ObjectDataProvider,然后也是使用Data Binding。
Assembly自帶語言:每個(gè)Assembly里放上支持的所有語言,使用配置文件設(shè)置軟件語言,比微軟的方案更進(jìn)一步,但是WPF程序多語言支持問題也還是存在的。
<!--[if !supportLists]--><!--[endif]--><!--[if !supportLists]--><!--[endif]--><!--[if !supportLists]--><!--[endif]-->
上面所有的方案都沒有同時(shí)解決下面這兩個(gè)問題:
<!--[if !supportLists]--> <!--[endif]-->
運(yùn)行時(shí)切換語言。
加入新語言,而不需要重新編譯軟件。
<!--[if !supportLists]--><!--[endif]-->
下面,就來介紹一種更靈活的,解決了上面兩個(gè)問題的WPF程序多語言支持方案。
基本方式還是使用Resource,只不過Resource是運(yùn)行時(shí)才加載進(jìn)來的。解決方案的結(jié)構(gòu)如下圖所示。
<!--[if !vml]-->
<!--[endif]-->
圖1. 解決方案的結(jié)構(gòu)
其中各個(gè)語言文件的資源文件放在Resources/Langs文件夾中,這些資源文件不會(huì)被編譯到Assembly中,編譯之后的文件結(jié)構(gòu)如下圖所示,語言文件被原樣復(fù)制到Output文件夾中。
<!--[if !vml]-->
<!--[endif]-->
圖2. 編譯后的文件結(jié)構(gòu)
先來看看程序的運(yùn)行效果,再來看代碼會(huì)比較直觀一些。
<!--[if !vml]-->
<!--[endif]-->
圖3. 英文界面
<!--[if !vml]-->
<!--[endif]-->
圖4. 漢語界面
下面就是這個(gè)界面的代碼。
- MainWindow
- <Window x:Class="Localization.DemoWindow"
所有的界面上的文字,都使用DynamicResource引用資源文件中的字符串。資源文件的格式如下(英文資源文件示例):
- <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
語言文件沒有編譯到Assembly中,使用起來就有些不太一樣。下面是App.xaml文件中設(shè)置Application的默認(rèn)加載語言的方式。
- <Application x:Class="Localization.App"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
前面的內(nèi)容基本上沒有什么和別的方案不一樣的地方,下面才是最重要的一點(diǎn),就是如何運(yùn)行時(shí)切換語言的呢?答案就是,只要把上面代碼里的ResourceDictionary替換掉就OK了,界面會(huì)自動(dòng)刷新。下面就是實(shí)現(xiàn)替換功能的代碼。
- public class LanguageHelper
- {
- /// <summary>
- ///
- /// < SPAN>summary>
- /// <param name="languagefileName">< SPAN>param>
- public static void LoadLanguageFile(string languagefileName)
- {
- Application.Current.Resources.MergedDictionaries[0] = new ResourceDictionary()
- {
- Source = new Uri(languagefileName, UriKind.RelativeOrAbsolute)
- };
- }
- }
參數(shù)languagefileName可以是文件的絕對(duì)路徑,如:C:\en-US.xaml或是和App.xaml里一樣的相對(duì)路徑。順便解釋一下,那個(gè)“pack://siteOfOrigin:,,,”無非就是當(dāng)前執(zhí)行程序的所在目錄。
以目前的測(cè)試結(jié)果來看,即使界面上有大量的細(xì)粒度文字。切換語言的速度也是一瞬間的事兒,如果慢,也是因?yàn)閤aml文件過大,讀文件用了不少時(shí)間。
WPF程序多語言支持缺陷
其實(shí)這才是最重要的,很多文章介紹一項(xiàng)技術(shù)的時(shí)候都會(huì)把這個(gè)技術(shù)夸得天花亂墜,卻對(duì)潛在的缺陷或問題避而不談。
缺陷就在于,不是所有的東西都是可以運(yùn)行是更新的。比如***一個(gè)菜單項(xiàng)是用Command實(shí)現(xiàn)的,如下代碼所示:
- <MenuItem Command="c:LanguageCommands.OpenLanguage"
- Header="{DynamicResource OpenLanguageFileMenuHeader}"/>
RoutedUICommand本身就已經(jīng)定義了Text屬性用來顯示在界面上,完全沒有必要為使用了這個(gè)Command的MenuItem設(shè)置Header屬性。但是這里為什么還是設(shè)置了呢?因?yàn)槟壳斑€沒有找到簡單的方案改變Command的Text后能自動(dòng)地更新界面。因?yàn)镃ommand的Text屬性不是一個(gè)Dependency Property。為了自動(dòng)更新界面,不得不為MenuItem設(shè)置Header屬性。
【編輯推薦】