Click here to Skip to main content
Click here to Skip to main content

Globalization in WPF using ResourceDictionary

, 31 Jan 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
Multilingual application using ResourceDictionary in WPF.

Introduction

This article introduces how to run a WPF application in difference languages. We will go through how to create a multilingual application. Sometimes question arises as to how to create a single version of an application that can be used in multiple languages (different culture). ResourceDictionary comes in place, and plays a role in running WPF applications in different languages.

Background 

ResourceDictionary is based on XML and takes advantage of the globalization support defined in the XML specification. We can create multiple resource files for each language and add at the root level (App.xaml) to implement in a whole application.

Using the code 

Creating resources

  1. Right click on the WPF project and select Add New Item, and select UserControl from list of items in the opened dialog.
  2. Convert UserControl to ResourceDictionary.
  3. A question arises in our mind that why do we need to add a UserControl and convert it into a ResourceDictionary instead of adding a ResourceDictionary directly.

    The answer is we are going to use MEF (Import/Export) classes in the next step.

  4. Give a proper name to the ResourceDictionary page based on the language, for example: EnglishLanguage.xaml, and write a string resource like this:
  5. <ResourceDictionary x:Class="WPF_Globalization.Resources.EnglishLanguage"
                        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                        xmlns:s="clr-namespace:System;assembly=mscorlib"
                        mc:Ignorable="d">
        <s:String x:Key="keyEmployeeName">Employee Name:</s:String>
        <s:String x:Key="keyAddress">Address:</s:String>
        <s:String x:Key="keyCountry">Country:</s:String>
        <s:String x:Key="keyState">State:</s:String>
        <s:String x:Key="keyCity">City:</s:String>
        <s:String x:Key="keyPhone">Phone Number:</s:String>
        <s:String x:Key="keyDesignation">Designation:</s:String>
    </ResourceDictionary></ResourceDictionary>

    x:key for strings in above code is a unique name, it used to identify a string resource.

  6. Using a string resource in the application:
  7. <TextBlock Grid.Row="0"
        Grid.Column="0"
        Text="{DynamicResource keyEmployeeName}" />

    To use global file resources you have to set DynamicResource for the local file resource you need to set with StaticResource.

  8. In this application I have created a demo for English and French languages, you can create more resource files based on your requirements.
  9. The same way, we add a ReourceDictionary file for other languages, for example, FrenchLanguage.xaml, like this:

    <ResourceDictionary x:Class="WPF_Globalization.Resources.FrenchLanguage"
                        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                        xmlns:s="clr-namespace:System;assembly=mscorlib"
                        mc:Ignorable="d">
        <s:String x:Key="keyEmployeeName">Nom de l'employé:</s:String>
        <s:String x:Key="keyAddress">adresse:</s:String>
        <s:String x:Key="keyCountry">pays:</s:String>
        <s:String x:Key="keyState">état:</s:String>
        <s:String x:Key="keyCity">ville:</s:String>
        <s:String x:Key="keyPhone">Numéro de téléphone:</s:String>
        <s:String x:Key="keyDesignation">désignation:</s:String>
    </ResourceDictionary>

    Using MEF

    Microsoft .NET Framework has a System.ComponentModel.Composition namespace which provides classes that constitute the core of MEF (Manage Extensibility Framework). For more details, visit MSDN.

    What is MEF?

    The Managed Extensibility Framework (MEF) is a composition layer for .NET that improves the flexibility, maintainability, and testability of large applications.

    It allows application developers to discover and use extensions with no configuration required. By using MEF, developers can easily encapsulate code and avoid fragile hard dependencies.

    Features of MEF

    MEF components (classes, methods, properties) specify both its dependencies (Imports) and capabilities (Exports) that are discovered by the runtime. When an object is created, the MEF composition engine satisfies its imports with what is available from other objects. It provides exports, an object that satisfies imports.

    There are a list of attributes available in MEF, apart from them I have used the following attributes in this application:

    • ExportAttribute
    • ImportAttribute
    • ImportManyAttribute

    ImportAttribute specifies that a property, field, or parameter should be populated with matching exports. It will import a list of operations. ExportAttribute specifies that a type, property, field, or method provides a particular export. Any export declared with a matching contract will fulfill this import.

  10. Open the EnglishLanguage.xaml.cs file and write the following to export this class (resource):
  11. [ExportMetadata("Culture", "en-US")]
    [Export(typeof(ResourceDictionary))]
    public partial class EnglishLanguage : ResourceDictionary
    {
        public EnglishLanguage()
        {
            InitializeComponent();
        }
    }

    ExportMetadata specifies the metadata for a type (or we can say it will attach metadata) in a key-value pair that will implement the operation.

  12. The same way attach metadata in the FrenchLanguage class.
  13. Now, create a class that has a property to import all exported classes, like this:
  14. public class ImportModule
    {
        [ImportMany(typeof(ResourceDictionary))]
        public IEnumerable<Lazy<ResourceDictionary, IDictionary<string, 
               object>>> ResourceDictionaryList { get; set; }
    }

    The above code snippet imports all classes from different assemblies that have the matching type ResourceDictionary.

    Composition Container: it is the core of MEF. It is used to discover parts (objects) by using a composable part catalog. A catalog can be any given type from hosting (like DirectoryCatalog, AssemblyCatalog, AggregateCatalog, etc.).

  15. Create a Singleton class and add a property for the ImportModule class.
  16. public class BaseModel 
    {
        private static BaseModel _instance;
        public static BaseModel Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new BaseModel();
                return _instance;
                }
        }
    
        private  ImportModule _importCatalog;
        public ImportModule ImportCatalog
        {
            get
            {
                _importCatalog = _importCatalog ?? new ImportModule();
                return _importCatalog;
            }
        }        
    }
  17. In the App.xaml.cs file write the following code in the OnStartup event to import all classes using the Catalog.
  18. string path = AppDomain.CurrentDomain.BaseDirectory;            
    DirectoryCatalog catalog = new DirectoryCatalog(path);
    CompositionContainer container = new CompositionContainer(catalog);
    container.ComposeParts(BaseModel.Instance.ImportCatalog);
  19. Create a class for the language that has Code and Name properties for binding.
  20. public class Languages
    {
        public string Code { get; set; }
        public string Name { get; set; }
    }
  21. Create a property in the ViewModel that has a list of languages.
  22. private List<Languages> _languageList;
    public List<Languages> LanguageList
    {
        get { return _languageList; }
        set
        {
            _languageList = value;
            RaisePropertyChanged("LanguageList");
        }
    }
    
    LanguageList = new List<Languages>();
    LanguageList.Add(new Languages() { Code = "en-US", Name = "English" });
    LanguageList.Add(new Languages() { Code = "fr-FR", Name = "French" });
  23. Add a ComoBox in the Usercontrol for changing the language and bind the languages from the ViewModel class, like this:
  24. <ComboBox x:Name="LanguageComboBox"
        Width="150"
        Margin="5"
        HorizontalAlignment="Left"
        DisplayMemberPath="Name"
        ItemsSource="{Binding LanguageList}"
        SelectedItem="{Binding SelectedLanguage, Mode=TwoWay}"
        SelectionChanged="LanguageComboBox_SelectionChanged" />
  25. Write code to apply the selected language resource in the application on change of the selected language from the combobox.
  26. private void LanguageComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var currentResourceDictionary = (from d in BaseModel.Instance.ImportCatalog.ResourceDictionaryList
                    where d.Metadata.ContainsKey("Culture")
                    && d.Metadata["Culture"].ToString().Equals(vm.SelectedLanguage.Code)
                    select d).FirstOrDefault();
        if (currentResourceDictionary != null)
        {
            Application.Current.Resources.MergedDictionaries.Add(currentResourceDictionary.Value);
            CultureInfo cultureInfo = new CultureInfo(vm.SelectedLanguage.Code);
            Thread.CurrentThread.CurrentCulture = cultureInfo;
            Thread.CurrentThread.CurrentUICulture = cultureInfo;
            Application.Current.MainWindow.Language = XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag);
         }
    }

    On change of the selected language, first find out the ResourceDictionary of the selected language culture from ResourceDictionaryList. Then add the Selected Resource into Application Resources and set CurrentCulture as the selected language culture. Last, set the application main window language using the System.Windows.Markup.XmlLanguage.GetLanguage method.

    That's it.

