2

I have a WPF application in which I support multiple langauges. I can change the language runtime, as I load different sets of resource dictionaries containing the string resources upon change of language.

private void UpdateLoadedLanguages(object sender, SelectionChangedEventArgs e)
        {
            RemoveLoadedLanguages();

            ResourceDictionary aResourceDictionary = new ResourceDictionary();
            ResourceDictionary bResourceDictionary = new ResourceDictionary();
            ResourceDictionary cLanguageResourceDictionary = new ResourceDictionary();

            Language selectedLanguage = (Language) e.AddedItems[0];

            switch (selectedLanguage)
            {
                case Language.enUS:
                    a.Source = new Uri("pack://application:,,,/[Projecta];component/src/Helpers/Language.en-US.xaml");
                    b.Source = new Uri("pack://application:,,,/[Projectb];component/src/resources/Language.en-US.xaml");
                    c.Source = new Uri("pack://application:,,,/[Projectc];component/src/resources/Language.en-US.xaml");
                    break;
                case Language.nlNL:
                    a.Source = new Uri("pack://application:,,,/[Projecta];component/src/Helpers/Language.nl-NL.xaml");
                    b.Source = new Uri("pack://application:,,,/[Projectb];component/src/resources/Language.nl-NL.xaml");
                    c.Source = new Uri("pack://application:,,,/[Projectc];component/src/resources/Language.nl-NL.xaml");
                    break;
                default:
                    a.Source = new Uri("pack://application:,,,/[Projecta];component/src/Helpers/Language.en-US.xaml");
                    b.Source = new Uri("pack://application:,,,/[Projectb];component/src/resources/Language.en-US.xaml");
                    c.Source = new Uri("pack://application:,,,/[Projectc];component/src/resources/Language.en-US.xaml");
                    break;
            }

            App.Current.Resources.MergedDictionaries.Add(aResourceDictionary );
            App.Current.Resources.MergedDictionaries.Add(bResourceDictionary );
            App.Current.Resources.MergedDictionaries.Add(cResourceDictionary );
        }

A resource dictionary with the translations typically looks like this:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:system="clr-namespace:System;assembly=mscorlib">

    <system:String x:Key="DefaultApplicationTitleString">Title</system:String>
    <system:String x:Key="DefaultDoneString">Done</system:String>
    <system:String x:Key="DefaultCancelString">Cancel</system:String>
    <system:String x:Key="DefaultDescriptionString">Description unknown</system:String>

    <system:String x:Key="LanguageSelectionString">Language</system:String>
    <system:String x:Key="AisleString">Aisle</system:String>
    <system:String x:Key="LevelsString">levels</system:String>

</ResourceDictionary>

When I use one of the strings as defined above like directly below it works perfect.

Title="{DynamicResource DefaultApplicationTitleString}"

But with a converter like this ...

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            string text = (string) Application.Current.FindResource("DefaultDescriptionString");

            if (value != null)
            {
                string temp = (string) value;

                char[] stringSeparators = { 'A', '.', '-' };
                string[] stringElements= temp.Split(stringSeparators, StringSplitOptions.RemoveEmptyEntries);

                string aisleString = (string) Application.Current.FindResource("AisleString");
                string levelsString = (string) Application.Current.FindResource("LevelsString");

                text = aisleString + " " + stringElements[1] + ", " + levelsString + " " + stringElements[2] + " - " +
                               stringElements[3];

            }

            return text;
        }

... that I use like this ...

<TextBlock Text="{Binding Id, Converter={StaticResource ItemIdToTextConverter}}" />

... then my strings aren't updated when I change the language. If the property that I convert is updated on its viewModel then the language change does happen.

So my question: Does anyone have a solution for me where also my strings as a result of converters are updated on a language change?

My guesses for a solution are:

  1. force an update of all frameworkelements that use a resource (How would I do this?)
  2. or maybe do something with DynamicResourceExtenstion (I found this question: Return a dynamic resource from a converter, however I do not understand the explanation of the DynamicResourceExtension. So is this the way to go? If so, how would I implement this?)
Community
  • 1
  • 1

1 Answers1

0

This would be too much for a comment, but to little for an answer (or maybe not).

Converter is called only once when element is initializing. After that there is no link to what was the source if you are using StaticResource. If you use DynamicResource, then anytime when source get changed converter will be called again.

  • Do you change source in your localization approach? Nope.
  • Does converter gets invoked when language is changed? Yes, but only on notification from Id (PropertyChanged).

One way you can fix it is to subscribe your ViewModel to language switch event and call property changed event for each property. This will execute your converter despite source values still the same.

Other possibility would be to register somehow objects which required localized data. You will not use converter anymore, but an attached property (or behavior, depends on complexity). Enumerating (or keeping a list) over elements with such property and updating it shouldn't be hard.

Third way (which I am going to use in my very first localized wpf application) is reflection. Register all windows upong creation (un-register when unloading of course), define a schema of how you identify localizable properties of elements and go through it - win.

Regarding schema. My "localizable" controls with static content will be using already existing Uid to define a key, instead of defining things in separate resource dictionary it will be automatically generated). Dynamic ones I don't yet decide, perhaps there will be a behavior or attached property or even small peace of code behind (I like MVVM, but I am not making it an absolute) to reassign values from static class with predefined strings, which will be changed by reflection.

Sinatr
  • 20,892
  • 15
  • 90
  • 319
  • Thank you for your reply! I took inspiration out of your first way to solve the problem, call property changed event. So I extended my base class for viewmodels with a method that raises the property changed events for all properties relevant to that one viewModel. On change of language selection I call that method for all loaded viewModels. – cherryorange Oct 21 '14 at 08:57