7

based on this question (Best place to set CurrentCulture for multilingual ASP.NET MVC web applications) I'm looking for a global way to do the same for ASP.NET WebAPI.

My current implementation for ASP.NET MVC looks like this:

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {

        AreaRegistration.RegisterAllAreas();
        GlobalConfiguration.Configure(WebApiConfig.Register);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);


        ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(new CultureAwareControllerActivator()));


    }
}


public class CultureAwareControllerActivator : System.Web.Mvc.IControllerActivator
{
    public System.Web.Mvc.IController Create(System.Web.Routing.RequestContext requestContext, System.Type controllerType)
    {
        string cultureName = null;

        // Attempt to read the culture cookie from Request
        HttpCookie cultureCookie = requestContext.HttpContext.Request.Cookies["_culture"];
        if (cultureCookie != null)
            cultureName = cultureCookie.Value;
        else
            cultureName = requestContext.HttpContext.Request.UserLanguages != null && requestContext.HttpContext.Request.UserLanguages.Length > 0 ?
                      requestContext.HttpContext.Request.UserLanguages[0] :  // obtain it from HTTP header AcceptLanguages
                      null;

        // Validate culture name
        cultureName = RoedingGmbh.Pis2Go.Web.Mvc.Helpers.CultureHelper.GetImplementedCulture(cultureName); // This is safe

        // Modify current thread's cultures            
        System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureName);
        // Currently the UI Language is only english
        //Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;

        return DependencyResolver.Current.GetService(controllerType) as IController;
    }
}

I'm trying to do the same things as in ASP.NET MVC, but following code doesn't work properly.

 public static class WebApiConfig
 {
      public static void Register(HttpConfiguration config)
      {
            // Web API configuration and services

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                 name: "DefaultApi",
                 routeTemplate: "api/{controller}/{id}",
                 defaults: new { id = RouteParameter.Optional }
            );

            GlobalConfiguration.Configuration.Services.Replace(typeof(System.Web.Http.Dispatcher.IHttpControllerActivator), new CultureAwareHttpControllerActivator());

          }
 }


 public class CultureAwareHttpControllerActivator : System.Web.Http.Dispatcher.IHttpControllerActivator
 {
     public System.Web.Http.Controllers.IHttpController Create(System.Net.Http.HttpRequestMessage request, System.Web.Http.Controllers.HttpControllerDescriptor controllerDescriptor, Type controllerType)
     {
        System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("DE-de");
        return GlobalConfiguration.Configuration.DependencyResolver.GetService(controllerType) as System.Web.Http.Controllers.IHttpController;
     }
 }

With that code, WebAPI cannot find any route anymore :( What have I to do to get this to work!

Regards, Daniel

Community
  • 1
  • 1
Daniel C.
  • 569
  • 2
  • 7
  • 19

2 Answers2

8

If you want global localization for whole application, you can put the set culture code into the BeginRequest method of the HttpApplication in global asax file. This will set your culture in the most first place of each request and will affect every MVC and webapi request.

Aik
  • 3,528
  • 3
  • 19
  • 20
  • 1
    Your answer seems to be to easy, but it works like a charme. Thanks for your quick answer! – Daniel C. Mar 19 '14 at 22:48
  • But what about threads using await/async? will this propagate through them all? – Korayem Jul 01 '16 at 19:39
  • In the case the User.Identity is needed, it is better to use AuthenticateRequest, because on BeginRequest the User is not defined yet. Side note: I was not being able to set the event handler. This thread helped me: https://stackoverflow.com/questions/1123741/why-cant-my-host-softsyshosting-com-support-beginrequest-and-endrequest-event – JoaoRibeiro Jul 18 '17 at 09:50
  • For async/await and since .Net Fx4.6, we are supposed to set [CultureInfo.CurrentCulture](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo.currentculture)/CurrentUICulture instead. They have both gained a setter, and setting them is supposed to work across async/await calls. But I cannot get it to work in my case. I set them in an ActionExecuting handler of an action filter, between two async calls. The controller action does see the change. – Frédéric Oct 28 '21 at 12:21
  • According to this [answer to a related question](https://stackoverflow.com/a/49384405/1178314), we have to do the switch way earlier, like in an authorization filter. What a mess. – Frédéric Oct 28 '21 at 12:26
3

I'd use a ActionFilter

If you are using Asp.Net MVC

//A foreigner, has possibly brew a cookie for me
public class SpeakNativeTongueAttribute : ActionFilterAttribute, IActionFilter
{
    const string cookieName = "culture";

     void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
    {
        var cookieKeys = filterContext.RequestContext.HttpContext.Request.Cookies.AllKeys;

        if (cookieKeys.Contains(cookieName))
        {
            //eat the cookie
            var theCultureCookie = filterContext.RequestContext.HttpContext.Request.Cookies[cookieName];
            var theCulture = theCultureCookie.Value;

            //say thanks in native tongue
            System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo(theCulture);
            System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo(theCulture);
        }
        else
        {
            //Didn't receive a cookie, don't speak their language, those bastards!

        }
    }
}

In Javascript you can set the language in a cookie named "culture"

NicoJuicy
  • 3,435
  • 4
  • 40
  • 66
  • //A foreigner, has possibly brew a cookie for me ? – Carlos P Mar 25 '15 at 16:33
  • 1
    We did this way at first, but it seems that the Model Binder is called *before* the Action Filter, so you might run into parsing issues. Global asax is best. – Jeff Jun 16 '16 at 15:46