简体   繁体   中英

WPF: How to change the CurrentUICulture at runtime

I am trying to change the language my WPF app uses in a click event but it doesn't change.

private void menuItemGerman_Click(object sender, RoutedEventArgs e)
{
   Settings.Default.Culture = "de-DE";

   Thread.CurrentThread.CurrentCulture = new CultureInfo(Settings.Default.Culture);
   Thread.CurrentThread.CurrentUICulture = new CultureInfo(Settings.Default.Culture);
}

What am I missing?

What am I missing?

You changed the culture registered with the thread, and String.Format will use this now, but you need to reload all localized items in the WPF hierarchy.

WPF Localization – On-the-fly Language Selection has more information.

If you have resource files, eg:

  • Resources.resx
  • Resources.hu-hu.resx

... and want to change the localization at runtime,

... and do not want to mess with additional resource dictionaries and recoding all UI localizations,

it will work with the

Thread.CurrentThread.CurrentUICulture = new CultureInfo(lang);

But it will not change the already shown window's language.

To achieve that, more coding is required - the Application lifecycle must be managed, instead of the default.

First, remove the StartupUri from App.xaml:

<Application
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         x:Class="ADUI.App"
         xmlns:System="clr-namespace:System;assembly=mscorlib" >
     <!--StartupUri="wndMain.xaml">-->
<Application.Resources>
</Application.Resources>

Second, implement a class, which is now responsible for the application lifecycle:

public class LocApp: Application
{
    [STAThread]
    public static void Main()
    {
        App app = new App();
        app.ShutdownMode = ShutdownMode.OnExplicitShutdown;
        wndMain wnd = new wndMain();
        wnd.Closed += Wnd_Closed;
        app.Run(wnd);
    }

    private static void Wnd_Closed(object sender, EventArgs e)
    {
        wndMain wnd = sender as wndMain;
        if (!string.IsNullOrEmpty(wnd.LangSwitch))
        {
            string lang = wnd.LangSwitch;

            wnd.Closed -= Wnd_Closed;

            Thread.CurrentThread.CurrentUICulture = new CultureInfo(lang);

            wnd = new wndMain();
            wnd.Closed += Wnd_Closed;
            wnd.Show();
        }
        else
        {
            App.Current.Shutdown();
        }
    }
}

Do not forget to change the startup object on your Project properties / Application page to LocApp!

Finally, implement some code which switches the languages in the main window's code:

public partial class wndMain : Window
{
    public string LangSwitch { get; private set; } = null;

    // ... blah, blah, blah

    private void tbEn_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        LangSwitch = "en";
        Close();
    }

    private void tbHu_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        LangSwitch = "hu-hu";
        Close();
    }

    // ... blah, blah, blah

}

Make sure, that the provided localization code matches with one of the resx file language code ("hu-hu" in this example)!

This solution will close and reopen the main window, with the chosen language, and will exit if the main window closed by other means.

I also experienced this problem and my solution was:

  • Resources.en-US
  • Resources.pt-PT

I created a class that will return a dictionary with the key and the label:

public class Labels : ObservableObject
{
    public Dictionary<string, string> Items { get; set; }
    public string this[string name]
    {
        get
        {
            return Items.ContainsKey(name) ? Items[name] : "";
        }
    }

    public Labels()
    {
        Items = new Dictionary<string, string>();
    }
}

Next, one more class to get the Resources:

public static class LanguageUtils
{
    public static Labels GetLangLables(string label)
    {
        var resources = Resources.ResourceManager.GetResourceSet(new CultureInfo(label), true, true);
        return new Labels
        {
            Items = resources.Cast<DictionaryEntry>().ToDictionary(r => r.Key.ToString(), r => r.Value.ToString())
        };
    }
}

When you need some language:

LanguageUtils.GetLangLables("pt-PT");

Once, you can't raise (RaisePropertyChanged()) static properties, use this:

public class LanguageContext
{
    private static LanguageContext _languageContext;
    public static LanguageContext Instance
    {
        get
        {
            if (_languageContext == null)
            {
                _languageContext = new LanguageContext();
            }

            return _languageContext;
        }
    }

    protected LanguageContext()
    {
        CurrentLangLabels = LanguageUtils.GetLangLables("en-US");
     }
     public Labels CurrentLangLabels { get; set; }
}

Now you can update language:

LanguageContext.Instance.CurrentLangLabels = LanguageUtils.GetLangLables(SelectedLanguage.Resource);

Raise like this:

public Labels CurrentLangLabels
{
   get { return LanguageContext.Instance.CurrentLangLabels; }
   set { RaisePropertyChanged(); }
}

And use label:

CurrentLangLabels.Items[LabelName]

this might come in handy for someone. I have used the advice given by George & Chris Schaller above to make this work in my project without creating new Application class.

public string LangSwitch { get; private set; } = null;    
private void BtnLngPl_Click(object sender, RoutedEventArgs e)
        {
            CultureInfo current = CultureInfo.CurrentUICulture;
            CultureInfo newUiCulture;
            if (current.Name.Equals("en-US"))
            {
                newUiCulture = new CultureInfo("pl");
                Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
                if (System.Windows.Application.Current.MainWindow != null)
                    ((MainWindow)System.Windows.Application.Current.MainWindow).Closed += Wnd_Closed;
                Thread.CurrentThread.CurrentCulture = newUiCulture;
                Thread.CurrentThread.CurrentUICulture = newUiCulture;
                LangSwitch = "pl";
                Close();
            }
            else
                newUiCulture = new CultureInfo("en-US");

            CultureInfo.CurrentUICulture = newUiCulture;
            Console.WriteLine(@"The current UI culture is now {0}",
                CultureInfo.CurrentUICulture.Name);
        }

and the other necessary code can be taken from George's answer. Not sure how good of a solution this is, but does the job for me.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM