Chitika

April 19, 2012

Localizable text template engine using RazorEngine

In some cases you need to use something like text template engine in your applications.
The best example is sending the email messages from an application.
Of course you can write a subject and a body for email message directly in your code:

    var subject = string.Format("Details about item ID - {0}", item.Id);
    var body = string.Format(@"Dear {0} {1},
This is a details about your item ID - {2}.
Regards.", item.FirstName, item.LastName, item.Id);

And everything is OK unless somebody ask you to change the text of  a subject or a body message.
In that case you have to change it in the code, release your application and deploy it again.
It often happens that when you already finished the deployment, you receive the new text message for a body, and based on my experience you will receive that kind of messages again and again.
Then somebody can ask you - what about localization? You need to send one message in English and another message in German. Later somebody ask about a Korean message, etc.

So, it's good example where a text template engine could help you.

In this post i will show you how to use RazorEngine for it.


Using the RazorEngine


The following command in the Package Manager console will install RazorEngine package into your ASP.NET MVC 4 WebAPI application.

PM > Install-Package RazorEngine

RazorEngine is a templating engine built upon Microsoft's Razor parsing technology. The RazorEngine allows you to use Razor syntax to build robust templates. Currently RazorEngine has integrated the vanilla Html + Code support, but it would support other markup languages in future.

So, let's start with TemplateEngine interface:

    public interface ITemplateEngine
    {
        string Parse(string template, dynamic model);
    }

This interface introduce just one method which receive a template text and a data model for a template in a parameters and send the generated text back.

Continue with realization:

    public class RazorTemplateEngine : ITemplateEngine
    {
        public string Parse(string template, dynamic model)
        {
            return Razor.Parse(template, model);
        }
    }

It's really easy, just don't forget include RazorEngine to your using block.


The localized templates service


Our template engine is ready. But we don't want to just convert one text to another using string variables.
We would like to specify the template name, the template data model and the current culture, and get the generated text back.

For that reason I will create an TemplatesService class, but let's start with an interface first:

    public interface ITemplatesService
    {
        string Parse(string templateName, dynamic model, CultureInfo cultureInfo = null);
    }

This simple interface has only one method which takes the template name, data model and current culture as a parameters and will return the generated text as well.

Realization of this simple interface is not so simple, but i will explain all the methods later.
But before I show you the realization I would like to tell you that our TemplatesService class needs to do some file system operations, e.g. read all contents of the files, check if a file exists.

I suggest to create a separate interface (and realization of course) to do all of this file system tasks. It allows you to avoid a lot of problems in unit testing, and make your services more clear to another developers.

The interface for the file system operations:

    public interface IFileSystemService
    {
        string ReadAllText(string fileName);
        bool FileExists(string fileName);
        string GetCurrentDirectory();
    }

And really simple realization:

    public class FileSystemService : IFileSystemService
    {
        public string ReadAllText(string fileName)
        {
            return File.ReadAllText(fileName);
        }

        public bool FileExists(string fileName)
        {
            return File.Exists(fileName);
        }

        public string GetCurrentDirectory()
        {
            return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        }
    }

I hope everything is clear to you in this class.

So, let's return to realization of TemplatesService. I will show you the realization from methods to methods. The constants, the properties and a constructor of the class, first:

    public class TemplatesService : ITemplatesService
    {
        private const string DefaultLanguage = "en";
        private const string TemplatesDirectoryName = "Templates";
        private const string TemplateFileNameWithCultureTemplate = "{0}.{1}.template";
        private const string TemplateFileNameWithoutCultureTemplate = "{0}.template";
        
        private readonly IFileSystemService _fileSystemService;
        private readonly ITemplateEngine _templateEngine;
        private readonly string _templatesDirectoryFullName;

        public TemplatesService(IFileSystemService fileSystemService, ITemplateEngine templateEngine)
        {
            _fileSystemService = fileSystemService;
            _templateEngine = templateEngine;
            _templatesDirectoryFullName = Path.Combine(_fileSystemService.GetCurrentDirectory(), TemplatesDirectoryName);
        }

        // rest of the code
    }

Nothing complex in this code: just declaring the four constants where I specified the default language name, the name of the directory where the templates are stored, and the string templates for the file name of template with and without a culture.
Also, I stored the full path to templates directory in the _templateDirectoryFullName property in the constructor of the class.

Then, the implementation of the one public method which declared in the interface:

        public string Parse(string templateName, dynamic model, CultureInfo cultureInfo = null)
        {
            var templateContent = GetContent(templateName, cultureInfo);

            return _templateEngine.Parse(templateContent, model);
        }

It takes the content of the template from GetContent method and call the template engine to get a string result.

        private string GetContent(string templateName, CultureInfo cultureInfo)
        {
            var templateFileName = TryGetFileName(templateName, cultureInfo);
            if (string.IsNullOrEmpty(templateFileName))
            {
                throw new FileNotFoundException(string.Format("Template file not found for template '{0}' in '{1}'", templateName, _templatesDirectoryFullName));
            }

            return _fileSystemService.ReadAllText(templateFileName);
        }

Te method GetContent tries to take the template full file name (a file name with a path) from the method TryGetFileName, and if this method return null or empty string throws an exception. Otherwise it reads all the template file content and return it.

        private string TryGetFileName(string templateName, CultureInfo cultureInfo)
        {
            var language = GetLanguageName(cultureInfo);

            // check file for current culture
            var fullFileName = GetFullFileName(templateName, language);
            if (_fileSystemService.FileExists(fullFileName))
            {
                return fullFileName;
            }

            // check file for default culture
            if (language != DefaultLanguage) 
            {
                fullFileName = GetFullFileName(templateName, DefaultLanguage);
                if (_fileSystemService.FileExists(fullFileName))
                {
                    return fullFileName;
                }
            }

            // check file without culture
            fullFileName = GetFullFileName(templateName, string.Empty);
            if (_fileSystemService.FileExists(fullFileName))
            {
                return fullFileName;
            }

            return string.Empty;
        }

This method gets the language name from CultureInfo parameters, and checks is the template file for that language exist, and if no, checks is the template file for default language exists, and if it is not found then checks is the template file without any culture exist.
For example, look at the templates files structure:

    subject.template
    subject.de.template

Let's imagine, that the current culture is German. The language is "de". The method should check the template file for this language and should found the second template 'subject.de.template'.
Then, let's imagine, that for now, the current culture is Korean. The language is "ko". The method should check the template file for this language, and will not find it, because we don't have a template file for Korean culture. Then the method should check the template file for default language which is 'en', and will not find it as well. The latest check will be for the template file without any culture and the first one template 'subject.template' should be found.

The implementation of the GetLanguage method is really simple:

        private static string GetLanguageName(CultureInfo cultureInfo)
        {
            return cultureInfo != null ? cultureInfo.TwoLetterISOLanguageName.ToLower() : DefaultLanguage;
        }

It returns the two letter ISO language name or the default language name if culture is not specified.

And the last method:

        
        private string GetFullFileName(string templateName, string language)
        {
            var fileNameTemplate = string.IsNullOrEmpty(language) ? TemplateFileNameWithoutCultureTemplate : TemplateFileNameWithCultureTemplate;

            var templateFileName = string.Format(fileNameTemplate, templateName, language);

            return Path.Combine(_templatesDirectoryFullName, templateFileName);
        }

That's all with the TemplatesService.


Using the localizable template service


To demonstrate how to use this service let's create the template file first.
Create a directory 'Templates' in your project.
Then click the right mouse button on this directory in the 'Solution Explorer' and select 'Add' -> 'New Item...' (or just press Ctrl+Shift+A).
In the new window click on 'Visual C# Items' and select the 'Text File' in the list. Enter the file name - 'first.template' and click 'Add' button.

Then insert the next text into template file:

ID: @Model.Id
Name: @Model.Name
Items:
@for(int i = 0; i < @Model.Items.Count; i++) {
    @:item #@i - @Model.Items[i]
}

Then write some simple code to parse the template:

            var model = new {
                Id = 10,
                Name = "Name1",
                Items = new List<string> {"Item1", "Item2", "Item3", "Item4", "Item5"}
            };

            var templateService = new TemplatesService(new FileSystemService(), new RazorTemplateEngine());

            var result = templateService.Parse("first", model);

After that, put the break point after the last line (var result = ...) and run your application in Debug mode.
When the debugger stops on that break point just check the value of the result variable in the Text Visualizer.

If should contains the next text:

ID: 10
Name: Name1
Items:
    item #0 - Item1
    item #1 - Item2
    item #2 - Item3
    item #3 - Item4
    item #4 - Item5

In my next post you can find how to create a localizable text template engine using StringTemplate.

That's all.
Good luck.

94 comments:

  1. I have learn several just right stuff here. Definitely
    worth bookmarking for revisiting. I wonder how much attempt you place
    to make this kind of fantastic informative website.



    My website - lotto 649 23 march 2013

    ReplyDelete
  2. it doesn't work for me ((

    Template file not found for template 'first' in 'C:\Users\UserName\AppData\Local\Temp\Temporary ASP.NET Files\root\1a3e82a9\9c2ce23\assembly\dl3\d7a75f04\34344e0f_ed70ce01\Templates'

    ReplyDelete
    Replies
    1. Try using this method instead:

      public string GetCurrentDirectory()
      {
      //return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

      return
      new System.Uri(
      System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().CodeBase)).
      LocalPath;
      }

      Delete
  3. Hi! This is a great post, really helped me a lot.

    Few things:

    - How about master layout, section and partial support? I've seen few examples on the net, but couldn't really make it work. It would be nice if you can add your thoughts here about those issues.

    - You are resolving the mapping Template Name -> Physical File really deep in the code. Problem with this is that I have a requirement to generate two templates for one email - html and text version. Maybe for future readers you should place this resolving up in the hierarchy, in the Parse method.

    Thanks!

    ReplyDelete
  4. Such as very good information promoting content are provided and more skills are improved after refer that post.For more details about oracle fusion please check our website.
    Oracle Fusion Training Institute



    ReplyDelete

  5. Its a wonderful post and very helpful, thanks for all this information. You are including better information regarding this topic in an effective way.Thank you so much

    Personal Installment Loans
    Payday Cash Advance loan
    Title Car loan
    Cash Advance Loan

    ReplyDelete
  6. Thanks for all the information, it was very helpful I really like that you are providing information.
    To find Best Training institutes can search in our Calfre.com it is very easy to find the complete details about all Training's Centers Click Here

    ReplyDelete
  7. Your article is so informative and I have cleared all of my doubts. Your way of explanation is awesome, thank you for sharing useful information.
    To find Best Training institutes can search in our Calfre.com it is very easy to find the complete details about all Training's Centers Click Here

    ReplyDelete
  8. I always enjoy reading quality articles by an individual who is obviously knowledgeable on their chosen subject. Ill be watching this post with much interest. Keep up the great work, I will be back

    Java training in Chennai | Java training in Tambaram

    Java training in Chennai | Java training in Velachery

    Java training in Chennai | Java training in Omr

    Oracle training in Chennai

    ReplyDelete
  9. It's interesting that many of the bloggers to helped clarify a few things for me as well as giving.Most of ideas can be nice content.The people to give them a good shake to get your point and across the command

    Java interview questions and answers

    Java training in Chennai | Java training institute in Chennai | Java course in Chennai

    Java training in Bangalore | Java training institute in Bangalore | Java course in Bangalore

    Java interview questions and answers

    ReplyDelete
  10. I prefer to study this kind of material. Nicely written information in this post, the quality of content is fine and the conclusion is lovely. Things are very open and intensely clear explanation of issues
    python training in chennai
    python training in chennai
    python training in bangalore

    ReplyDelete
  11. This is a nice article here with some useful tips for those who are not used-to comment that frequently. Thanks for this helpful information I agree with all points you have given to us. I will follow all of them.
    excel advanced excel training in bangalore | Devops Training in Chennai

    ReplyDelete
  12. Thanks for such a great article here. I was searching for something like this for quite a long time and at last, I’ve found it on your blog. It was definitely interesting for me to read about their market situation nowadays.pmp training centers in chennai| pmp training in velachery | project management courses in chennai |pmp training in chennai | pmp training institute in chennai

    ReplyDelete
  13. Very good information. Its very useful for me. We have a good career in java. We need learn from real time examples and for this we choose good training institute, we need to learn from experts . We need a good training institute for our learning . so people making use of the free demo classes.Many training institute provides free demo classes. One of the best training institute in Bangalore is Apponix Technologies.
    https://www.apponix.com/Java-Institute/Java-Training-Institute-in-Bangalore.html

    ReplyDelete
  14. Hi, It’s Amazing to see your blog.This provide us all the necessary information regarding
    upcoming real estate project which having all the today’s facilities.
    autocad in bhopal
    3ds max classes in bhopal
    CPCT Coaching in Bhopal
    java coaching in bhopal
    Autocad classes in bhopal
    Catia coaching in bhopal

    ReplyDelete
  15. For AWS training in Bangalore, Visit:
    AWS training in Bangalore

    ReplyDelete
  16. I really enjoy reading this article. Hope that you would do great in upcoming time.A perfect post. Thanks for sharing.aws training in bangalore

    ReplyDelete
  17. The content was very interesting, I like this post. Your explanation way is very attractive and very clear.data science training in bangalore

    ReplyDelete
  18. The Information which you provided is very much useful for Agile Training Learners. Thank You for Sharing Valuable Information.google cloud platform training in bangalore

    ReplyDelete
  19. Thanks for sharing this blog. This very important and informative blog.dot net training in bangalore

    ReplyDelete
  20. Enjoyed reading the article above, really explains everything in detail, the article is very interesting and effective. Thank you and good luck…
    Start your journey with Database Developer Training in Bangalore and get hands-on Experience with 100% Placement assistance from experts Trainers @Bangalore Training Academy Located in BTM Layout Bangalore.

    ReplyDelete
  21. Thanks for your post very userful for the readers .keep me as a bookmark for future reference .
    Rpa training in chennai | RPA training course chennai
    AI training in chennai




    ReplyDelete
  22. I can’t imagine that’s a great post. Thanks for sharing.

    Start your journey with AWS Course and get hands-on Experience with 100% Placement assistance from Expert Trainers with 8+ Years of experience @eTechno Soft Solutions Located in BTM Layout Bangalore.

    ReplyDelete
  23. I can’t imagine that’s a great post. Thanks for sharing.

    Get Best Dell Boomi Training in Bangalore from Real Time Industry Experts with 100% Placement Assistance in MNC Companies. Book your Free Demo with Softgen Infotech.

    ReplyDelete
  24. Snapdeal Winner List here came up with an Offer where you can win Snapdeal prize list 2020 by just playing a game & win prizes.
    Snapdeal winner name2020 also check the Snapdeal lucky draw2020

    ReplyDelete
  25. This post is really nice and informative. The explanation given is really comprehensive and useful... salesforce administrator training

    ReplyDelete
  26. This comment has been removed by the author.

    ReplyDelete
  27. Thanks for the informative article About Angular Js. This is one of the best resources I have found in quite some time. Nicely written and great info. I really cannot thank you enough for sharing.
    Java training in chennai | Java training in annanagar | Java training in omr | Java training in porur | Java training in tambaram | Java training in velachery

    ReplyDelete

  28. Firstly talking about the Blog it is providing the great information providing by you . Thanks for that . Next i want to share some information about Salesforce training in Banglore .

    ReplyDelete
  29. This is most informative and also this post most user friendly and super navigation to all posts. Thank you so much for giving this information to me.
    DevOps Training in Chennai

    DevOps Course in Chennai



    ReplyDelete
  30. Nice & Informative Blog !
    If you are looking for the best accounting software that can help you manage your business operations. call us at QuickBooks Phone Number 1-(855) 550-7546.

    ReplyDelete
  31. Hey!! Great work. You have a very informative blog .You are doing well. Keep it up. We will also provide Quickbooks Subscription Error to alter Quickbook’s issues. If you have any issues regarding Quickbooks dial +1-8555-756-1077 for getting instant help.

    ReplyDelete
  32. Nice Post !
    QuickBooks is the best accounting software of the current time that provides ease of use and a user-friendly interface.you may face some technical queries in this software. To get solutions for such problems, call us at QuickBooks Customer Service Number 1-(855) 550-7546.

    ReplyDelete
  33. Hey! Good blog. I was facing an error in my QuickBooks software, so I called QuickBooks Error 6123 (855)-756-1077. I was tended to by an experienced and friendly technician who helped me to get rid of that annoying issue in the least possible time.

    ReplyDelete
  34. Common Service Centre provides different types of online activities in a single point. Visit jainand Digital Point for getting best service.

    ReplyDelete
  35. Hey! Mind-blowing blog. Keep writing such beautiful blogs. In case you are struggling with issues on QuickBooks software, dial QuickBooks Support Phone Number (877)603-0806. The team, on the other end, will assist you with the best technical services.

    ReplyDelete
  36. Hey! Excellent work. Being a QuickBooks user, if you are struggling with any issue, then dial QuickBooks Phone Number (877)948-5867. Our team at QuickBooks will provide you with the best technical solutions for QuickBooks problems.

    ReplyDelete
  37. Nice Blog !
    QuickBooks is an accounting software that helps you manage and handle all the accounting operations at the same time.However, many times, you may come across errors like QuickBooks Error 1926 while working on this software.To get instant assistance for QuickBooks issues, Our team offers 24 hours of service to our clients.

    ReplyDelete
  38. Nice Blog !
    QuickBooks Error 1334 is an error that degrades the performance of your software.They utilise their whole skill and experience into resolving all the annoying issues of QuickBooks users.

    ReplyDelete
  39. Hey! What a wonderful blog. I loved your blog. QuickBooks is the best accounting software, however, it has lots of bugs like QuickBooks Error. To fix such issues, you can contact experts via QuickBooks technical support number

    ReplyDelete
  40. Great writeup! Thanks so much for putting in the work and sharing with everyone.In case if you face any techincal issue in QuickBooks, you can contact Us:

    QuickBooks Customer Service


    ReplyDelete
  41. I am glad to be here and read your very interesting article, it was very informative and helpful information for me. keep it up. Roy Batty Coat

    ReplyDelete
  42. Thank you very much for this great post.
    Tyler Ronan Jacket

    ReplyDelete
  43. Hey! Fabulous post. It is the best thing that I have read on the internet today. Moreover, if you need instant support for QuickBooks Error, visit at QuickBooks Customer Service Our team is always ready to help and support their clients.

    ReplyDelete
  44. virtual edge .This approach could translate to more ticket sales for the conference itself and The addition of a corporate meeting could mean that more employees will be travelling to the destination. business event invitation email and thank you letter for invitation to event

    ReplyDelete

  45. Hey! Mind-blowing blog. Keep writing such beautiful blogs. In case you are struggling with issues on QuickBooks software, dial QuickBooks Support Phone Number . The team, on the other end, will assist you with the best technical services.

    ReplyDelete
  46. This comment has been removed by the author.

    ReplyDelete
  47. This comment has been removed by the author.

    ReplyDelete
  48. Hey! What a wonderful blog. I loved your blog. QuickBooks is the best accounting software, however, it has lots of bugs like QuickBooks Error. To fix such issues, you can contact experts via QuickBooks Phone Number

    ReplyDelete
  49. https://www.facebook.com/Jack-russell-puppies-looking-for-a-lovely-home-103472108733880/

    ReplyDelete
  50. Hey! Lovely blog. Your blog contains all the details and information related to the topic. In case you are a QuickBooks user, here is good news for you. You may encounter any error like QuickBooks Error, visit at QuickBooks Customer Service Number for quick help.

    ReplyDelete
  51. Hi , Thank you so much for writing such an informational blog. If you are Searching for latest Jackets, Coats and Vests, for more info click on given link-Rip Wheeler Jacket

    ReplyDelete
  52. Hey! Well-written blog. It is the best thing that I have read on the internet today. Moreover, if you are looking for the solution of QuickBooks Software, visit at QuickBooks Customer Service (866)669-5068 to get your issues resolved quickly.

    ReplyDelete
  53. Hey! Nice Blog, I have been using QuickBooks for a long time. One day, I encountered QuickBooks Customer Service in my software, then I called QuickBooks Customer Service (855)963-5959. They resolved my error in the least possible time.

    ReplyDelete
  54. This was an extremely wonderful post. Thanks for providing this info. 4th Hokage Cloak

    ReplyDelete
  55. It was not first article by this author as I always found him as a talented author. walter white khaki jacket

    ReplyDelete
  56. I read this article. I think You put a lot of effort to create this article. I appreciate your work. penelope blossom coat

    ReplyDelete
  57. Hii!!
    Good content.wonderful blog. If you are searching for Quickbooks Customer Serviceyou can reach us at.+1 888-210-4052,PA.

    ReplyDelete
  58. This comment has been removed by the author.

    ReplyDelete


  59. Thank you so much providing amazing information.architect in pune

    ReplyDelete
  60. architects in pune Our aim is to work with dedication and passion and enhance the human experience.
    rehab center in westwood nj

    ReplyDelete
  61. Thank you for sharing your awesome and valuable article this is the best blog for the students they can also learn.

    ReplyDelete