'WinUI 3 runtime localization

I am developing WinUI 3 app, currently stuck with localization. Although I wrote separate resw resource files for different cultures and localized with x:Uid I cannot find a way to change language in app runtime.

Setting Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride only works fine for settings startup language.

Is runtime localization in WinUI 3 even possible?



Solution 1:[1]

Original Answer:

I think you're on the right track. If you are trying to change the language at runtime, call a method that will set Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride to the new language "string". But then after that, you will also have to reload your Page so that the new language takes effect.

private void ReloadLanguage(string languageOverride)
{
    // Change the app language
    Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = languageOverride;

    // Be sure to clear the Frame stack so that cached Pages are removed, otherwise they will have the old language.
    Frame.BackStack.Clear();

    // Reload the page that you want to have the new language
    Frame.Navigate(typeof(MainPage));

}

-OR-

You'll have to call your ResourceLoader for the UI components you need/want to refresh one at a time.

private void RefreshUIText()
{
    var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();
    this.myXAMLTextBlockElement.Text = resourceLoader.GetString("Farewell");
}

Updated Answer:

According to Microsoft online documentation, they suggest that anytime you change the language for you "to reload your strings from the default ResourceContext". I know this is not ideal, but it appears to work.

I got the following Example Solution below to work:

MainWindow.xaml

<Grid>
    <Frame x:Name="MainFrame"/>
</Grid>

MainWindow.xaml.cs

public MainWindow()
{
    this.InitializeComponent();
    MainFrame.Navigate(typeof(MainPage));
}

MainPage.xaml

<Grid>
    <StackPanel>
        <TextBlock x:Uid="Greeting" x:Name="Greeting" Text="" Margin="15"/>
        <TextBlock x:Uid="Farewell" x:Name="Farewell" Text="" Margin="15"/>
        <Button x:Name="SelectLanguageButton" Click="SelectLanguageButton_Click" Content="Select Language" Margin="15"/>
    </StackPanel>
</Grid>

MainPage.xaml.cs

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
        this.Loaded += MainPage_Loaded;
    }

    private void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        RefreshUIText();
    }

    private void SelectLanguageButton_Click(object sender, RoutedEventArgs e)
    {
        Frame.Navigate(typeof(LanguagePage));
    }

    private void RefreshUIText()
    {
        var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForViewIndependentUse();
        this.Greeting.Text = resourceLoader.GetString("Greeting/Text");
        this.Farewell.Text = resourceLoader.GetString("Farewell/Text");
    }
}

LanguagePage.xaml

<Grid>
    <StackPanel>
        <Button x:Name="EnglishButton" Click="EnglishButton_Click" Content="English" Margin="15"/>
        <Button x:Name="FrenchButton" Click="FrenchButton_Click" Content="French" Margin="15"/>
    </StackPanel>
</Grid>

LanguagePage.xaml.cs

public sealed partial class LanguagePage : Page
{
    public LanguagePage()
    {
        this.InitializeComponent();
    }

    private void EnglishButton_Click(object sender, RoutedEventArgs e)
    {
        ReloadLanguage("en");
    }

    private void FrenchButton_Click(object sender, RoutedEventArgs e)
    {
        ReloadLanguage("fr");
    }

    private void ReloadLanguage(string languageOverride)
    {
        // Change the app language
        Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = languageOverride;

        // Be sure to clear the Frame stack so that cached Pages are removed, otherwise they will have the old language.
        Frame.BackStack.Clear();

        // Reload the page that you want to have the new language
        Frame.Navigate(typeof(MainPage));
    }
}

Resource Files and contents, Solution Explorer

enter image description here

Also remember to add your language declarations in the Package.appxmanifest file

enter image description here

Solution 2:[2]

I created new project - Blank App, Packaged with WAP(WinUI 3 in Desktop) with latest project templates, which come with VS extension Windows APP SDK C# VS2019. As you noticed - I am using VS 2019, Community edition. I am attaching csproj configuration of a project below.

Project config

There is a frame in main window which navigates to main page. There is only one button on main page which leads to LanguagePage on click.

Main window ctr

Goal is to change language, clear navigation stack and navigate back to main page. However, various scenarios in SetAppLanguage didn't produced wanted result. enter image description here

Please take a look at https://www.py4u.net/discuss/731870 - tried all scenarios, no effect on GUI. Nevertheless Windows.ApplicationModel.Resources.Core.ResourceContext.GetForCurrentView(); is throwing a COMException: 'Resource Contexts may not be created on threads that do not have a CoreWindow. (0x80073B27)'

Solution 3:[3]

I've spent many hours on this subject. Here's my sincere advice: DO NOT TRY TO LOCALIZE YOUR VIEWS. Also, don't try to handle changes to the language at RunTime. No one does this, you'll never see an ROI in the investment.

Make all your localization in the view models and use the StringLocalizer class that's built into .NET's DI. Add the string localizer to the DI first:

this.serviceProvider = new ServiceCollection()
            .AddLogging()
            .AddLocalization();

Then, in your .NET or .Net Standard library, in your view model, add the following initialization:

private readonly IStringLocalizer<LandingViewModel> stringLocalizer;

public MyViewModel(IStringLocalizer<MyViewModel> stringLocalizer)
{
    this.stringLocalizer = stringLocalizer;
}

Now, if you have some text you want to display, say on a button, you'd have:

public string SignUpText => this.stringLocalizer["SignUp"];

And in XAML, you'd have:

        <Button Content="{x:Bind ViewModel.SignUpText}"/>

Finally, give the RESX file the same name as your view model. It should be an Embedded Resource with, and this is important, no custom tool:

enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Pawsh
Solution 3