Sunday, June 6, 2010

Resource Generator Tool

In my latest work, I have to implement the capability to have support for more than one language. Microsoft has come up with few tricks for doing that, but I devise my own logic to implement the same. As languages are actually string data which I want to present on the WPF applications, we can easily introduce individual resource files for data related to a particular Language.

Now when the program loads, we can easily go on and add the one that I need to add for the current CultureInfo. I have introduced the same in an article :
Simplest Way to implement Multilingual Application

Later on in my article Pluggable Resource for WPF ResourceDictionary, I have introduced the way by which you could plug - in the Resources into external assemblies.

So to summarize, whenever I need to use a string, I will need to modify the string resource with an appropriate key in the resource file. We need to create new Resource files for each language and also need to map each key with appropriate data.
So instead of Text="My Custom text", I will write something like Text="{DynamicResource rKeyCustomText}" so that the rKeyCustomText will be replaced with the appropriate text during runtime of the project and thus we can maintain language based string resources. To do this, we need to create a schema like :

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:system="clr-namespace:System;assembly=mscorlib">
    <system:String x:Key="rKeyCustomText" >This is my custom Text</system:String>
</ResourceDictionary>

For french we need to recreate the same as :
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
                    xmlns:system="clr-namespace:System;assembly=mscorlib">
  <system:String x:Key="rKeyCustomText">C'est mon texte personnalisé</system:String>
</ResourceDictionary>

So the problem is once we declare a resource key we need to instantly convert the string to all the resource files that we need to support for the application and write that. So the work seems to me very boring and laborious. So I thought to build a tool to help you in this regard. Lets talk about how you can use this tool to build your Resource files automatically.




To build this application the technologies that I have used are :
  1. .NET (of  course)
  2. WPF for UI
  3. Bing Services for Translator
  4. Threads to handle bankground work
So the UI looks simple, just have two textboxes to show the Target nad Destination files. Lets see the steps to work with it :
Step 1 : Create a file for resource exactly with the same schema that I specified. You can insert system:strings of any language.
Step 2: Open the Console and choose the File you have just created.
Step 3: Create a file using Destination button.
Step 4: Choose the appropriate Source and target Language.
Step 5 : Click on Convert to generate the file.

Thus you can see the file will be generated with same schema.

The Implementation 
The implementation is very simple. I took the help of LINQ classes for XML to generate the output. Lets look into the code:


public void CreateResourceDictionary(string targetfile, string destinationfile, DoWorkEventArgs e)
        {

            DateTime startTime = DateTime.Now;
            e.Result = "";

            XDocument document = XDocument.Load(targetfile);
            oWorker.ReportProgress(2, "Initializing...");
            foreach (XElement elem in document.Descendants("{clr-namespace:System;assembly=mscorlib}String"))
            {
                XAttribute attribute = elem.Attribute("{http://schemas.microsoft.com/winfx/2006/xaml}Key");
                elememnts.Add(attribute.Value, elem.Value);
            }

            oWorker.ReportProgress(4, "Target File Read Successfully.");

            XDocument targetDocument = null;
            XNamespace xaml = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";
            XNamespace x = "http://schemas.microsoft.com/winfx/2006/xaml";
            XNamespace system = "clr-namespace:System;assembly=mscorlib";
            XElement root = new XElement(xaml + "ResourceDictionary",
                                        new XAttribute("xmlns", "http://schemas.microsoft.com/winfx/2006/xaml/presentation"),
                                        new XAttribute(XNamespace.Xmlns + "x", "http://schemas.microsoft.com/winfx/2006/xaml"),
                                        new XAttribute(XNamespace.Xmlns + "system", "clr-namespace:System;assembly=mscorlib"));

            oWorker.ReportProgress(5, "Creating Target File.");
            int elements = elememnts.Count;
            int i = 1;
            foreach (string key in elememnts.Keys)
            {
                try
                {
                    using (LiveSearchPortTypeClient client = new LiveSearchPortTypeClient())
                    {
                        string sCode = this.Currentsource.Code;
                        string tCode = this.CurrentTarget.Code;
                        SearchResponse response = client.Search(BuildRequest(elememnts[key], sCode, tCode));
                        if (response.Translation.Results.Count() > 0)
                        {
                            string item = response.Translation.Results[0].TranslatedTerm;
                            XElement element = new XElement(system + "String", item);
                            element.Add(new XAttribute(x + "Key", key));
                            root.Add(element);
                            int percentage = ((i * 90)/elements) + 5;
                            oWorker.ReportProgress(percentage, string.Format("string {0} is converted as {1}", elememnts[key], item));

                            //Cancel the WORKER
                            if (oWorker.CancellationPending)
                            {
                                e.Cancel = true;
                                break;
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    // An exception occurred while accessing the network.
                    oWorker.ReportProgress(((i * 90) / elements) + 5, ex.Message);
                }
                i = i+1;

            }
            targetDocument = targetDocument ?? new XDocument();
            targetDocument.Add(root);
            targetDocument.Save(destinationfile);
            oWorker.ReportProgress(100, "Target file created Successfully");
            TimeSpan span = DateTime.Now - startTime;
            e.Result = string.Concat(span.TotalSeconds, " seconds");
            
        }

        public static SearchRequest BuildRequest(string query, string sCode, string tCode)
        {
            SearchRequest request = new SearchRequest();

            request.AppId = "3382CF24D27D0A095C7C4945EA17FDD8E2946C73";
            request.Query = query;
            request.Sources = new SourceType[] { SourceType.Translation };

            request.Translation = new TranslationRequest();
            
            request.Translation.SourceLanguage = sCode;
            request.Translation.TargetLanguage = tCode;

            request.Version = "2.2";

            return request;
        }
You can see the BuildRequest actually creates a SearchRequest object, which is used to invoke Bing translator. The SourceLanguage and TargetLanguage will enable you to specify the cultures.

The Search Request will then be invoked using LiveSearchPortTypeClient object. The call client.Search(Request) takes the request object and returns the result. The probable results will be found on the collection response.Translation.Results.

You can see we made a logic around this using XDocument objects to generate the same output that we need for the Resource.

I hope this would be helpful to you.

To Download the source code of the tool just click on the link
Download the Source files

Thank you for reading.

1 comment:

  1. Thanks for this great article.
    The source code file is damaged or not available, could you please reupload it?

    ReplyDelete

Please make sure that the question you ask is somehow related to the post you choose. Otherwise you post your general question in Forum section.

Author's new book

Abhishek authored one of the best selling book of .NET. It covers ASP.NET, WPF, Windows 8, Threading, Memory Management, Internals, Visual Studio, HTML5, JQuery and many more...
Grab it now !!!