WPF 多國語系有很多種作法,這一篇來介紹如何使用 CSV 檔案來作為多國語系來源
使用機制說明
這次的範例主要利用 DynamicResource 的特性來達成。寫 WPF 的各位應該都知道 DynamicResource 會監控資源變化並反應改變。
這是從 DynamicResource 的基本原理出發而發展出的解決方案。做法很簡單,就是把 CSV 檔案灌進 ResourceDictionary 裡面就能夠達到要求的效果。
工具類別
不囉嗦,直接來寫一個讀取檔案並且設定給 ResourceDictionary 的工具類別:
internal class LanguageProcess
{
private const string DefaultFileName = "Default.txt";
public static ResourceDictionary Resources { get; private set; } = new ResourceDictionary();
public static void SetLanguage(string path)
{
if (!File.Exists(path))
{
path = DefaultFileName;
}
foreach (var item in File.ReadLines(path).Select(line => line.Split(',')))
{
Resources[item[0]] = item[1];
}
}
}
程式碼相當的簡單,先設定一個常數當成預設語言的文字檔來源,外部呼叫 SetLanguage 方法傳入語言內容檔案的路徑。程式碼內剖析文字檔並在 Resources 設定 key-value pair,這邊用到了一個 Dictionary Indexer setter 的特性,可以參考前面的文章 利用Dictionary的特性處理尾端資料符合問題。
應用方式
為了簡單起見,我建立了一個列舉型別:
public enum LanguageCode
{
Default, zhtw
}
接著在 App class 撰寫程式碼:
public partial class App : Application
{
public static LanguageCode CurrentLanguage { get; private set; }
public static void SetCurrentLanguage(LanguageCode code)
{
if (code == LanguageCode.Default)
{
LanguageProcess.SetLanguage("Default.txt");
CurrentLanguage = LanguageCode.Default;
}
else if (code == LanguageCode.zhtw)
{
LanguageProcess.SetLanguage("zh-tw.txt");
CurrentLanguage = LanguageCode.zhtw;
}
}
public App()
{
SetCurrentLanguage(LanguageCode.Default);
this.Resources.MergedDictionaries.Add(LanguageProcess.Resources);
}
}
以 SetCurrentLanguage 方法當作一個呼叫前述工具類別的門戶,在 App 建構式中呼叫此門戶,並將其 Resources 合併到 App 本身的 Recources 當中。
至於 view 的部分就簡單了,我們只要設定屬性使用 DynamicResource 繫結即可:
<Window x:Class="WpfMultilanguageByCsvSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfMultilanguageByCsvSample"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel Orientation="Horizontal" >
<StackPanel.Resources >
<Style TargetType="Button" >
<Setter Property="Width" Value="120"/>
<Setter Property="Height" Value="36"/>
<Setter Property="Margin" Value="12"/>
</Style>
</StackPanel.Resources>
<Button Content="{DynamicResource Add}" />
<Button Content="{DynamicResource Delete}"/>
<Button Content="{DynamicResource ChangeLanguage}" Click="Button_Click"/>
</StackPanel>
</Window>
在 MainWindow 的後置程式碼加入 Button_Click 方法測試變換的效果:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (App.CurrentLanguage == LanguageCode.Default)
{
App.SetCurrentLanguage(LanguageCode.zhtw);
return;
}
App.SetCurrentLanguage(LanguageCode.Default);
}
}
如此就大功告成了。完整範例請在此下載 (txt 檔案放在 bin\debug 目錄下)。
後記
這篇文章主要在介紹從外部檔案讀取資料設定到 ResourceDictionary 的機制,在 SetCurrentLanguage 內部沒有用上其他太多的技巧,在正式專案由於語系可能相當多,所以當實作這個工具類別的時候,需要一些技巧來管理這些語系與檔案路徑的對應,例如像是使用 Dictionary 或 Attribute 等等的技巧。
另外一個說明的是,既然都可以用 CSV 這種外部檔案來當成語系檔案來源,稍微改寫一下就可以把語系資料存放在資料庫中存取。本篇介紹的技巧很簡單,靠著想像力我們可以有更多的發揮。