Click here to Skip to main content
15,867,568 members
Articles / Desktop Programming / WPF

Globalization in WPF using ResourceDictionary

Rate me:
Please Sign up or sign in to vote.
5.00/5 (15 votes)
31 Jan 2013CPOL4 min read 71.1K   2.7K   38   17
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.

Image 1

Image 2

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. XML
    <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. XML
    <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:

    XML
    <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. C#
    [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. C#
    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. C#
    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. C#
    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. C#
    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. C#
    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. XML
    <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. C#
    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)


Written By
Team Leader Reputed IT Company
India India
Having 9+ years of experience in Microsoft.Net Technology.
Experience in developing applications on Microsoft .NET Platform ( Asp.Net, WPF, Silverlight, Windows Phone 7/8).
Experience and knowledge of software design methodologies (Agile), object oriented design, and software design patterns (MVVM).
Experience in Developing android mobile application using Xamarin (mono for android) framework.

http://hirenkhirsaria.blogspot.com/

Comments and Discussions

 
AnswerCareful! Fake thread culture! There is no globalization here! Pin
Sergey Alexandrovich Kryukov6-Sep-23 16:45
mvaSergey Alexandrovich Kryukov6-Sep-23 16:45 
QuestionSatellite assemblies? Pin
Sergey Alexandrovich Kryukov6-Sep-23 4:49
mvaSergey Alexandrovich Kryukov6-Sep-23 4:49 
QuestionA Suggestion Pin
Ed Gadziemski11-Nov-14 4:57
professionalEd Gadziemski11-Nov-14 4:57 
MEF requires .NET 4.0 or higher. WPF works from 3.0 onwards. It would be helpful to identify the .NET version requirements in your article tags and in the introduction.

Other than that, a very helpful article and source code.
AnswerNo. There is no globalization here — look thoroughly. Pin
Sergey Alexandrovich Kryukov6-Sep-23 16:54
mvaSergey Alexandrovich Kryukov6-Sep-23 16:54 
SuggestionSystem.Windows.ResourceDictionary Warning: 9 : Resource not found; ResourceKey='-whatEverKey-' Pin
jirk1591596-Sep-14 0:55
jirk1591596-Sep-14 0:55 
AnswerHow it is related go globalization? Pin
Sergey Alexandrovich Kryukov6-Sep-23 16:53
mvaSergey Alexandrovich Kryukov6-Sep-23 16:53 
QuestionQuery Pin
Member 812999625-Jul-14 3:12
Member 812999625-Jul-14 3:12 
AnswerRe: Query Pin
Member 1297067930-Jan-17 4:04
Member 1297067930-Jan-17 4:04 
QuestionResourceDictionaryList is always NULL Pin
streezer25-May-14 22:18
streezer25-May-14 22:18 
AnswerRe: ResourceDictionaryList is always NULL Pin
Hiren Khirsaria27-May-14 6:15
professionalHiren Khirsaria27-May-14 6:15 
AnswerIt is fixed, but... Pin
Sergey Alexandrovich Kryukov6-Sep-23 16:52
mvaSergey Alexandrovich Kryukov6-Sep-23 16:52 
QuestionVery similar to this project: https://globalizer.codeplex.com/ Pin
rhyous3-Mar-14 12:08
rhyous3-Mar-14 12:08 
SuggestionAccesing the DynamicResource from within the code Pin
kribo28-Oct-13 6:22
professionalkribo28-Oct-13 6:22 
GeneralMy vote Pin
kribo28-Oct-13 6:16
professionalkribo28-Oct-13 6:16 
AnswerRe: My vote Pin
Sergey Alexandrovich Kryukov6-Sep-23 16:50
mvaSergey Alexandrovich Kryukov6-Sep-23 16:50 
GeneralMy vote of 5 Pin
Shmuel Zang31-Jan-13 19:48
Shmuel Zang31-Jan-13 19:48 
AnswerYou missed it Pin
Sergey Alexandrovich Kryukov6-Sep-23 16:50
mvaSergey Alexandrovich Kryukov6-Sep-23 16:50 

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

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