.NET WPF应用程序如何实现运行时语言切换?
旧版.NET WPF应用运行时切换语言实现方案
问题背景
正在对一款基于C#开发的旧版.NET WPF应用做本地化,GUI由两个XAML窗口构建。已编写LocalizationManager类负责翻译逻辑,多语言字符串存储于英文、德文.resx文件中。当前翻译功能正常:可在设置中切换语言,设置保存至settings.xml,重启应用后生效。现需实现运行时切换语言,无需重启即可让UI立即更新为新语言内容,寻求简单可行的解决方案。
当前LocalizationManager代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Globalization; using System.Reflection; using System.Resources; using System.Threading; namespace ProgrammierstationV2.Logic.Localization { /// <summary> /// A class for managing the localisation of texts based on resource files. /// </summary> class LocalizationManager { private ResourceManager _resourceManager; // Property for defining and retrieving the current culture for localisation purposes public CultureInfo CurrentCulture { get; set; } static private bool eninitialized = false; static private CultureInfo cinfo; private static readonly Lazy<LocalizationManager> _instance = new Lazy<LocalizationManager>(() => new LocalizationManager()); /// <summary> /// The singleton instance of LocalizationManager. /// </summary> public static LocalizationManager Instance => _instance.Value; /// <summary> /// Event that is triggered when the language is changed. /// </summary> public event EventHandler LanguageChanged; /// <summary> /// Private constructor to prevent instantiation. /// Initialises the resource manager instance based on the name of the resource file and the assembly type. /// </summary> private LocalizationManager() { // Initialisation of the resource manager with the name of the resource file // and the assembly type of the LocalisationManager class> _resourceManager = new ResourceManager("ProgrammierstationV2.LocalizationResources.Strings", typeof(LocalizationManager).Assembly); // Sets the default language to English if (eninitialized == true) { CurrentCulture = cinfo; } else { cinfo = new CultureInfo("en-GB"); eninitialized = true; CurrentCulture = cinfo; } } /// <summary> /// Returns a list of supported languages. /// </summary> /// <returns>A list of supported languages as culture codes.</returns> public List<string> GetSupportedLanguages() { return new List<string> { "en-GB", "de-DE" }; } /// <summary> /// Displays the localised text for a specific key. /// </summary> /// <param name='key'>The key that references the desired localised text in the resource file.</param> public string DisplayLocalizedText(string key) { SetCulture(CurrentCulture); // Retrieve the localised text for the specified key string localizedText = _resourceManager.GetString(key); // Check whether the resource has been found if (localizedText == null) { // Generate an error message that the resource is missing Console.WriteLine($"Error: Resource with key '{key}' is missing in resource files."); // Returns a standard text or null return "Error: Missing Resource"; } // Retrieve and return the localised text for the specified key return localizedText; } /// <summary> /// Sets the current culture for localisation. /// </summary> /// <param name='culture'>The desired culture.</param> public void SetCulture(CultureInfo culture) { // Setting the current culture and UI culture Thread.CurrentThread.CurrentCulture = culture; Thread.CurrentThread.CurrentUICulture = culture; } /// <summary> /// Changes the current language for localisation. /// </summary> /// <param name='languageCode'>The culture code of the desired language.</param> public void ChangeLanguage(string languageCode) { // Check whether the specified language is supported if (GetSupportedLanguages().Contains(languageCode)) { // Create a new CultureInfo instance with the specified language code CultureInfo newCulture = new CultureInfo(languageCode); cinfo = newCulture; // Set the current culture for localisation SetCulture(newCulture); // Updating the CurrentCulture property CurrentCulture = newCulture; } } } }
曾尝试在MainViewModel中添加事件处理但无进展,暂不考虑多线程方案,希望尝试其他简单方案。
解决方案
第一步:完善LocalizationManager的事件触发逻辑
当前LocalizationManager已定义LanguageChanged事件,但ChangeLanguage方法未触发该事件,需补充触发逻辑,同时优化文本获取逻辑:
public void ChangeLanguage(string languageCode) { if (GetSupportedLanguages().Contains(languageCode)) { CultureInfo newCulture = new CultureInfo(languageCode); cinfo = newCulture; SetCulture(newCulture); CurrentCulture = newCulture; // 触发语言变更事件,通知UI更新 LanguageChanged?.Invoke(this, EventArgs.Empty); } } // 优化DisplayLocalizedText方法,移除重复设置文化的逻辑 public string DisplayLocalizedText(string key) { // 直接传入CurrentCulture,避免依赖线程文化 string localizedText = _resourceManager.GetString(key, CurrentCulture); if (localizedText == null) { Console.WriteLine($"Error: Resource with key '{key}' is missing in resource files."); return "Error: Missing Resource"; } return localizedText; }
第二步:实现UI即时更新的两种简单方案
方案一:数据绑定+ViewModel通知(推荐,适配MVVM结构)
若应用使用ViewModel(如MainViewModel),可让ViewModel监听语言变更事件,触发属性变更通知以刷新UI:
- 让MainViewModel实现
INotifyPropertyChanged接口:
public class MainViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public MainViewModel() { // 监听语言变更事件,触发属性更新 LocalizationManager.Instance.LanguageChanged += (s, e) => { // 触发所有本地化文本属性的变更通知 OnPropertyChanged(nameof(WindowTitle)); OnPropertyChanged(nameof(SaveButtonText)); OnPropertyChanged(nameof(MessageLabelText)); // 其他本地化属性依次添加 }; } // 示例:本地化属性定义 public string WindowTitle => LocalizationManager.Instance.DisplayLocalizedText("Window_Title"); public string SaveButtonText => LocalizationManager.Instance.DisplayLocalizedText("Button_Save"); public string MessageLabelText => LocalizationManager.Instance.DisplayLocalizedText("Label_Message"); }
- 在XAML中绑定这些属性:
<Window Title="{Binding WindowTitle}"> <Button Content="{Binding SaveButtonText}" /> <Label Content="{Binding MessageLabelText}" /> </Window>
切换语言时,ViewModel收到事件后触发属性变更,UI会自动重新读取本地化文本。
方案二:手动遍历控件刷新(适合无MVVM的旧项目)
若未使用ViewModel,可在窗口后台代码中监听语言变更事件,手动更新所有控件文本:
- 在窗口构造函数中添加事件监听:
public MainWindow() { InitializeComponent(); // 初始化时设置一次文本 UpdateLocalizedTexts(); // 监听语言变更事件 LocalizationManager.Instance.LanguageChanged += (s, e) => UpdateLocalizedTexts(); }
- 实现文本更新方法:
private void UpdateLocalizedTexts() { this.Title = LocalizationManager.Instance.DisplayLocalizedText("Window_Title"); btnSave.Content = LocalizationManager.Instance.DisplayLocalizedText("Button_Save"); lblMessage.Content = LocalizationManager.Instance.DisplayLocalizedText("Label_Message"); // 其他需要本地化的控件依次添加 }
若有多个窗口,需在每个窗口中重复上述逻辑。
额外注意事项
- 所有需要本地化的文本必须通过
LocalizationManager.Instance.DisplayLocalizedText(key)获取,禁止硬编码字符串。 - 切换语言后,需确保调用原有逻辑将设置保存至settings.xml。
- 对于代码动态生成的控件,需在
UpdateLocalizedTexts中同步更新其文本,或在创建时绑定本地化方法。
内容的提问来源于stack exchange,提问作者sw-dummy
相关产品推荐
相关产品推荐

