.NET · WinForms · WPF

Change the language at runtime in WinForms and WPF.

Setting CurrentUICulture changes nothing on the screen: forms and windows read their resources once, when they are created. There are three ways to switch, and they differ in what you have to write and what can break.

Release folder EXEMyApp.exe DEde\ FRfr\ JAja\ WPF loads the folder of the chosen language Harbor Supply 12 Neue Bestellung DateiSprache Kunde:Menge: Erstellen Abbrechen Deutsch Français 日本語 Nouvelle commande FichierLangue Client :Quantité : Créer Annuler Release folder EXEMyApp.exe DEde\ FRfr\ JAja\ Harbor Supply 12 Neue Bestellung DateiSprache Kunde:Menge: Erstellen Abbrechen Deutsch Français 日本語 Nouvelle commande FichierLangue Client :Quantité : Créer Annuler
On this page

Three ways to switch, from safest to most work.

All three use the same satellite assemblies. The difference is only in your code.

Ways to change the UI language at runtime and their trade-offs
WayWhat you writeWhat can breakFits
RestartSave the choice, read it in Main, restartNothing in the UI; the user loses unsaved state unless you keep itMost desktop applications; WinForms and WPF
Recreate the windowsSet the culture, open new forms or windows, close the old onesState held in the old windows; dialogs that are openApplications with one main window and little state in it
Reapply resourcesWalk every control and apply its resources againLayout, text the user typed, menus, user controls, strings set in codeWinForms only, when a restart is not acceptable

WPF windows built from BAML cannot reload their XAML in place: there the choice is between restarting and recreating. Libraries that bind every text to a .resx through a markup extension switch in place, but only for the text bound that way.

1. Restart with the new language.

Set the culture in Main before the first form or window, as the WinForms and WPF guides show, and restart when the user picks another language. The WPF sample in Localizer does exactly this from its Language menu.

WinForms

void SwitchLanguage(string culture)   // "de-DE"
{
    Settings.Default.Language = culture;
    Settings.Default.Save();
    Application.Restart();
}

WPF

void SwitchLanguage(string culture)
{
    Settings.Default.Language = culture;
    Settings.Default.Save();
    Process.Start(Environment.ProcessPath!);   // .NET 6 and later
    Application.Current.Shutdown();
}

2. Recreate the windows.

A new form or window reads its resources in InitializeComponent, in the culture current at that moment. Set the culture for this thread and for new ones, open a new main window, and close the old one.

// WPF; in WinForms the same with a new MainForm and an ApplicationContext
void SwitchLanguage(CultureInfo culture)
{
    CultureInfo.DefaultThreadCurrentUICulture = culture;
    Thread.CurrentThread.CurrentUICulture = culture;

    var old = Application.Current.MainWindow;
    var fresh = new MainWindow();
    Application.Current.MainWindow = fresh;   // before closing the old one
    fresh.Show();
    old.Close();
}
  • Carry the state over yourself: the open document, the selection, window position.
  • Close or reopen dialogs that are open: they keep the old language.

3. Reapply resources to open forms (WinForms).

ComponentResourceManager.ApplyResources sets every localizable property of a control from the form's resources in the current UI culture. That is also why layouts break: the stored Location and Size come back too, and so does the original Text of a text box.

static void ApplyCulture(Form form, CultureInfo culture)
{
    Thread.CurrentThread.CurrentUICulture = culture;
    var resources = new ComponentResourceManager(form.GetType());
    resources.ApplyResources(form, "$this");
    ApplyTo(form.Controls, resources);
}

static void ApplyTo(Control.ControlCollection controls, ComponentResourceManager resources)
{
    foreach (Control c in controls)
    {
        resources.ApplyResources(c, c.Name);
        ApplyTo(c.Controls, resources);
    }
}
  • Menus and toolbars are not in Controls: walk the Items of each MenuStrip, ToolStrip, and StatusStrip as well.
  • User controls have their own resources: apply them with a resource manager for the user control's type.
  • Text set in code after InitializeComponent, such as a status message, has to be set again.
  • Keep what the user changed: save sizes, splitter positions, and typed text before you apply, and restore them after.

Only part of the UI changed language?

Parts of the UI that keep the old language, and why
SymptomCauseWhat to do
Text set in code stays in the old languageIt was read once from Strings.Something and storedRead it again after the switch, or restart
Work on background threads uses the old languageCurrentUICulture is per threadSet CultureInfo.DefaultThreadCurrentUICulture too
Dates and numbers keep the old formatCurrentCulture (formats) and CurrentUICulture (resources) are separateSet both, if the formats should follow the language
WPF bindings format dates in US EnglishBindings use FrameworkElement.Language, which is en-US unless setOverride its default at startup, or set Language on the window (FAQ below)
MessageBox buttons and file dialogs stay in EnglishThey are Windows dialogs and follow the Windows display languageNothing in your satellites changes them
One language shows English text everywhereIts satellite is not foundSee Satellite assembly not loading

Satellites from Lingobit Localizer.

The switching code stays yours; Lingobit makes what it switches to. It translates the forms, windows, and strings of your compiled WinForms or WPF application, or your .resx files, and writes the satellite assembly of each language. Run Localized Application starts the application in the language you are checking.

Questions about switching the language

Why does setting CurrentUICulture not change the open form?

The form read its resources in InitializeComponent when it was created. The new culture applies to resources read from then on: new forms, new windows, and strings you read again.

Why do WPF bindings show dates in US format?

WPF formats bound values with FrameworkElement.Language, which defaults to en-US, not with CurrentCulture. Override the default once, at startup, before the first window is created:

FrameworkElement.LanguageProperty.OverrideMetadata(
    typeof(FrameworkElement),
    new FrameworkPropertyMetadata(
        XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));

The default can be overridden only once per process. For a later switch, set Language on each new window; its elements inherit it.

Can WPF switch the language without restarting?

Windows from BAML cannot reload their XAML, so recreate them. Text bound to .resx resources through a markup extension that listens for the culture change switches in place; text from {x:Static} is read once.

Do I need different satellites for switching at runtime?

No. The same de\MyApp.resources.dll serves a start in German and a switch to German. Only the code that picks the culture differs.