Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asp.net Mvc, Razor and Localization

I know this matter has already been brought on these pages many times, but still I haven't found the "good solution" I am required to find. Let's start the explanation.

Localization in .net, and in mvc, is made in 2 ways that can even be mixed together:

  • Resource files (both local or global)
  • Localized views with a viewengine to call the appropriate view based on culture

I'll explain the solutions I tried and all the problems I got with every one of them.

Text in resource files, all tags in the view

This solution would have me put every text in resources, and every tag in the view, even the inline tags such as [strong] or [span].

Pros:

  • Clean separation, no structure whatsoever in localization.
  • Easy encoding: everything that is returned from the resource gets html encoded.

Cons:

  • If I have a paragraph with some strongs, a couple of link etc I have to split it in many resource keys. This is considered to make the view too unreadable and also takes too much time to create it.
  • For the same reason as above, if in two different languages the [strong] text is in different places (like "Il cane di Marco" and "Marcos's dog"), I can't achieve it, since all my tags are in the view.

Text and inline tags in resource files, through parameters

This method will have the resources contain some placeholders for string.Format, and those placeholders will be filled with inline tags post-encoding.

Pros:

  • Clean separation, with just placeholders in the text, so if I am ever to replace [strong] with [em] I do it in the view where I pass it as parameter and it gets changed in every language

Cons:

  • Encoding is a bit harder, I have to pre-encode the value from the resource, then use string.Format, and finally return it as MvcHtmlString to tell the view engine to not re-encode it when displaying.
  • For the same reason as above, including, for instance, an ActionLink as parameter would be troublesome. Let's say I get the text for my actionlink from a resource. My method already encodes it. But then, the ActionLink method would re-encode it again. I would need a distinct method to get resources without encoding them, or new helper methods that get an MvcHtmlString instead of a string as text parameter, but both are rather unpractical.
  • Still takes a whole lot of time to build views, having to create all the resource keys and then fill them.

Localized views

Pros:

  • All views are plain html. No resources to read.

Cons:

  • Duplicated html everywhere. I don't even need to explain how this is totally evil.
  • Have to manually encode all troublesome characters like grave vowels, quotes and such.

Conclusions

A mix of the above techinques inherits pros and cons, but it's still no good. I am challenged to find a proper productive solution, while all of the above are considered "unpractical" and "time consuming".

To make things worse, I found out that there isn't a single tool that refactors "text" from aspx or cshtml (or even html) views/pages into resources. All the tools out there can refactor System.String instances in code files (.cs or .vb) into resources only (resharper for instance, and a couple of others I can't remember now).

So I'm stuck, can't find anything appropriate on my own, and can't find anything on the web either. Is it possible noone else got challenged with this problem before and found a solution?

like image 549
Matteo Mosca Avatar asked Mar 18 '11 17:03

Matteo Mosca


2 Answers

I personally like the idea of storing inline tags in the resource file. However I do it a little differently. I store very plain tags like <span class='emphasis'>dog</span> and then I use CSS to style the tags appropriately. Now, instead of "passing in" a tag as a parameter, I simply style the span.emphasis rule in my CSS appropriately. Change carries over to all languages.


The Sexier Option:

Another option I thought of and quite enjoy is to use a "readable markup" language like StackOverflow's very own MarkdownSharp. This way you aren't storing any HTML in the resource file, only markdown text. So in your resource you would have **dog** and then it gets shunted through markdown in the view (I created a helper for this, (Usage: Html.Markdown(string text)). Now you're not storing tags, you're storing a common human readable markup language. The markdownsharp source is one .CS file and it's easy to modify. So you could always change the way it renders the ending HTML. This gives you total control over all your resources without storing HTML, and without duplicating views or chunks of HTML.

EDIT

This also gives you control over the encoding. You could easily make sure the content of your resource files contain no valid HTML. Markdown syntax (as you know from using stack overflow) does not contain HTML tags and thus can be encoded without harm. Then you just use your helper to convert the Markdown syntax to valid HTML.

EDIT #2

There is one bug in markdown that I had to fix myself. Anything markdown detects is to be rendered as a "code" block will be HTML encoded. This is a problem if you have already HTML encoded all content being passed to markdown as anything in the code blocks will be essentially re-encoded which turns &gt; into &amp;gt; and completely screws up the text within code blocks. To fix this I modified the markdown.cs file to include a boolean option that stops markdown from encoding text within code blocks. See this issue for the fixed .cs file that I added to the MarkdownSharp project issues.

EDIT #3 - Html Helper Sample

public static class HtmlHelpers
{
    public static MvcHtmlString Markdown(this HtmlHelper helper, string text)
    {
        var markdown = new MarkdownSharp.Markdown
        {
            AutoHyperlink = true,
            EncodeCodeBlocks = false, // This option is my custom option to stop the code block encoding problem.
            LinkEmails = true,
            EncodeProblemUrlCharacters = true
        };
        string html = markdown.Transform(markdownText);
        return MvcHtmlString.Create(html);
    }
}
like image 168
Chev Avatar answered Sep 28 '22 21:09

Chev


Nothing stops you from storing HTML in resource files, then calling @Html.Raw(MyResources.Resource).

like image 25
Max Toro Avatar answered Sep 28 '22 21:09

Max Toro