View source code on GitHub
Once upon a time in our large application arised the problem of WPF localization. “Large” means WPF client solution that includes about 80 projects with almost 2000 XAML files. And it continues to grow with a good pace. (I’m interesting how many so large WPF solutions in the world?)
At time the problem arised our solution was not so big, but we knew it will be. At first we looked at Microsoft official way for translation https://msdn.microsoft.com/en-us/library/ms788718(v=vs.110).aspx. But it’s turned out as quite cumbersome from many points of view I wouldn’t go deep into.
It’s better to start with requirements we want our WPF localization system has to implement:
Requirements for our WPF localization and translation library
Simple using for developers and designers
It has to be almost as simple as just writing a text. For example content on Save button can be written as
1 |
<Button Content="{Res Lib_Save}"/> |
Supporting MVVM
Examples below are show this requirement in action.
Supporting enum translations
Enums are static stuff and often have static translations. Any kind of statuses: OrderStatus, CustomerStatus, ArrivalStatus; DayOfWeek etc. Supposing that we have Order in ViewModel with Status property, it would be good to write in XAML:
1 |
TextBlock Content="{ResEnum KeyPart1={ResKeyPart Order.Status}}"/> |
Easy way to provide localized enum list
For ComboBox.ItemsSource we want just to specify a Type of enum to get a list of translated items:
1 2 3 |
<ComboBox SelectedItem="{ResEnum KeyPart1={ResKeyPart Order.Status}}" ItemsSource="{ResEnumList {x:Type entities:OrderStatus}}"/> |
non-WPF application translation
We want to have same localization core for both WPF client and .NET server applications
Translate from C# code
1 |
string s = ResManager.Instance.GetResourceString("Lib_Save"); |
And for MVVM binding with changing on the fly:
1 2 3 |
Button button = new Button(); ... ResExtension.BindExtension(button, Button.ContentProperty, "Lib_Save"); |
Formatted strings
For order count in ListView it can be written:
1 2 3 4 5 |
<Label> <Res Key="MainView_OrderCount"> <ResParam ElementName="OrderGrid" Path="SelectedItems.Count" /> </Res> </Label> |
which results in “Order count: 16” string, where “16” is taken from OrderGrid.SelectedItems.Count property
Dynamic resource keys support
For example we have column in ListView whose Header can be changed dynamically:
1 |
<GridViewColumn Header="{Res KeyProvider={DynamicKeyProvider}, KeyPart1={ResKeyPart Path=SomeDynamicResKey}}"> |
Complex keys support
Depending on window context some labels(and not only labels) can be represented with different strings even for the same language. As example lets look at the case in advertisting. Suppose the same window used for TV, Radio and Internet medias. User have the ability to select from these three options in combobox. Depending on this selection other labels can change. For example the label which displays advertising place can be “TV Channel” when “TV” choosen in combobox, “Radiostation” for “Radio” and “Site” for “Internet“. For demonstration purposes lets go further and imagine that user can select from “Single“, “Multiple” values in other combobox and label have to change to “TV Channels“, “Radiostations“, “Sites“. So we get to the point where translation depends on three values: Language, MediaType, Multiplicity. How can we write this in XAML:
1 2 3 4 5 6 7 8 |
<TextBlock> <TextBlock.Text> <Res Key="AdPlace"> <ResKeyPart Path="SelectedMediaType" /> <ResKeyPart Path="SelectedMultiplicity" /> </Res> </TextBlock.Text> </TextBlock> |
where SelectedMediaType and SelectedMultiplicity are properties in ViewModel to which comboboxes are bound to. Language changes on the application level.
Complex cases
Complex keys with formatted strings. Lets imagine previous case returns formatted string with two placeholders:
1 2 3 4 5 6 7 8 9 10 |
<TextBlock> <TextBlock.Text> <Res Key="AdPlace"> <ResKeyPart Path="SelectedMediaType" /> <ResKeyPart Path="SelectedMultiplicity" /> <ResParam Path="SomePropertyInVM" /> <ResParam Path="SomePropertyInVM2" /> </Res> </TextBlock.Text> </TextBlock> |
Change on the fly
Yes, we have to change on the fly all localized objects when application language is changed. Even those objects that we translated via C# code. And provide support for many languages.
Easy-to-use resource editor
It have to be easy to use resource management tool for developers, designers and interpreters.
Summary and download
So here are the requirements for our translation system. In the next articles I will describe in details how we achieve all these goals. For those who not interested in details and want to use translation library immediately I suggest to use the code from GitHub:
Source code on GitHub
and integrate into your application.
Some remarks about integration. There are two significant projects Common.Res and Common.Res.Wpf. They are separated WPF and non-WPF parts of translation library. For using the same core both in server parts of applications or in non-WPF applications.
You can use library as dll’s or integrate it as code into your solution.
Trackbacks/Pingbacks