WPF 多國語系 -- 使用 CSV 檔案

  • 84
  • 0

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 這種外部檔案來當成語系檔案來源,稍微改寫一下就可以把語系資料存放在資料庫中存取。本篇介紹的技巧很簡單,靠著想像力我們可以有更多的發揮。