Points of interest 

This way developers can create a ResourceDictionary for each language and set the resources for the whole application using MEF.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Hiren Khirsaria
Software Developer (Senior)
India India
has Total 5+ years of Experience in Microsoft.Net Environment. out of that,
 
2 + year of Excperience in .Net Development with C# and SQL Server 2005/2008.
 
3 + years of Experience in WPF/Silverlight.
 
Current area of Development in Mono for Android (Xamarin), Silverlight/ WPF and Windows Phone 7 Application.
 
Follow him on : http://hirenkhirsaria.blogspot.com/

Comments and Discussions

 
QuestionA Suggestion PinprofessionalEd Gadziemski11-Nov-14 5:57 
SuggestionSystem.Windows.ResourceDictionary Warning: 9 : Resource not found; ResourceKey='-whatEverKey-' Pinmemberjirk1591596-Sep-14 1:55 
QuestionQuery PinmemberMember 812999625-Jul-14 4:12 
QuestionResourceDictionaryList is always NULL Pinmemberstreezer25-May-14 23:18 
AnswerRe: ResourceDictionaryList is always NULL PinmemberHiren Khirsaria27-May-14 7:15 
QuestionVery similar to this project: https://globalizer.codeplex.com/ Pinmemberrhyous3-Mar-14 13:08 
SuggestionAccesing the DynamicResource from within the code Pinmemberkribo28-Oct-13 7:22 
GeneralMy vote Pinmemberkribo28-Oct-13 7:16 
GeneralMy vote of 5 PinmemberShmuel Zang31-Jan-13 20:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.1411023.1 | Last Updated 31 Jan 2013
Article Copyright 2013 by Hiren Khirsaria
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid