In my current project, I needed a localization feature where users can switch languages at run time. After googling for several hours, I found an article that was close to my requirements except that it didn't support the design time. Another solution had design time support but it was far more complex. So I decided to develop my own solution by extending one of them that was closer to my requirements.
Please note, this is not purely my idea. I just extended it to fit my requirements and I wanted to share this.
My requirements are.
- Application UI uses the language of the operating system by default.
- User can change the language at run time.
- The application supports design time, so the developer can see the default words while developing.
- Localized words be separated across multiple files. Each window (page) uses one file for easy development and maintenance.
Let’s begin.
- Create a WPF application and name it “DynamicLocalization”. You can change the name to anything you want.
![]()
- Create a new folder in application root directory and name it “i18N”.
![]()
- Create a new "Resource Dictionary" file in the folder i18N, and name it “MainWindow.en-US.xaml”.
![]()
The name is important as we will search for this file using a certain pattern, as following.
- MainWindow is the name of the class that we will implement for localization. We will create separate files for each language and window or page.
- en-US is culture info string code for English US.
- Change the properties of the file as follows.
![]()
Please note that we use blank for property “Custom Tool”.
- In the file MainWindow.en-US.xaml, replace the following line,
![]()
with the the following line.
![]()
- Add the following string resource as identifier, so we can easily manage resources.
![]()
- Add required string resources as much as you need. In my example, I created three resource strings.
![]()
- Copy “MainWindow.en-US.xaml” to another file as much as you need, rename it according to culture string code, and change the value. Here, I copied and renamed it to “MainWindow.id-ID.xaml” for Indonesian translation. Do not forget to set its property as above.
![]()
- Next, we embed a Windows resource in MainWindow.xaml, so that we see the word at design time. Add the following code to “ManWindow.xaml”.
![]()
- Design a form example to demonstrate the dynamic localization. First, we update the WindowTitle to use the string from resource dictionary we assigned above.
![]()
Then, add the following code to MainWindow.xaml between Grid declaration.
And, add the following code to code-behind (MainWindow.xaml.cs).
Now, you can test and run the project. You will see the result as follow. Until now, we have not implemented the dynamic localization.
- Next, create a class, name the file as “LocUtil.cs”, and add the following code to it.
- using Microsoft.Win32;
- using System;
- using System.Globalization;
- using System.IO;
- using System.Threading;
- using System.Windows;
- namespace DynamicLocalization {
- public static class LocUtil {
-
-
-
-
-
- private static string getAppName(FrameworkElement element) {
- var elType = element.GetType().ToString();
- var elNames = elType.Split('.');
- return elNames[0];
- }
-
-
-
-
-
- private static string getElementName(FrameworkElement element) {
- var elType = element.GetType().ToString();
- var elNames = elType.Split('.');
- var elName = "";
- if (elNames.Length >= 2) elName = elNames[elNames.Length - 1];
- return elName;
- }
-
-
-
-
-
-
- public static string GetCurrentCultureName(FrameworkElement element) {
- RegistryKey curLocInfo = Registry.CurrentUser.OpenSubKey("GsmLib" + @ "\" + getAppName(element), false);
- var cultureName = CultureInfo.CurrentUICulture.Name;
- if (curLocInfo != null) {
- cultureName = curLocInfo.GetValue(getElementName(element) + ".localization", "en-US").ToString();
- }
- return cultureName;
- }
-
-
-
-
-
- public static void SetDefaultLanguage(FrameworkElement element) {
- SetLanguageResourceDictionary(element, GetLocXAMLFilePath(getElementName(element), GetCurrentCultureName(element)));
- }
-
-
-
- public static void SwitchLanguage(FrameworkElement element, string inFiveCharLang) {
- Thread.CurrentThread.CurrentUICulture = new CultureInfo(inFiveCharLang);
- SetLanguageResourceDictionary(element, GetLocXAMLFilePath(getElementName(element), inFiveCharLang));
-
- RegistryKey UserPrefs = Registry.CurrentUser.OpenSubKey("GsmLib" + @ "\" + getAppName(element), true);
- if (UserPrefs == null) {
-
- RegistryKey newKey = Registry.CurrentUser.CreateSubKey("GsmLib");
- UserPrefs = newKey.CreateSubKey(getAppName(element));
- }
- UserPrefs.SetValue(getElementName(element) + ".localization", inFiveCharLang);
- }
-
-
-
-
-
- public static string GetLocXAMLFilePath(string element, string inFiveCharLang) {
- string locXamlFile = element + "." + inFiveCharLang + ".xaml";
- string directory = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
- return Path.Combine(directory, "i18N", locXamlFile);
- }
-
-
-
-
-
- private static void SetLanguageResourceDictionary(FrameworkElement element, String inFile) {
- if (File.Exists(inFile)) {
-
- var languageDictionary = new ResourceDictionary();
- languageDictionary.Source = new Uri(inFile);
-
- int langDictId = -1;
- for (int i = 0; i < element.Resources.MergedDictionaries.Count; i++) {
- var md = element.Resources.MergedDictionaries[i];
-
-
- if (md.Contains("ResourceDictionaryName")) {
- if (md["ResourceDictionaryName"].ToString().StartsWith("Loc-")) {
- langDictId = i;
- break;
- }
- }
- }
- if (langDictId == -1) {
-
- element.Resources.MergedDictionaries.Add(languageDictionary);
- } else {
-
- element.Resources.MergedDictionaries[langDictId] = languageDictionary;
- }
- } else {
- MessageBox.Show("'" + inFile + "' not found.");
- }
- }
- }
- }
- Next, update the MainWindow constructor to set the language to previously saved setting (if any); otherwise. set it to OS language.
![]()
- And add the following code to the MenuItem_Click method.
![]()
Now, you can run the application and test dynamic localization using Resource Dictionary. I hope, this article will help someone who is looking for such a solution.