DotNetCore 3.0 助力 WPF本地化

来源:https://www.cnblogs.com/luquanmingren/archive/2019/08/21/11384104.html
-Advertisement-
Play Games

概覽 隨著我們的應用程式越來越受歡迎,我們的下一步將要開發多語言功能。方便越來越多的國家使用我們中國的應用程式, 基於 WPF 本地化,我們很多時候使用的是系統資源文件,可是動態切換本地化,就比較麻煩了。 有沒有一種方法既可以適用系統的資源文件,又能方便快捷的切換本地化呢? 實現思路 現在我們將要實 ...


概覽

隨著我們的應用程式越來越受歡迎,我們的下一步將要開發多語言功能。方便越來越多的國家使用我們中國的應用程式,
基於 WPF 本地化,我們很多時候使用的是系統資源文件,可是動態切換本地化,就比較麻煩了。
有沒有一種方法既可以適用系統的資源文件,又能方便快捷的切換本地化呢?

實現思路

現在我們將要實現的是基於 DotNetCore 3.0 以上版本 and WPF 桌面應用程式模塊化的多語言功能。
動態切換多語言思路:

  • 把所有模塊的資源文件添加到字典集合。
  • 將資源文件里的key,綁定到前臺。
  • 通過通知更改 CurrentCulture 多語言來使用改變的語言文件里的key。
  • 通過綁定 Binding 拼接Path 在輸出。

動態切換

我們先來看實現結果

  • 第一行是我們的主程式的數據展示,用於業務中的本地化
  • 第二行是我們業務模塊A的數據展示
  • 第三行是我們業務模塊B的數據展示

來看一下xaml展示

通過ComboBox選擇來切換語言

搭建模擬業務項目

創建一個WPF App(.NET Core)應用程式

創建完成後,我們需要引入業務A模塊及業務B模塊和業務幫助模塊

PS:根據自己的業務需要來完成項目的搭建。本教程完全適配多語言功能。

使用ResX資源文件

在各個模塊里添加Strings 文件夾用來包含 各個國家和地區的語言文件。

多語言可以參考:https://github.com/UnRunDeaD/WPF---Localization/blob/master/ComboListLanguages.txt

資源文件可以放在任意模塊內,比如業務模塊A ,主程式,底層業務,控制項工具集等

創建各個業務模塊資源文件

Strings文件夾可以任意命名
SR資源文件可以任意命名

幫助類

封裝到底層供各個模塊調用

    public class TranslationSource : INotifyPropertyChanged
    {
        public static TranslationSource Instance { get; } = new TranslationSource();

        private readonly Dictionary<string, ResourceManager> resourceManagerDictionary = new Dictionary<string, ResourceManager>();

        public string this[string key]
        {
            get
            {
                Tuple<string, string> tuple = SplitName(key);
                string translation = null;
                if (resourceManagerDictionary.ContainsKey(tuple.Item1))
                    translation = resourceManagerDictionary[tuple.Item1].GetString(tuple.Item2, currentCulture);
                return translation ?? key;
            }
        }

        private CultureInfo currentCulture = CultureInfo.InstalledUICulture;
        public CultureInfo CurrentCulture
        {
            get { return currentCulture; }
            set
            {
                if (currentCulture != value)
                {
                    currentCulture = value;
                    // string.Empty/null indicates that all properties have changed
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(string.Empty));
                }
            }
        }

        // WPF bindings register PropertyChanged event if the object supports it and update themselves when it is raised
        public event PropertyChangedEventHandler PropertyChanged;

        public void AddResourceManager(ResourceManager resourceManager)
        {
            if (!resourceManagerDictionary.ContainsKey(resourceManager.BaseName))
            {
                resourceManagerDictionary.Add(resourceManager.BaseName, resourceManager);
            }
        }

        public static Tuple<string, string> SplitName(string local)
        {
            int idx = local.ToString().LastIndexOf(".");
            var tuple = new Tuple<string, string>(local.Substring(0, idx), local.Substring(idx + 1));
            return tuple;
        }
    }

    public class Translation : DependencyObject
    {
        public static readonly DependencyProperty ResourceManagerProperty =
            DependencyProperty.RegisterAttached("ResourceManager", typeof(ResourceManager), typeof(Translation));

        public static ResourceManager GetResourceManager(DependencyObject dependencyObject)
        {
            return (ResourceManager)dependencyObject.GetValue(ResourceManagerProperty);
        }

        public static void SetResourceManager(DependencyObject dependencyObject, ResourceManager value)
        {
            dependencyObject.SetValue(ResourceManagerProperty, value);
        }
    }

    public class LocExtension : MarkupExtension
    {
        public string StringName { get; }

        public LocExtension(string stringName)
        {
            StringName = stringName;
        }

        private ResourceManager GetResourceManager(object control)
        {
            if (control is DependencyObject dependencyObject)
            {
                object localValue = dependencyObject.ReadLocalValue(Translation.ResourceManagerProperty);

                // does this control have a "Translation.ResourceManager" attached property with a set value?
                if (localValue != DependencyProperty.UnsetValue)
                {
                    if (localValue is ResourceManager resourceManager)
                    {
                        TranslationSource.Instance.AddResourceManager(resourceManager);

                        return resourceManager;
                    }
                }
            }

            return null;
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            // targetObject is the control that is using the LocExtension
            object targetObject = (serviceProvider as IProvideValueTarget)?.TargetObject;

            if (targetObject?.GetType().Name == "SharedDp") // is extension used in a control template?
                return targetObject; // required for template re-binding

            string baseName = GetResourceManager(targetObject)?.BaseName ?? string.Empty;

            if (string.IsNullOrEmpty(baseName))
            {
                // rootObject is the root control of the visual tree (the top parent of targetObject)
                object rootObject = (serviceProvider as IRootObjectProvider)?.RootObject;
                baseName = GetResourceManager(rootObject)?.BaseName ?? string.Empty;
            }

            if (string.IsNullOrEmpty(baseName)) // template re-binding
            {
                if (targetObject is FrameworkElement frameworkElement)
                {
                    baseName = GetResourceManager(frameworkElement.TemplatedParent)?.BaseName ?? string.Empty;
                }
            }

            Binding binding = new Binding
            {
                Mode = BindingMode.OneWay,
                Path = new PropertyPath($"[{baseName}.{StringName}]"),
                Source = TranslationSource.Instance,
                FallbackValue = StringName
            };

            return binding.ProvideValue(serviceProvider);
        }
    }

前臺綁定

//引用業務模塊
xmlns:ext="clr-namespace:WpfUtil.Extension;assembly=WpfUtil"
// 引用剛纔你命名的文件夾名字
xmlns:resx="clr-namespace:ModuleA.Strings"
// 每個模塊通過幫助類,將當前模塊的資源類,
// 載入到資源管理集合裡面用於分配每個鍵值
// 引用剛纔你命名的資源文件名字 -> SR
ext:Translation.ResourceManager="{x:Static resx:SR.ResourceManager}"

顯示文字

//讀取資源文件里的鍵值
<Label Content="{ext:Loc Test}" FontSize="21" />

後臺實現

根據業務的需要,我們在界面上無法適用靜態文字顯示的,一般通過後臺代碼來完成,對於 code-behind 的變數使用,同樣可以應用於資源字典。
比如在業餘模塊代碼段里的模擬實現

// SR 是當前業務模塊的資源文件類,管理當前模塊的資源字元串。
// 根據不同的 `CurrentCulture` 選擇相對應的本地化
Message = string.Format(SR.ResourceManager.GetString("Message",Thread.CurrentThread.CurrentUICulture),System.DateTime.Now);

PS: 歡迎各位大佬慷慨指點,有不足之處,請指出!有疑問,請指出,喜歡它,請支持!

下載地址

https://github.com/androllen/WpfNetCoreLocalization

相關鏈接

https://github.com/Jinjinov/wpf-localization-multiple-resource-resx-one-language/blob/master/README.md
https://codinginfinity.me/post/2015-05-10/localization_of_a_wpf_app_the_simple_approach


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 以前的測試的時候使用正常,當輸入數據無效時,行標題上會出現紅色感嘆號的圓形圖標,最近拿起來適當修改完善了一下,發現ErrorText屬性的設置失效了,行標題上死活都不出現紅色感嘆號的圓形圖標,奇了怪了,怎麼回事,搜遍全網(包括官網),沒找到解決方案。 我就不信這個鞋,幸好有備份的之前的版本,拿過來和 ...
  • //獲取日期+時間 DateTime.Now.ToString(); // 2008-9-4 20:02:10 DateTime.Now.ToLocalTime().ToString(); // 2008-9-4 20:12:12 //獲取日期 DateTime.Now.ToLongDateStri... ...
  • C# 中的委托(Delegate)類似於 C 或 C++ 中函數的指針。委托(Delegate) 是存有對某個方法的引用的一種引用類型變數。引用可在運行時被改變。 委托(Delegate)特別用於實現事件和回調方法。所有的委托(Delegate)都派生自 System.Delegate 類。 委托多 ...
  • 大漠插件:3.1233 找圖,找色,識字,找字,視窗,按滑鼠,按鍵盤 0、註冊dm.dll; regsvr32 xxx\dm.dll 1、添加com引用; 2、dmsoft各種調用; 原理: 查找視窗hwnd→視窗激活→添加消息到文本框→回車→視窗取消激活 截圖: 代碼: ...
  • 不多說,直接上代碼 web.config如下 其中 SymmetricMethod.Decrypto 解密演算法,至於加解密演算法,大家自行百度吧 ...
  • 在使用vs2019添加mvc控制器的時候 這已經是第二次遇到這個問題了。常言道,多喝熱水,重啟試試。有時候當應用工作不正常,重啟也許能解決問題。但是程式員通常接觸不到伺服器系統許可權。而運維人員和公司流程經常人為製造麻煩阻止我們去重啟應用。 上一次就是通過重啟解決的。 翻牆找了很多解決方案,好似VS團 ...
  • 最近工作中常常有有數據處理的需求,一個Excel動不動就是上十萬的數據量,在用 EPPlus.Core 導入數據入庫的時候遇到了一個莫名其妙的問題 我TM一個導入Excel,解析數據哪裡來的 dictionary??? 然後開始了艱辛的調試debug之旅,反覆上傳導入都不行,然後自己創建了一個格式相 ...
  • 最近遇到了一個部署問題,每次啟動都老是報錯,我的應用程式是基於.NET 4.5.2 VS2015 。用事件查看器 就發現是發生在App啟動的時候。 給代碼加上try..catch 啥也沒抓不上,再後來用同事的機器跑了跑,也沒啥問題。最後實錘是環境問題。 其中安裝Cefsharp完(Nuget)rea ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...