怎樣實現wpf prism module的國際化和本地化?

怎樣實現wpf prism module的國際化和本地化?

上一篇有簡單居間主工程的國際化,使用的資源字典(xaml)實現的。這幾天我添加了幾個prism模塊(module),發現子模塊使用資源字典的方式實現國際化和本地化不好做,沒有找到比較好的參考文章,所以換了一種方式,使用資源文件實現了。

最后更新 2020/4/21 下午1:45
沙漠尽头的狼
预计阅读 7 分钟
分类
WPF
专题
wpf mvvm框架 prism系列
标签
.NET WPF Prism 國際化 本地化

上一篇有簡單居間主工程的國際化,使用的資源字典(xaml)實現的。這幾天我添加了幾個 prism 模塊(module),發現子模塊使用資源字典的方式實現國際化和本地化不好做,沒有找到比較好的參考文章,所以換了一種方式,使用資源文件實現了。

一.本文概述

子模塊的國際化和本地化要求:

  1. 各模塊需要有自己單獨的語言文件。
  2. 在主工程中動態切換語言時,子模塊也需要跟著切換。
  3. 使用了 prism 實現模塊化框架,即要求主工程與各子模塊不能有引用關係,即鬆耦合,不能直接在主工程中切換子模塊的語言文件。

基於上面的要求,我嘗試在各模塊(module)中也定義了語言文件(xaml),主窗體切換語言時,加載模塊語言文件老是提示不存在對應的資源字典文件,我惱火呀,後面還是參考“accelerider.windows”國際化的方式,使用資源文件實現本地化和國際化了,不糾結 xaml 的方式了,唉。

下面是修改後的效果:

和上一版異同:

  1. 標題欄國際化無變化,只是文字綁定換了種方式,實現效果一致。

  2. 左側添加了三個子模塊(home\client\server),使用 prism 動態加載的,並且跟隨主工程主窗體語言切換而切換語言。

下面簡單居間怎麼創建模塊,以及主窗體和模塊國際化怎麼做的,真的是很簡單的居間,具體的實現可以拉代碼看看。

二.添加三個 prism 模塊(module)

可安裝 prism 模板,快速創建模塊工程,當然手工創建.net core 工程也是可以的,就是多了幾個步驟而已(需要用 nuget 安裝 prism.wpf 包(7.2.0.1422)),我使用得的 prism 模板快速創建的。

2.1創建模塊之前的準備工作

下載上圖搜索到的 prism 模板,重啟 vs,它會自動安裝,新建項目時,就有 prism 模塊模板選擇了:

注意要選擇.net core 3 的版本,因為我是使用.net core 創建的 wpf 項目。

2.2創建模塊

下面是已經創建好的三個模塊工程截圖:

目前三個模塊文件組織結構類似:

  • i18nresources:資源文件夾,放 3 個語言資源文件和一個 t4 模板文件(用於引用語言 key),其中 t4 模板文件在 3 個模塊和主工程中定義是一樣的,具體可從 github 下載源碼查看。
  • views 放置視圖文件,現在只使用到了主工程主窗體中顯示的 tabitem 視圖,即 maintabitem.xaml,繼承自 tabitem。
  • xxxxmodule.cs:prism 模板定義文件,prism 發現模塊使用。

三個模塊關鍵點需要注意:

  1. 編輯模塊工程文件,修改模塊文件輸出目錄:
// 省略部分代码,下面这一行设置为False,代表输出目录不带.NET Core版本信息
<AppendTargetFrameworkToOutputPath>Flase</AppendTargetFrameworkToOutputPath>
// 省略部分代码,修改Debug与Release编译输出目录,方便主工程统一加载模块
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
  <OutputPath>..\Build\Debug\Modules</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
  <OutputPath>..\Build\Release\Modules</OutputPath>
</PropertyGroup>
// 省略部分代码
  1. xxxmodule 中需要將資源文件的 resourcemanager 引用添加到另一個庫中保存,待切換語言時需要使用,如在 homemodule 的構造函數中添加代碼如下,只添加這一句代碼就好,模塊的國際化及本地化就算完事了:
I18nManager.Instance.Add(TerminalMACS.Home.I18nResources.UiResource.ResourceManager);
  1. xxxmodule 的 registertypes 方法中註冊視圖"maintabitem"到"regionnames.maintabregion",主窗體使用"regionnames.maintabregion"關聯模塊視圖顯示加載。
_regionManager.RegisterViewWithRegion(RegionNames.MainTabRegion, typeof(MainTabItem));
  1. ui 控制項國際化文字綁定,其中 markup 使用的一個開源庫命名空間,後面會給出連結,本項目直接將該庫加載進了解決方案中;i18nresources:language 即 t4 模板文件生成的類,關聯文字翻譯的 key。綁定文字部分代碼如下:
<TextBlock Grid.Row="2" Text="{markup:I18n {x:Static i18NResources:Language.MainTabItm_Header}}"

三.主工程

主工程目錄組織結構如下:

3.1動態加載 prism 模塊

配置加載 3 個模塊的關鍵代碼在 app.xaml.cs 文件中,看上面的代碼,我將三個模塊輸出到了 modules 目錄下,主工程直接加載此目錄即可,其他加載方式還有使用配置文件等,可以參考 prism 官方例子,文末給出連結:

protected override IModuleCatalog CreateModuleCatalog()
{
    string modulePath = @".\Modules";
    if (!Directory.Exists(modulePath))
    {
        Directory.CreateDirectory(modulePath);
    }
    return new DirectoryModuleCatalog() { ModulePath = modulePath };
}

主窗體顯示子模塊註冊的 tabitem 視圖,prism:regionmanager.regionname 即在各子模塊中註冊過的區域字符串,他與模塊對應的 tabitem 視圖關聯,代碼如下:

<TabControl Grid.ColumnSpan="2" SelectedIndex="0"
    Style="{StaticResource MainTabControlStyle}"
    ItemContainerStyle="{StaticResource MainTabItemStyle}"
    prism:RegionManager.RegionName="{x:Static ui:RegionNames.MainTabRegion}"/>

主窗體以 tabcontrol 的控制項形式展示子模塊視圖:

子模块的TabItem视图

主工程要能正常加載子模塊,主工程的工程文件也需要修改其輸出目錄:

// 省略部分代码,下面这一行设置为False,代表输出目录不带.NET Core版本信息
<AppendTargetFrameworkToOutputPath>Flase</AppendTargetFrameworkToOutputPath>
// 省略部分代码,修改Debug与Release编译输出目录,方便主工程统一加载模块
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
  <OutputPath>..\Build\Debug</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
  <OutputPath>..\Build\Release</OutputPath>
</PropertyGroup>
// 省略部分代码

3.2修改語言文件格式

刪除了原有的 xaml 語言文件,替換為 resx 的資源文件,和三個模塊的資源文件類型類似,下面是主工程的資源文件:

资源文件作为语言文件使用

替換成資源文件,編輯是要比 xaml 文件要方便點,起初是有考慮使用資源文件實現國際化的,作死想嘗試 xaml 文件。

折腾是可以涨姿势的

3.3語言切換核心代碼

動態切換語言的關鍵代碼改為:

public static void SetLanguage(string language = "")
{
    if (string.IsNullOrWhiteSpace(language))
    {
        language = ConfigHelper.ReadKey(KEY_OF_LANGUAGE);
        if (string.IsNullOrWhiteSpace(language))
        {
            language = System.Globalization.CultureInfo.CurrentCulture.ToString();
        }
    }

    ConfigHelper.SetKey(KEY_OF_LANGUAGE, language);
    _lastLanguage = language;

    var culture = new System.Globalization.CultureInfo(language);
    I18nManager.Instance.CurrentUICulture = culture;
}

核心的語言切換代碼是最後一句,不詳細說了,解決方案中有庫、有源碼:

I18nManager.Instance.CurrentUICulture = culture;

四.源碼

  • 源碼地址,歡迎 star:https://github.com/dotnet9/TerminalMACS/tree/master/src/TerminalMACS.Manager/TerminalMACS.ManagerForWPF

五.參考資料

  • prism template pack(prism 模板):https://marketplace.visualstudio.com/items? itemname=brianlagunas.prismtemplatepack
  • wpf 國際化開源輔助庫:https://github.com/DingpingZhang/WpfExtensions
  • accelerider.windows(子模塊加載參考開源項目):https://github.com/Accelerider/Accelerider.Windows
  • prism-samples-wpf(官方 demo):https://github.com/PrismLibrary/Prism-Samples-Wpf
Keep Exploring

延伸阅读

更多文章