1

I am new to WPF but want to build a Windows application with the UI defined in XAML that supports localization from the beginning. It is not only the UI elements that needs to be localized but also a lot of content which is residing in a database.

I have set up a database structure to be able to hold the localized information, but I am not sure how to get it displayed with regards to the locale chosen e.g. "en-US" or "de-DE".

How do I provide the localized texts to the XAML UI elements in a way that is natural to WPF/XAML? I do not want to rely on any third party code such as WPFLocalizationExtension.

I have seen how this is possible with a resource file but that will only support localization for the known UI elements not the content which is dynamically generated.

Is there a specific provider I should implement or am I missing something completely different?

4
  • Can't you just assign the "Text" or "Content" on the elements that you generate dinamically? I mean, when you add let's say a button, you can just do a query on your db and set the content property from code behind. Commented Oct 31, 2017 at 16:44
  • Yes that can certainly be done. However I want to do this "the right way" with regards to WPF XAML and I am not sure if this is the intended way to handle this in WPF. Commented Nov 1, 2017 at 8:25
  • Well you don't have particular choice. It's like i said in the first comment, or you define a viewmodel that implement the INotifyPropertyChanged and define enough property to cover all your possible dynamic control. Each property represent a text or a content that is filled with a query and is binded to a single control. However in my opinion you shouldn't use the viewmodel since we are talking of something that is purely Visual (i.e. your data are not afflicted, it's just a different localization for some text), that's why i suggested to do that via code behind Commented Nov 2, 2017 at 7:52
  • I think you are right in your first comment @DanieleSartori . After researching a bit further I have come to the same conclusion that localization tag (i.e. "en-US") should be part of the query for the data. You could think of it as items in a store that have different names in different languages. Commented Nov 2, 2017 at 10:44

1 Answer 1

0

The way I ended up solving this was as follows. Remember I need to extract the localized texts from a database/remote api.

With this solution I can databind like this and all bound texts will change automatically when I change the language:

<Label Content="{Binding Path=Strings.ErrorLevelColumnHeaderLabel}"/> 

Of course the Strings object must be accessible from all datacontexts.

The strings are stored in a database table that looks like this, an ID-column and one column for each of the supported languages:

ID en da de 24 'Level' 'Niveau' 'Stufe' 

I created a UIStringsVM class which implements the INotifyPropertyChanged interface. In my example I have implemented this in a base class called Observable, which I am sure many others have as well. See this answer for details.

public class UIStringsVM : Observable { public static UIStringsVM CurrentStringsInstance; private bool stringsAreLoading = false; private Dictionary<int, string> stringDictionary; } 

The UIStringsVM class have a property for each string I need to have localized. Since the class supports the INotifyPropertyChanged interface through the base class I can then rely on changes to be reflected in the UI when ever the language changes.

Inside the UIStringsVM class the strings for the current language is stored in a Dictionary<int, string>. The reason for this is that I can use the string ID from the database to access the correct string.

Now I can use the ID inside the property Get method to return whatever string is stored for that value. So the properties will look like this:

public string ErrorLevelColumnHeaderLabel { get => this.stringDictionary[24].Replace("\\n", Environment.NewLine); private set => this.stringDictionary[24] = value; } 

The properties are never set individually so the setter could be omitted.

The constructor:

public UIStringsVM() { this.stringDictionary = new Dictionary<int, string>(); // Initialize with default values. The ! at the end makes it easier to identify missing values in the database. this.LoginButtonText = "Login!"; this.LogoutButtonText = "Logout!"; this.UserLabelFormatString = "{0} logged in!"; this.ErrorLevelColumnHeaderLabel = "Level!"; UIStringsVM.CurrentStringsInstance = this; } 

In order to load the strings I use the following method:

public async Task LoadStringsAsync(string languageCode, CancellationToken ct) { if (languageCode.Length != 2) throw new ArgumentException("languageCode must be exactly 2 characters.", nameof(languageCode)); this.StringsAreLoading = true; var client = new UIStringsClient(AppVM.BaseURL); try { var apiStrings = await client.GetByLanguageAsync(languageCode, ct); foreach (var r in apiStrings) { /* Note: this will make it impossible to overwrite a string with an empty string from the database, * thus always keeping the string defined in this class' constructor. However strings will always * have a value as defined in the constructor even if one does not exist in the database. * */ if (string.IsNullOrWhiteSpace(r.Value)) continue; this.stringDictionary[r.Key] = r.Value; } this.OnPropertyChanged((string)null); // Raise a change event for the entire object, not just a named property } finally { this.StringsAreLoading = false; } } 

I hope this helps anyone who might happen to come across this late answer. I have been running this solution for 15 months or so, and its been really great to work with.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.