[WPF][C#] 讓你的應用程式無國界~WPF多國語系實作系列之一 - 使用LocBaml.exe

  • 11885
  • 0
  • C#
  • 2013-07-15

多語系的實作在各種平台和應用程式間一直是個很重要的環節,當然對WPF來說也不例外,接下來的幾篇文章,就要跟大家分享在WPF中,實作出支援多國語系的幾種方法。
第一篇就由在MSDN上也查詢得到的方法-透過LocBaml.exe來實作出多國語系的WPF應用程式來打頭陣。

 

多語系的實作在各種平台和應用程式間一直是個很重要的環節,當然對WPF來說也不例外,接下來的幾篇文章,就要跟大家分享在WPF中,實作出支援多國語系的幾種方法。

第一篇就由在MSDN上也查詢得到的方法-透過LocBaml.exe來實作出多國語系的WPF應用程式來打頭陣。

 

Step 1:建立WPF應用程式

首先,請自行建立一個WPF專案,並且依自己的喜好在MainWindow中放一些和文字有關的控制項(例如TextBlock)吧~

為了方便大家練習,給大家看看我自己拉出來的介面長相:

image

而該介面原始Xaml如下:

MainWindow.xaml
<Window x:Class="Wpf_PureApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
		xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
		xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignWidth="800"
		d:DesignHeight="600" Title="MainWindow" Height="Auto" Width="Auto">
	<Grid>
		<Border BorderBrush="#FF646464" BorderThickness="2" HorizontalAlignment="Center" Height="300"
				VerticalAlignment="Center" Width="400" CornerRadius="10" Background="White">
			<Border.Effect>
				<DropShadowEffect Opacity="0.5" />
			</Border.Effect>
			<Grid>
				<Grid.ColumnDefinitions>
					<ColumnDefinition Width="125" />
					<ColumnDefinition Width="0.646*" />
				</Grid.ColumnDefinitions>
				<Grid.RowDefinitions>
					<RowDefinition Height="0.2*" />
					<RowDefinition Height="0.2*" />
					<RowDefinition Height="0.2*" />
					<RowDefinition Height="0.2*" />
					<RowDefinition Height="0.2*" />
				</Grid.RowDefinitions>
				<TextBlock x:Name="textBlock" HorizontalAlignment="Center" TextWrapping="Wrap"
						VerticalAlignment="Center" Grid.ColumnSpan="2" Text="WPF Multilingual Sample" FontSize="24"
						Foreground="#FF323232" />
				<TextBlock x:Name="textBlock3" TextWrapping="Wrap" Text="Languages:" HorizontalAlignment="Left"
						VerticalAlignment="Center" Grid.Row="1" Margin="20,0,0,0" FontSize="16" />
				<ComboBox x:Name="comboBox" d:LayoutOverrides="Height" Grid.Row="1" Grid.Column="1"
						VerticalAlignment="Center" Margin="10" FontSize="16" DisplayMemberPath="DisplayName" />
				<TextBlock x:Name="textBlock2" TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Center"
						Grid.Row="2" Margin="20,0,0,0" FontSize="16" Text="UserAccount:" />
				<TextBox x:Name="textBox" TextWrapping="Wrap" Grid.Column="1" Grid.Row="2" Margin="10" FontSize="16"
						VerticalAlignment="Center" />
				<TextBlock x:Name="textBlock1" TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Center"
						Grid.Row="3" Margin="20,0,0,0" FontSize="16" Text="Password:" />
				<TextBox x:Name="textBox1" TextWrapping="Wrap" Grid.Row="3" Grid.Column="1" Margin="10" FontSize="16"
						VerticalAlignment="Center" />
				<StackPanel Grid.Row="4" Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center"
						Orientation="Horizontal">
					<Button x:Name="button" Content="Ok" Width="75" Margin="10" FontSize="16" />
					<Button x:Name="button1" Content="Cancel" Width="75" Margin="10" FontSize="16" />
				</StackPanel>
			</Grid>
		</Border>
	</Grid>
</Window>

基本上到這邊應該都不會有什麼問題,大家可以執行看看自己寫的小範例,看看是不是能正常的運作,正常的話就可以繼續下一個步驟囉!!

 

Step 2:為專案檔加入UICulture屬性

使用筆記本或其他文字編輯工具,打開專案檔(以我的範例是C#建立的為例,副檔名為.csproj),並且在PropertyGroup區段中透過<UICulture>語系簡碼<UICulture>加入預設的語系屬性(例如我的預設語系想使用英文,則輸入en-US)。

image

儲存檔案後再次透過Visual Studio重新編譯專案,這時會發現在預設的輸出資料夾中出現了一個名為en-US的資料夾,而且裡面包含一個名稱為"專案名稱.resources.dll"的檔案。

image

 

Step 3:為控制項加上Uid屬性

接下來,我們要透過MSbuild.exe為我們Xaml檔中可能會因為語系切換而有所改變的控制項加上UID屬性,以便後續LocBaml擷取控制項的屬性使用。這時候,請打開開始功能表 -> 所有程式 -> Microsoft Visual Studio 2010 -> Visual Studio Tools -> 點選Visual Studio Command Prompt (2010)

image

接著請透過Dos指令,瀏覽到專案檔(.csproj)所在的資料夾,並輸入以下指令MSbuild /t:updateuid 專案名稱.csproj

image

執行無誤之後,請回頭看看我們的MainWindow.xaml檔,這時候會發現我們的使用者控制項中都被加上了一個x:Uid的屬性。

image

而這個屬性,就是之後要供LocBaml用來擷取使用者控制項屬性的重要依據喔!!在確認控制項都被加上Uid了之後,請再次重建專案

 

Step 4:透過LocBaml.exe擷取出控制項可能因為語系而改變的各項屬性

接下來就輪到主角出場了~歡迎我們的LocBaml.exe!!!(在MSDN上可以找到相容於.Net Framework 3.0的版本),而像我一樣使用.Net Framework 4.0的朋友們,前面那個連結下載的版本就不相容囉!!不過!!還好有好心人改好了可以相容於.Net Framework 4.0的LocBaml.exe,請各位自行前往

LocBaml for .NET 4.0下載。(這邊建議各位將下載回來的LocBaml.exe檔存放到專案的Bin\Debug資料夾下,以便後續的操作)

接著請一樣透過命令模式,切換到專案的Bin\Debug資料夾下,並輸入以下指令:LocBaml.exe /parse 預設語系名稱/專案名稱.resources.dll /out:輸出文字檔名稱.csv

image

執行無誤後,應該會發現資料夾中多了我們指定輸出的csv檔,可以使用一般的文字編輯工具或是Excel打開它來進行編輯。

如上例預設的輸出格式會是以逗號分隔的csv檔,裡面包含了七個欄位,如果不做其他的動作,我們只需要針對最後一個欄位(Value)來做修改(關於每個欄位的用途,可以參閱MSDN上的Use LocBaml to Parse a File)

仔細的看產出的檔案的話,會發現和語系相關的各種屬性(像是文字內容、長度、寬度甚至是圖片的Source...等等)都被抓了出來,可以讓我們很輕鬆的進行修改,我的範例就只簡單的針對文字內容的部份進行修改(小提醒,記得存檔的格式要相容於UTF-8喔!!)。

我修改後的csv檔內容如下:

WPF_PureApp.Resources.csv
Wpf_PureApp.g.en-US.resources:mainwindow.baml,Window_1:Wpf_PureApp.MainWindow.$Content,None,TRUE,TRUE,,#Grid_1;
Wpf_PureApp.g.en-US.resources:mainwindow.baml,Window_1:System.Windows.Window.Title,Title,TRUE,TRUE,,MainWindow
Wpf_PureApp.g.en-US.resources:mainwindow.baml,Window_1:System.Windows.FrameworkElement.Height,None,FALSE,TRUE,,Auto
Wpf_PureApp.g.en-US.resources:mainwindow.baml,Window_1:System.Windows.FrameworkElement.Width,None,FALSE,TRUE,,Auto
Wpf_PureApp.g.en-US.resources:mainwindow.baml,Border_1:System.Windows.Controls.Border.BorderBrush,None,FALSE,TRUE,,\#FF646464
Wpf_PureApp.g.en-US.resources:mainwindow.baml,Border_1:System.Windows.Controls.Border.BorderThickness,None,FALSE,TRUE,,2
Wpf_PureApp.g.en-US.resources:mainwindow.baml,Border_1:System.Windows.FrameworkElement.HorizontalAlignment,None,FALSE,TRUE,,Center
Wpf_PureApp.g.en-US.resources:mainwindow.baml,Border_1:System.Windows.FrameworkElement.Height,None,FALSE,TRUE,,300
Wpf_PureApp.g.en-US.resources:mainwindow.baml,Border_1:System.Windows.FrameworkElement.VerticalAlignment,None,FALSE,TRUE,,Center
Wpf_PureApp.g.en-US.resources:mainwindow.baml,Border_1:System.Windows.FrameworkElement.Width,None,FALSE,TRUE,,400
Wpf_PureApp.g.en-US.resources:mainwindow.baml,Border_1:System.Windows.Controls.Border.Background,None,FALSE,TRUE,,\#FFFFFFFF
Wpf_PureApp.g.en-US.resources:mainwindow.baml,DropShadowEffect_1:System.Windows.Media.Effects.DropShadowEffect.$Content,None,TRUE,TRUE,,
Wpf_PureApp.g.en-US.resources:mainwindow.baml,DropShadowEffect_1:System.Windows.Media.Effects.DropShadowEffect.Opacity,None,FALSE,TRUE,,0.5
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock:System.Windows.Controls.TextBlock.$Content,Text,TRUE,TRUE,,
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock:System.Windows.FrameworkElement.HorizontalAlignment,None,FALSE,TRUE,,Center
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock:System.Windows.Controls.TextBlock.TextWrapping,None,FALSE,TRUE,,Wrap
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock:System.Windows.FrameworkElement.VerticalAlignment,None,FALSE,TRUE,,Center
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock:System.Windows.Controls.Grid.ColumnSpan,None,FALSE,TRUE,,2
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock:System.Windows.Controls.TextBlock.Text,Text,TRUE,TRUE,,WPF Multilingual Sample
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock:System.Windows.Controls.TextBlock.FontSize,None,TRUE,TRUE,,24
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock:System.Windows.Controls.TextBlock.Foreground,None,FALSE,TRUE,,\#FF323232
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock3:System.Windows.Controls.TextBlock.$Content,Text,TRUE,TRUE,,
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock3:System.Windows.Controls.TextBlock.TextWrapping,None,FALSE,TRUE,,Wrap
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock3:System.Windows.Controls.TextBlock.Text,Text,TRUE,TRUE,,語系:
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock3:System.Windows.FrameworkElement.HorizontalAlignment,None,FALSE,TRUE,,Left
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock3:System.Windows.FrameworkElement.VerticalAlignment,None,FALSE,TRUE,,Center
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock3:System.Windows.Controls.Grid.Row,None,FALSE,TRUE,,1
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock3:System.Windows.FrameworkElement.Margin,None,FALSE,TRUE,,"20,0,0,0"
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock3:System.Windows.Controls.TextBlock.FontSize,None,TRUE,TRUE,,16
Wpf_PureApp.g.en-US.resources:mainwindow.baml,comboBox:System.Windows.Controls.ComboBox.$Content,ComboBox,TRUE,TRUE,,
Wpf_PureApp.g.en-US.resources:mainwindow.baml,comboBox:System.Windows.Controls.Grid.Row,None,FALSE,TRUE,,1
Wpf_PureApp.g.en-US.resources:mainwindow.baml,comboBox:System.Windows.Controls.Grid.Column,None,FALSE,TRUE,,1
Wpf_PureApp.g.en-US.resources:mainwindow.baml,comboBox:System.Windows.FrameworkElement.VerticalAlignment,None,FALSE,TRUE,,Center
Wpf_PureApp.g.en-US.resources:mainwindow.baml,comboBox:System.Windows.FrameworkElement.Margin,None,FALSE,TRUE,,10
Wpf_PureApp.g.en-US.resources:mainwindow.baml,comboBox:System.Windows.Controls.Control.FontSize,None,TRUE,TRUE,,16
Wpf_PureApp.g.en-US.resources:mainwindow.baml,comboBox:System.Windows.Controls.ItemsControl.DisplayMemberPath,ComboBox,TRUE,TRUE,,DisplayName
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock2:System.Windows.Controls.TextBlock.$Content,Text,TRUE,TRUE,,
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock2:System.Windows.Controls.TextBlock.TextWrapping,None,FALSE,TRUE,,Wrap
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock2:System.Windows.FrameworkElement.HorizontalAlignment,None,FALSE,TRUE,,Left
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock2:System.Windows.FrameworkElement.VerticalAlignment,None,FALSE,TRUE,,Center
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock2:System.Windows.Controls.Grid.Row,None,FALSE,TRUE,,2
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock2:System.Windows.FrameworkElement.Margin,None,FALSE,TRUE,,"20,0,0,0"
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock2:System.Windows.Controls.TextBlock.FontSize,None,TRUE,TRUE,,16
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock2:System.Windows.Controls.TextBlock.Text,Text,TRUE,TRUE,,帳號:
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBox:System.Windows.Controls.TextBox.$Content,Text,TRUE,TRUE,,
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBox:System.Windows.Controls.TextBox.TextWrapping,None,FALSE,TRUE,,Wrap
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBox:System.Windows.Controls.Grid.Column,None,FALSE,TRUE,,1
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBox:System.Windows.Controls.Grid.Row,None,FALSE,TRUE,,2
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBox:System.Windows.FrameworkElement.Margin,None,FALSE,TRUE,,10
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBox:System.Windows.Controls.Control.FontSize,None,TRUE,TRUE,,16
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBox:System.Windows.FrameworkElement.VerticalAlignment,None,FALSE,TRUE,,Center
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock1:System.Windows.Controls.TextBlock.$Content,Text,TRUE,TRUE,,
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock1:System.Windows.Controls.TextBlock.TextWrapping,None,FALSE,TRUE,,Wrap
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock1:System.Windows.FrameworkElement.HorizontalAlignment,None,FALSE,TRUE,,Left
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock1:System.Windows.FrameworkElement.VerticalAlignment,None,FALSE,TRUE,,Center
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock1:System.Windows.Controls.Grid.Row,None,FALSE,TRUE,,3
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock1:System.Windows.FrameworkElement.Margin,None,FALSE,TRUE,,"20,0,0,0"
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock1:System.Windows.Controls.TextBlock.FontSize,None,TRUE,TRUE,,16
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBlock1:System.Windows.Controls.TextBlock.Text,Text,TRUE,TRUE,,密碼:
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBox1:System.Windows.Controls.TextBox.$Content,Text,TRUE,TRUE,,
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBox1:System.Windows.Controls.TextBox.TextWrapping,None,FALSE,TRUE,,Wrap
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBox1:System.Windows.Controls.Grid.Row,None,FALSE,TRUE,,3
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBox1:System.Windows.Controls.Grid.Column,None,FALSE,TRUE,,1
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBox1:System.Windows.FrameworkElement.Margin,None,FALSE,TRUE,,10
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBox1:System.Windows.Controls.Control.FontSize,None,TRUE,TRUE,,16
Wpf_PureApp.g.en-US.resources:mainwindow.baml,textBox1:System.Windows.FrameworkElement.VerticalAlignment,None,FALSE,TRUE,,Center
Wpf_PureApp.g.en-US.resources:mainwindow.baml,StackPanel_1:System.Windows.Controls.Grid.Row,None,FALSE,TRUE,,4
Wpf_PureApp.g.en-US.resources:mainwindow.baml,StackPanel_1:System.Windows.Controls.Grid.ColumnSpan,None,FALSE,TRUE,,2
Wpf_PureApp.g.en-US.resources:mainwindow.baml,StackPanel_1:System.Windows.FrameworkElement.VerticalAlignment,None,FALSE,TRUE,,Center
Wpf_PureApp.g.en-US.resources:mainwindow.baml,StackPanel_1:System.Windows.FrameworkElement.HorizontalAlignment,None,FALSE,TRUE,,Center
Wpf_PureApp.g.en-US.resources:mainwindow.baml,StackPanel_1:System.Windows.Controls.StackPanel.Orientation,None,FALSE,TRUE,,Horizontal
Wpf_PureApp.g.en-US.resources:mainwindow.baml,button:System.Windows.Controls.Button.$Content,Button,TRUE,TRUE,,
Wpf_PureApp.g.en-US.resources:mainwindow.baml,button:System.Windows.Controls.ContentControl.Content,Button,TRUE,TRUE,,確定
Wpf_PureApp.g.en-US.resources:mainwindow.baml,button:System.Windows.FrameworkElement.Width,None,FALSE,TRUE,,75
Wpf_PureApp.g.en-US.resources:mainwindow.baml,button:System.Windows.FrameworkElement.Margin,None,FALSE,TRUE,,10
Wpf_PureApp.g.en-US.resources:mainwindow.baml,button:System.Windows.Controls.Control.FontSize,None,TRUE,TRUE,,16
Wpf_PureApp.g.en-US.resources:mainwindow.baml,button1:System.Windows.Controls.Button.$Content,Button,TRUE,TRUE,,
Wpf_PureApp.g.en-US.resources:mainwindow.baml,button1:System.Windows.Controls.ContentControl.Content,Button,TRUE,TRUE,,取消
Wpf_PureApp.g.en-US.resources:mainwindow.baml,button1:System.Windows.FrameworkElement.Width,None,FALSE,TRUE,,75
Wpf_PureApp.g.en-US.resources:mainwindow.baml,button1:System.Windows.FrameworkElement.Margin,None,FALSE,TRUE,,10
Wpf_PureApp.g.en-US.resources:mainwindow.baml,button1:System.Windows.Controls.Control.FontSize,None,TRUE,TRUE,,16

 

Step 4:再次透過LocBaml.exe產出其他語系的資源檔

修改完csv檔之後,我們就要透過LocBaml.exe來產出其他語系的資源檔啦~以我的例子來說,我將原先以英文顯示的部份改成了以正體中文來顯示,而其他的部份都是都沒去動到。

再來我們得手動建立新資源檔存放的資料夾,正體中文對應到的名稱為zh-TW,所以我得在專案的Bin\Debug下建立好一個名稱為zh-TW的資料夾(這個步驟很重要,千萬別漏掉喔~否則後面的動作都不會成功!!)。

接著一樣透過命令列模式進行輸入以下指令:LocBaml.exe /generate 預設語系資料夾名稱/專案名稱.resources.dll /trans:編輯後的文字檔名稱 /out:目標資料夾名稱 /cul:目標語系名稱

(請保持目標資料夾名稱請與語系名稱相同)

image

 

Step 5:再次執行專案,享受美好的成果吧!!

嘿嘿~輕鬆的就做到當地化啦!!!

image

 

附註:其實這個方法也直接實作完了本地化的部份,而且透過LocBaml來實作多語系可以讓Designer在設計階段不用花太多的時間在其他語系的設計上,而且可以在整個專案撰寫完畢之後,再透過LocBaml來擷取出資源檔,並且簡單的就能進行其他語系的新增,不過~~由於系統會自動依照使用者作業系統的語系決定要使用的資源檔,所以要進行其他語系的測試,可能就得進控制台去修改地區和語言的設定或是直接修改App.xaml.cs檔,在裡面直接設定Thread.CurrentThread.CurrentCulture 和Thread.CurrentThread.CurrentUICulture 才能比較方便的測試不同的語系喔!!

最後,奉上成品的專案範例原始碼,請自行服用: