views:

770

answers:

3

Hi, I am getting crazy with the localization of an MVC application.

After a recent question of mine I have followed this approach:

  1. The language is stored in Session["lang"]
  2. Each controller inherits from my own BaseController, which overrides OnActionExecuting, and in this method reads the Session and sets CurrentCulture and CurrentUICulture

This works great, until the Data Annotation Layer comes in. It seems like it gets called BEFORE the action itself is executed, and therefore it always gets the error messages in the default language!

The fields declarations go like this:

[Required(ErrorMessageResourceName = "validazioneRichiesto", ErrorMessageResourceType = typeof(Resources.Resources))]
public string nome { get; set; }

So, is there any reasonable place where I can put the call?

I initialize the Data Annotation Model Binder in my Controller constructor.

public CardController() : base() {
    ModelBinders.Binders.DefaultBinder =
    new Microsoft.Web.Mvc.DataAnnotations.DataAnnotationsModelBinder();
}

So, since Session is always null in the controller's constructor, and the action override is called AFTER the data annotation has validated the fields, where can I possibly set the CurrentCulture and CurrentUICulture to get localized errors?

Help...

EDIT putting the CurrentCulture and CurrentUiCulture in Application_* (e.g. Application_AcquireRequestState or Application_PreRequestHandlerExecute) seems to have no effect...

EDIT

The final solution was the following, thanks to the accepted answer by twk:

void Application_PreRequestHandlerExecute(object sender, EventArgs e) {
  if (Context.Handler is IRequiresSessionState || 
      Context.Handler is IReadOnlySessionState) {
    if (Session["lang"] == null) {
      Session["lang"] = "it";
    }

    if (Request.QueryString["lang"] == "it" || Request.QueryString["lang"] == "en") {
      Session["lang"] = Request.QueryString["lang"];
    }

    string lang1 = Session["lang"].ToString();
    string lang2 = Session["lang"].ToString().ToUpper();

    if (lang2 == "EN")
      lang2 = "GB";

    System.Threading.Thread.CurrentThread.CurrentCulture = 
        System.Globalization.CultureInfo.CreateSpecificCulture(lang1 + "-" + lang2);
    System.Threading.Thread.CurrentThread.CurrentUICulture = 
        System.Globalization.CultureInfo.CreateSpecificCulture(lang1 + "-" + lang2);
  }
}
+2  A: 

As the culture is a global user setting, I am using it in the global.asax.cs file in the Application_BeginRequest method.

It does the work for me (using cookies) but the Session is also available there.

EDIT:

/by Brock Allen: http://www.velocityreviews.com/forums/t282576-aspnet-20-session-availability-in-globalasax.html/

Session is available in PreRequesthandlerExecute.

The problem is that your code is being executed for every request into the server, and some requests (like ones for WebResourxe.axd) don't utlilize Session (because the handler doesn't implement IRequireSessionState). So change your code to only access Session if that request has access to it.

Change your code to do this:

protected void Application_PreRequestHandlerExecute(Object sender, EventArgs e)
{
    if (Context.Handler is IRequiresSessionState || Context.Handler is IReadOnlySessionState)
     SetCulture();    
}

Anyway, not sure if it works with mvc

twk
"Session state is not available in this context". I tried to put it into Application_AcquireRequestState too, but it has no effect on the validation.
Palantir
Good! That was it! Thanks a lot. Editing the original question with the code, for future reference to others.
Palantir
thanks :) I am glad you got it!
twk
A: 

Another approach you can use is to put the lang in the URL, with this benefits:

  • The site is spidered by search engines in different languages
  • The user can send a URL to a friend in the selected language

To do this, use the Application_BeginRequest method in Global.asax

Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
    Dim lang As String
    If HttpContext.Current.Request.Path.Contains("/en/") Then
        lang = "en"
    Else
        lang = "es"
    End If
    Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(lang)
    Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(lang)
End Sub

See this question for more info on how to implement it

Eduardo Molteni
This looks interesting. Are you currently using it?
twk
Yes, this is actual production code from a ASP.net site, with routing. It is not a MVC project, but the concept will be similar.
Eduardo Molteni
That's ok, but my problem is still there. It's not how I pass the language information, it's about how do I pass this information to the application.
Palantir
+1  A: 
Eduardo Molteni
It was already like that. Resources work, if I print them out in the View, they are correctly translated. The problem is ihat I am unable to set the culture for the binder.
Palantir