1. 程式人生 > >淺談靈活的WPF程式多語言支援

淺談靈活的WPF程式多語言支援

<!--[if !supportLists]--> <!--[endif]-->

微軟的WPF程式多語言支援官方解決方案:使用Resource,並把Resource按語言編譯成獨立DLL,程式會根據系統當前語言設定,自動載入最合適的資源。(這種方法靈活性較差,而且不能滿足多樣的需求,於是網上各種多語言方案紛至沓來。)這裡有一篇對官方方案的進一步解釋。

使用XML儲存語言檔案:放進來只是因為網上的確有這麼個解釋方案,雖然沒有什麼實用價值……,Resource本來就是XML,還用自己定義一個XML,還XMLDataProvider,還XML-based Data Binding,看著都累……
使用Project Resource的:和上面的類似,不過把字串全放在Project Resource裡,然後用ObjectDataProvider,然後也是使用Data Binding。

Assembly自帶語言:每個Assembly裡放上支援的所有語言,使用配置檔案設定軟體語言,比微軟的方案更進一步,但是WPF程式多語言支援問題也還是存在的。

<!--[if !supportLists]--><!--[endif]--><!--[if !supportLists]--><!--[endif]--><!--[if !supportLists]--><!--[endif]-->

上面所有的方案都沒有同時解決下面這兩個問題:

<!--[if !supportLists]--> <!--[endif]-->

執行時切換語言。

加入新語言,而不需要重新編譯軟體。

<!--[if !supportLists]--><!--[endif]-->
下面,就來介紹一種更靈活的,解決了上面兩個問題的WPF程式多語言支援方案。

基本方式還是使用Resource,只不過Resource是執行時才載入進來的。解決方案的結構如下圖所示。

<!--[if !vml]-->

解決方案的結構

<!--[endif]-->

圖1. 解決方案的結構

其中各個語言檔案的資原始檔放在Resources/Langs資料夾中,這些資原始檔不會被編譯到Assembly中,編譯之後的檔案結構如下圖所示,語言檔案被原樣複製到Output資料夾中。

<!--[if !vml]-->

編譯後的檔案結構

<!--[endif]-->

圖2. 編譯後的檔案結構

先來看看程式的執行效果,再來看程式碼會比較直觀一些。

<!--[if !vml]-->

英文介面

<!--[endif]-->

圖3. 英文介面

<!--[if !vml]-->

漢語介面

<!--[endif]-->

圖4. 漢語介面

下面就是這個介面的程式碼。

  1. MainWindow
  2. <Window x:Class="Localization.DemoWindow"
  1. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  2. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  3. xmlns:c="clr-namespace:Localization.Backend.Commands"
  4. Title="{DynamicResource MainWindowTitle}"
  5. Width="230" Height="150">
  6. <DockPanel LastChildFill="False">
  7. <Menu DockPanel.Dock="Top">
  8. <Menu.CommandBindings>
  9. <x:Static Member="c:LanguageCommands.OpenLanguageBinding"/>
  10. Menu.CommandBindings>
  11. <MenuItem Header="{DynamicResource LanguageMenuHeader}">
  12. <MenuItem Header="{DynamicResource EnglishMenuHeader}"
  13. Click="OnLoadEnglishClick"/>
  14. <MenuItem Header="{DynamicResource ChineseMenuHeader}"
  15. Click="OnLoadChineseClick" />
  16. <Separator/>
  17. <MenuItem Command="c:LanguageCommands.OpenLanguage"
  18. Header="{DynamicResource OpenLanguageFileMenuHeader}"/>
  19. MenuItem>
  20. Menu>
  21. DockPanel>
  22. Window>

所有的介面上的文字,都使用DynamicResource引用資原始檔中的字串。資原始檔的格式如下(英文資原始檔示例):

  1. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  2. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  1. xmlns:s="clr-namespace:System;assembly=mscorlib">
  2. <s:String x:Key="MainWindowTitle">Localization Demos:String>
  3. <s:String x:Key="LanguageMenuHeader">_Languages:String>
  4. <s:String x:Key="EnglishMenuHeader">_Englishs:String>
  5. <s:String x:Key="ChineseMenuHeader">漢語(_C)s:String>
  6. <s:String x:Key="OpenLanguageFileMenuHeader">_Open Language Files:String>
  7. ResourceDictionary>

語言檔案沒有編譯到Assembly中,使用起來就有些不太一樣。下面是App.xaml檔案中設定Application的預設載入語言的方式。

  1. <Application x:Class="Localization.App"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  1. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  2. StartupUri="UI\DemoWindow.xaml">
  3. <Application.Resources>
  4. <ResourceDictionary>
  5. <ResourceDictionary.MergedDictionaries>
  6. <ResourceDictionary Source="pack://siteOfOrigin:,,,/Resources/Langs/en-US.xaml"/>
  7. ResourceDictionary.MergedDictionaries>
  8. ResourceDictionary>
  9. Application.Resources>
  10. Application>

前面的內容基本上沒有什麼和別的方案不一樣的地方,下面才是最重要的一點,就是如何執行時切換語言的呢?答案就是,只要把上面程式碼裡的ResourceDictionary替換掉就OK了,介面會自動重新整理。下面就是實現替換功能的程式碼。  

  1. public class LanguageHelper
  2. {
  3. /// <summary>
  4. ///
  5. /// summary>
  6. /// <param name="languagefileName">param>
  7. public static void LoadLanguageFile(string languagefileName)
  8. {
  9. Application.Current.Resources.MergedDictionaries[0] = new ResourceDictionary()
  10. {
  11. Source = new Uri(languagefileName, UriKind.RelativeOrAbsolute)
  12. };
  13. }
  14. }

引數languagefileName可以是檔案的絕對路徑,如:C:\en-US.xaml或是和App.xaml裡一樣的相對路徑。順便解釋一下,那個“pack://siteOfOrigin:,,,”無非就是當前執行程式的所在目錄。

以目前的測試結果來看,即使介面上有大量的細粒度文字。切換語言的速度也是一瞬間的事兒,如果慢,也是因為xaml檔案過大,讀檔案用了不少時間。

WPF程式多語言支援缺陷

其實這才是最重要的,很多文章介紹一項技術的時候都會把這個技術誇得天花亂墜,卻對潛在的缺陷或問題避而不談。

缺陷就在於,不是所有的東西都是可以執行是更新的。比如最後一個選單項是用Command實現的,如下程式碼所示:

  1. <MenuItem Command="c:LanguageCommands.OpenLanguage"
  2. Header="{DynamicResource OpenLanguageFileMenuHeader}"/>

RoutedUICommand本身就已經定義了Text屬性用來顯示在介面上,完全沒有必要為使用了這個Command的MenuItem設定Header屬性。但是這裡為什麼還是設定了呢?因為目前還沒有找到簡單的方案改變Command的Text後能自動地更新介面。因為Command的Text屬性不是一個Dependency Property。為了自動更新介面,不得不為MenuItem設定Header屬性。