Search code examples
c#uwpuwp-xamlwinui-3winui

How to Change Application Language at Runtime?


WinUI3 has surprisingly few documentation, and I can't even find how to switch the display language when the app is running

Back to the question, I created multiple languages' resource files, but I don't know how to let users choose the language they want to display.

In MainWindow.xaml

<ComboBox x:Name="LanguangeMode" SelectionChanged="Language_SelectionChanged">
    <ComboBoxItem Content="English"/>
    <ComboBoxItem Content="Simplified Chinese"/>
</ComboBox>

And in MainWindow.xaml.cs

private void Language_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var comboBox = sender as ComboBox;
    var selectedIndex = comboBox.SelectedIndex;

    switch (selectedIndex)
    {
        case 0:
            // Change language to English
            break;
        case 1:
            // Change language to Simplified Chinese
            break;
    }
}

Solution

  • This is a minimal example that demonstrates localization. (I also have a sample app repo and its video.)

    1. Create a Strings folder in your project.
    2. Inside the Strings folder, add folders for each language you want to support. You should name each language folder with its corresponding BCP-47 language tag. I use this page as reference for language tags.
    3. Create a resource file named Resources.resw in each language folder.
    4. Populate each resource files with localizations.
    5. Use x:Uid to target controls in XAML or the GetString method from ResourceLoader for localization in C# code.
    • Strings
      • en-us
        • Resources.resw
          • Name: LocalizedButton.Content / Value: Click this
          • Name: en-US / Value: English (United States)
          • Name: es-ES / Value: Spanish (Spain)
      • es-ES
        • Resources.resw
          • Name: LocalizedButton.Content / Value: Haga clic
          • Name: en-US / Value: Inglés (Estados Unidos)
          • Name: es-ES / Value: Español (España)

    MainWindow.xaml

    <Window
        x:Class="SwitchingLanguageExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="using:SwitchingLanguageExample"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid RowDefinitions="Auto,Auto">
            <ComboBox
                Grid.Row="0"
                ItemsSource="{x:Bind LanguageItems, Mode=OneWay}"
                Loaded="ComboBox_Loaded"
                SelectionChanged="ComboBox_SelectionChanged">
                <ComboBox.ItemTemplate>
                    <DataTemplate x:DataType="local:LanguageItem">
                        <TextBlock Text="{x:Bind DisplayName}" />
                    </DataTemplate>
                </ComboBox.ItemTemplate>
            </ComboBox>
            <!--  You need to set the x:Uid to target a control.  -->
            <Button
                x:Uid="LocalizedButton"
                Grid.Row="1" />
        </Grid>
    
    </Window>
    
    

    MainWindow.cs.xaml

    using Microsoft.UI.Xaml;
    using Microsoft.UI.Xaml.Controls;
    using Microsoft.Windows.ApplicationModel.Resources;
    using System.Collections.ObjectModel;
    using System.Linq;
    using Windows.Globalization;
    
    namespace SwitchingLanguageExample;
    
    public class LanguageItem
    {
        public LanguageItem(string languageTag, string displayName)
        {
            LanguageTag = languageTag;
            DisplayName = displayName;
        }
    
        public string LanguageTag { get; }
    
        public string DisplayName { get; }
    }
    
    public sealed partial class MainWindow : Window
    {
        private ResourceManager resourceManager = new();
    
        private ResourceLoader resourceLoader = new();
    
        public MainWindow()
        {
            this.InitializeComponent();
    
            LanguageItems.Add(new LanguageItem("en-US", this.resourceLoader.GetString("en-US")));
            LanguageItems.Add(new LanguageItem("es-ES", this.resourceLoader.GetString("es-ES")));
        }
    
        public ObservableCollection<LanguageItem> LanguageItems { get; } = new();
    
        private void ComboBox_Loaded(object sender, RoutedEventArgs e)
        {
            if (sender is not ComboBox languageTagComboBox)
            {
                return;
            }
    
            if (LanguageItems
                .Where(x => x.LanguageTag == ApplicationLanguages.PrimaryLanguageOverride)
                .FirstOrDefault() is LanguageItem currentLanguageItem)
            {
    
                languageTagComboBox.SelectedValue = currentLanguageItem;
            }
        }
    
        private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (sender is not ComboBox languageTagComboBox ||
                languageTagComboBox.SelectedValue is not LanguageItem selectedLanguageItem)
            {
                return;
            }
    
            ApplicationLanguages.PrimaryLanguageOverride = selectedLanguageItem.LanguageTag;
            
            ResourceContext resourceContext = this.resourceManager.CreateResourceContext();
            resourceContext.QualifierValues["Language"] = selectedLanguageItem.LanguageTag;
        }
    }
    

    NOTE

    Unfortunately, you need to restart the app to see the change of languages. Also, this doesn't work on unpackaged (non-packaged) apps.

    That's why I created the WinUI3Localizer. Give it a try. Hope it helps!

    • Switch languages without restarting the app
    • You/users can edit localized strings even after deployment
    • You/users can add new languages even after deployment
    • Use standard Resources.resw