You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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:

  1. 让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");
}
  1. 在XAML中绑定这些属性:
<Window Title="{Binding WindowTitle}">
    <Button Content="{Binding SaveButtonText}" />
    <Label Content="{Binding MessageLabelText}" />
</Window>

切换语言时,ViewModel收到事件后触发属性变更,UI会自动重新读取本地化文本。

方案二:手动遍历控件刷新(适合无MVVM的旧项目)

若未使用ViewModel,可在窗口后台代码中监听语言变更事件,手动更新所有控件文本:

  1. 在窗口构造函数中添加事件监听:
public MainWindow()
{
    InitializeComponent();
    // 初始化时设置一次文本
    UpdateLocalizedTexts();
    // 监听语言变更事件
    LocalizationManager.Instance.LanguageChanged += (s, e) => UpdateLocalizedTexts();
}
  1. 实现文本更新方法:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 06:05:01