DucDigital for ( $girl = 1; $girl < $required; $girl++ ) { echo “I love DucDigital”; }

10Jan/102

Noob guide to Globalization in Asp.net MVC

Yea. Finally, I've done the Globalization after google and code for 4 hours, it's a pain because there are really (as of when I search) no specific post for this area. So basically in this post, i will gather everything I've done in my Globalization / Localization / Internationalization (whatever you called it is) and post here.

First step, we need to make a folder name: "App_GlobalResources", which you can create by right click your project, Add -> Add ASP.NET Folder -> App_GlobalResources

After you created the folder, we need to create Resource Files in this folder for the language. Basically, you can see the picture below have 3 files:

  1. Language.resx -> this is the default of the language, all the default translation must be in here.
  2. Language.en.resx -> this is the English file. It will override the default translations
  3. Language.vi.resx -> Vietnamese translation, used UTF-8 in here on an UTF-8 website

This is the Language.resx, which I just have to put the name because it actually doesn't matter, but if for precaution, you can always duplicate one of your language file and replace the Language.resx with it so you don't have to worry.

And here is the picture showing the file Language.en.resx. Which I have the Name and the Value

As you notice the {0} and {1} above, I will explain in the later part.

2nd, we need now is to create a new folder to contain our HtmlExtension and use this to output text string.
In my case, I created a Folder name Helper in my Project root.

Notice: these files are not mine, they are code from, i have modified a little from this source: http://blog.eworldui.net/post/2008/10/ASPNET-MVC-Simplified-Localization-via-ViewEngines.aspx . If ever you need to explain further about WebFormViewEngine or something, you can always go to their website. In my case, to make it as simple as possible, i will cut down this part.

LocalizationWebFormView.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using System.IO;
using System.Web.Mvc;
 
namespace Project.Helper.Localization
{
    public class LocalizationWebFormView : WebFormView
    {
        internal const string ViewPathKey = "__ViewPath__";
 
        public LocalizationWebFormView(string viewPath) : base(viewPath)
        {
        }
 
        public LocalizationWebFormView(string viewPath, string masterPath) : base(viewPath, masterPath)
        {
        }
 
        public override void Render(ViewContext viewContext, TextWriter writer)
        {
            // there seems to be a bug with RenderPartial tainting the page's view data
            // so we should capture the current view path, and revert back after rendering
            string originalViewPath = (string) viewContext.ViewData[ViewPathKey];
 
            viewContext.ViewData[ViewPathKey] = ViewPath;
            base.Render(viewContext, writer);
 
            viewContext.ViewData[ViewPathKey] = originalViewPath;
        }
    }
}

LocalizationWebFormViewEngine.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System.Web.Mvc;
 
namespace Project.Helper.Localization
{
    public class LocalizationWebFormViewEngine : WebFormViewEngine
    {
        protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
        {
            return new LocalizationWebFormView(viewPath, masterPath);
        }
 
        protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
        {
            return new LocalizationWebFormView(partialPath, null);
        }
    }
}

LocalizationWebFormViewEngine.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System.Web.Mvc;
 
namespace Project.Helper.Localization
{
    public class LocalizationWebFormViewEngine : WebFormViewEngine
    {
        protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
        {
            return new LocalizationWebFormView(viewPath, masterPath);
        }
 
        protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
        {
            return new LocalizationWebFormView(partialPath, null);
        }
    }
}

ResourceExtensions.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
using System.Globalization;
using System.Web;
using System.Web.Compilation;
using System.Web.Mvc;
using System;
 
namespace P014.Helper.Localization
{
    public static class ResourceExtensions
    {
        public static string Resource(this Controller controller, string expression, params object[] args)
        {
            ResourceExpressionFields fields = GetResourceFields(expression, "~/");
            return GetGlobalResource(fields, args);
        }
 
        public static string Resource(this HtmlHelper htmlHelper, string expression, params object[] args)
        {
            string path = (string)htmlHelper.ViewData[LocalizationWebFormView.ViewPathKey];
            if (string.IsNullOrEmpty(path))
                path = "~/";
 
            ResourceExpressionFields fields = GetResourceFields(expression, path);
            if (!string.IsNullOrEmpty(fields.ClassKey))
                return GetGlobalResource(fields, args);
 
            return GetLocalResource(path, fields, args);
        }
 
        public static string Language(this HtmlHelper htmlHelper, string key, params object[] args)
        {
		    //Replace Language with a name of your choice, if you have lang.en.resx, you should change the value here to "lang"
            string expression = "Language, " + key;
            return Resource(htmlHelper, expression, args);
        }
 
        static string GetLocalResource(string path, ResourceExpressionFields fields, object[] args)
        {
            try{
                return string.Format((string)HttpContext.GetLocalResourceObject(path, fields.ResourceKey, CultureInfo.CurrentUICulture), args);
            }
            catch { return (string)HttpContext.GetLocalResourceObject(path, fields.ResourceKey, CultureInfo.CurrentUICulture); }
        }
 
        static string GetGlobalResource(ResourceExpressionFields fields, object[] args)
        {
            try{
                return string.Format((string)HttpContext.GetGlobalResourceObject(fields.ClassKey, fields.ResourceKey, CultureInfo.CurrentUICulture), args);
            }
            catch { return (string)HttpContext.GetGlobalResourceObject(fields.ClassKey, fields.ResourceKey, CultureInfo.CurrentUICulture); }
        }
 
        static ResourceExpressionFields GetResourceFields(string expression, string virtualPath)
        {
            var context = new ExpressionBuilderContext(virtualPath);
            var builder = new ResourceExpressionBuilder();
            return (ResourceExpressionFields)builder.ParseExpression(expression, typeof(string), context);
        }
    }
}

What I've added in this code is in the file ResourceExtensions.cs. Which i add a language helper to make you called out from your view easier. Remember to replace Language with a name of your choice, if you have lang.en.resx, you should change the value here to "lang".

Now for the program to finally fire up the Globalization / Localization for the webpage, we need to declare the culture for the program, either you can declare in web.config or you can write a little code in Global.asax

web.config approach:

<system .web>
      <globalization uiCulture="vi" />
</system>

Global.asax

        protected void Application_BeginRequest(Object sender, EventArgs e)
        {
            // Application Level language
            // VI here is the language you want to set it on default.
            Thread.CurrentThread.CurrentUICulture = new CultureInfo("vi");
        }

3rd, Configure.... We need to config the view of WFViewEngine to be able to find the Localization classes.
Open web.config and search for

 
/* Find: */
<namespaces>
 
/* Add this after, where "Project.Helper.Localization" is the class of the files above. */
<add namespace="Project.Helper.Localization" />  
</namespaces>

Finally! We've done most of it. You can now use the tag below to insert the translation text into the program.

< %= Html.Language("name") %>

For the {0} {1} argument I've mention earlier, I will put some example here:

// name: argumenttest
// value: This is first argument {0}, 2nd {1}, 3rd <strong>{3} </strong>
< %= Html.Language("argumenttest", new [] { "First", "duc", "strong" } %>
// output: This is first argument First, 2nd duc, 3rd strong

Now you can have a fully functional globalization / localization without a sweat!
I hope you enjoy this post :) !

  • Share/Bookmark
Comments (2) Trackbacks (1)
  1. I’ve used a similar approach to the one here and found it worked great. The only thing I realize now is to have major content stored in the Global resource area. I have an MobileViewEngine and that content will appear on similar views.

    Great work though.

  2. Thank you for your comment. Your blog is quite interesting too :)


Leave a comment