Here I’d like to describe some approaches how .NET applications can be localized.
General
In general the texts that are supposed to be localized should be added to the Resources.resx file located under Properties of the project:
The data is stores in an XML format, but also type save access properties are created automatically. Don’t forget to set the Access Modifier setting of the resources file to public. Otherwise the generated properties are not accessible from within XAML or from outside the project.
To add an additional language, simply add another RESX file to the properties and name it like the original file, but add the country code to the file name like this:
Resources.de.resx – Add this to add German language support.
This will result in an additional DLL file that contains just the translated strings. The DLL has the same name as the original assembly, but is located in the sub folder „de“. Just deploy those files together with the other files of your project and you have a language support. At runtime .NET will automatically select the DLLs matching to the current locale settings.
Using Static Strings
The easiest way to support localization is to use static strings in XAML code. Here we add a static reference to the Resources property that was created when we edited the RESX file.
<Window x:Class="CMS2.Export.Views.WindowLogin" x:ClassModifier="internal" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:resx="clr-namespace:CMS.Export.Properties"> <TextBlock Text="{x:Static resx:Resources.Login}" VerticalAlignment="Center" FontSize="20" FontWeight="Bold" /> </Window>
I added a namespace named resx that refers to the Properties namespace of my assembly.
I then set the Text property of the TextBlock to the static Login property of the Resources file. This is done using the x:Static extension.
This is very easy to use. The text of the default language is used in case a localized string cannot be found.
The disadvantage of this approach is that the GUI language cannot be changed at runtime and that it will always use the current system setting as language setting.
User Defined Language Settings
If you wand to support user defined language settings you need a more sophisticated approach. This can be used for example to support different languages and to ask the user at program start, which language he wants to use.
In this approach I will use a converter to supply the translated string values for the WPF application GUI.
[ValueConversion(typeof(string), typeof(string))] public class LanguageConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null) return DependencyProperty.UnsetValue; string s = Globalization.Resources.ResourceManager.GetString(parameter.ToString()); if (string.IsNullOrWhiteSpace(s)) return DependencyProperty.UnsetValue; return s; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return DependencyProperty.UnsetValue; } }
In that example I use a separate assembly named „Globalization“ to store the localized strings. The organization of the RESX files inside of that project remains like described above.
Using the converter:
<Window x:Class="CMS2.Export.Views.WindowLogin" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:helpers="clr-namespace:Globalization.Helpers"> <Window.Resources> <helpers:LanguageConverter x:Key="LanguageConverter" /> </Window.Resources> ... <Button Content="{Binding Path=., RelativeSource={RelativeSource Mode=Self}, Converter={StaticResource LanguageConverter}, ConverterParameter=OK}" Click="buttonOK_Click" /> </Window>
The converter will be added to the resources of the window and bound to at all places where we need it. The actual reference of the binding is not used. Therefore we just set a reference to the control itself. The ConverterParameter is used to pass the key of the text to localize. The key must match the key used in the resource file. The converter will use the ResourceManager to find a matching translation.
In the binding you can also set a Fallback value that will be used in case the tranlation cannnot be found.
That approach will use the current’s thread culture settings to find the matching translation. That means you can change the language at runtime by changing the current thread’s culture:
Thread.CurrentThread.CurrentCulture = currentCulture; Thread.CurrentThread.CurrentUICulture = currentCulture;