Search code examples
.netwpflocalizationresources

Can't find any new guides and documentation on WPF localization


So, I want to localize my app, but I can't find any new guides or documentation, usually the guides either for .NET framework written in like 2015, or guides, that are using RESX, isn't there another way to localize the app instead of using RESX and {x:Static} binding. I know there is RESW, but it looks like that RESW is only supported in UWP.


Solution

  • I cannot give you a definitive guide, but here is a simple way to localize strings and number/date formatting in WPF. This is the approach I cobbled together from various answers here and other websearching.

    Note that, as we discussed in this example, there is no ability for the user to be able to change the language on the fly; Once the language is changed, the app must be restarted.

    Suppose your app is one module, named MyCompany.MyApp. You put all of your strings into a RESX file named MyStrings.resx. When you build it the output folder might look like this

    \net8.0-windows
        MyCompany.MyApp.exe
        MyCompany.MyApp.dll
    

    This works fine for English but you want to add... French and Japanese. You want not only strings translated into whichever of those languages is current, but you also want number/date formatting to follow that language's default culture.

    First, create two new RESX files. MyStrings.fr.resx and MyStrings.ja.resx, right alongside the main MyStrings.resx. The key is to use the two-letter lanugage code in the name. You can delete the backing .CS file from these new files; You only need it for the main one.

    Each of these will hold the translations of every string in MyStrings.resx for that language.

    Next, come up with some way to save to settings an identifier for the lanuguage configured.

    At application startup, before you do anything else, get that saved language code from your settings and then obtain (from the system) the CultureInfo for that language. Use it to set thread cultures. You can use ResourceManager.GetResourceSet to obtain it the various cultures. Once you have obtained the CultureInfo for the language that is going to be used, set the thread cultures in this way.

    System.Threading.Thread.CurrentThread.CurrentUICulture = languageCulture;
    CultureInfo.DefaultThreadCurrentUICulture  = languageCulture;
    

    What the above buys you is automatic selection of the appropriate string from the appropriate .RESX file based upon the language culture you chose at startup. So if your C# code refers to a string resource in MyStrings.resx like this

    var str = MyStrings.SomeStringResource
    

    ...or if your XAML refers to it like this

    <TextBlock Text="{x:Static res:MyStrings.SomeStringResource}"/>
    

    The string chosen will be automatically selected from the appropriate .RESX file. If the current language you set up is Japanese, it will be chosen from MyStrings.ja.resx. If it is French it will be chosen from MyStrings.fr.resx. if it is English or if it is some culture that you do not have a .RESX file for, it will be chosen from MyStrings.resx

    Finally, to deal with number/date formatting, you need to Set this language on the FrameworkElement class metadata. To illustrate, assume that languageCulture below is the CultureInfo of the currently configured language which you obtained at startup.

    FrameworkElement.LanguageProperty.OverrideMetadata(
        typeof(FrameworkElement), 
        new FrameworkPropertyMetadata(
            XmlLanguage.GetLanguage(languageCulture.Name)));
    

    Again, this must be one early before any UI shows. But once you do this, every single call to IValueConverter.Convert or IMultiValueConverter.Convert will receive a CultureInfo that is appropriate for the configured language, regardless of the OS configuration.

    When you build your project, you will see new subdirectories in the build output. Each subdirectory holds the resource DLLs for a single language. Like this

    \net8.0-windows
        MyCompany.MyApp.exe
        MyCompany.MyApp.dll
        \fr
            MyCompany.MyApp.Resources.dll
        \ja
            MyCompany.MyApp.Resources.dll
    

    You will want to ship these with your installed project.

    Every time you need to add a new language, all you need is a new .RESX file, you'll get a new subdirectory.

    I'm sure there are better ways, more sophisticated ways. But this suits our purpose. Our strings are translated and our number/date formatting looks good