Установить культуру в приложении ASP.Net MVC - PullRequest
81 голосов
/ 13 октября 2009

Как лучше установить Культуру / Культуру пользовательского интерфейса в приложении ASP.net MVC

В настоящее время у меня есть класс CultureController, который выглядит следующим образом:

public class CultureController : Controller
{
    public ActionResult SetSpanishCulture()
    {
        HttpContext.Session["culture"] = "es-ES";
        return RedirectToAction("Index", "Home");
    }

    public ActionResult SetFrenchCulture()
    {
        HttpContext.Session["culture"] = "fr-FR";
        return RedirectToAction("Index", "Home");
    }
}

и гиперссылка для каждого языка на главной странице с такой ссылкой:

<li><%= Html.ActionLink("French", "SetFrenchCulture", "Culture")%></li>
<li><%= Html.ActionLink("Spanish", "SetSpanishCulture", "Culture")%></li>

, который работает нормально, но я думаю, что есть более подходящий способ сделать это.

Я читаю Культуру, используя следующий ActionFilter http://www.iansuttle.com/blog/post/ASPNET-MVC-Action-Filter-for-Localized-Sites.aspx. Я немного новичок в MVC, поэтому не уверен, что установил это в правильном месте. Я не хочу делать это на уровне web.config, это должно основываться на выборе пользователя. Я также не хочу проверять их http-заголовки, чтобы узнать культуру их настроек браузера.

Edit:

Просто чтобы прояснить - я не пытаюсь решить, использовать сеанс или нет. Я доволен этим. Я пытаюсь выяснить, лучше ли это делать в контроллере культуры, в котором есть метод действия для каждой культуры, или в MVC-конвейере есть лучшее место для этого?

Ответы [ 8 ]

109 голосов
/ 13 октября 2009

Я использую этот метод локализации и добавил параметр маршрута, который устанавливает культуру и язык, когда пользователь посещает example.com/xx-xx/

Пример:

routes.MapRoute("DefaultLocalized",
            "{language}-{culture}/{controller}/{action}/{id}",
            new
            {
                controller = "Home",
                action = "Index",
                id = "",
                language = "nl",
                culture = "NL"
            });

У меня есть фильтр, который выполняет фактические настройки языка / языка:

using System.Globalization;
using System.Threading;
using System.Web.Mvc;

public class InternationalizationAttribute : ActionFilterAttribute {

    public override void OnActionExecuting(ActionExecutingContext filterContext) {

        string language = (string)filterContext.RouteData.Values["language"] ?? "nl";
        string culture = (string)filterContext.RouteData.Values["culture"] ?? "NL";

        Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(string.Format("{0}-{1}", language, culture));
        Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(string.Format("{0}-{1}", language, culture));

    }
}

Чтобы активировать атрибут Интернационализация, просто добавьте его в свой класс:

[Internationalization]
public class HomeController : Controller {
...

Теперь, когда посетитель заходит на http://example.com/de-DE/Home/Index, отображается немецкий сайт.

Надеюсь, этот ответ направит вас в правильном направлении.

Я также сделал небольшой пример проекта MVC 5, который вы можете найти здесь

Просто перейдите на http://{yourhost}:{port}/en-us/home/index, чтобы увидеть текущую дату на английском (США), или измените ее на http://{yourhost}:{port}/de-de/home/index для немецкого и так далее.

37 голосов
/ 22 июля 2011

Я знаю, что это старый вопрос, но если вы действительно хотите, чтобы это работало с вашим ModelBinder (в отношении DefaultModelBinder.ResourceClassKey = "MyResource";, а также с ресурсами, указанными в аннотациях данных классов viewmodel), контроллер или даже ActionFilter слишком поздно для установки культуры .

Культура может быть установлена ​​в Application_AcquireRequestState, например:

protected void Application_AcquireRequestState(object sender, EventArgs e)
    {
        // For example a cookie, but better extract it from the url
        string culture = HttpContext.Current.Request.Cookies["culture"].Value;

        Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(culture);
        Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture);
    }

EDIT

На самом деле есть лучший способ, используя пользовательский маршрутный обработчик , который устанавливает культуру в соответствии с URL, прекрасно описанным Алексом Адамяном в его блоге .

Все, что нужно сделать, это переопределить метод GetHttpHandler и установить там культуру.

public class MultiCultureMvcRouteHandler : MvcRouteHandler
{
    protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        // get culture from route data
        var culture = requestContext.RouteData.Values["culture"].ToString();
        var ci = new CultureInfo(culture);
        Thread.CurrentThread.CurrentUICulture = ci;
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name);
        return base.GetHttpHandler(requestContext);
    }
}
24 голосов
/ 13 октября 2009

Я бы сделал это в событии Initialize контроллера, как это ...

    protected override void Initialize(System.Web.Routing.RequestContext requestContext)
    {
        base.Initialize(requestContext);

        const string culture = "en-US";
        CultureInfo ci = CultureInfo.GetCultureInfo(culture);

        Thread.CurrentThread.CurrentCulture = ci;
        Thread.CurrentThread.CurrentUICulture = ci;
    }
7 голосов
/ 13 октября 2009

Поскольку эта настройка хранится для каждого пользователя, сеанс является подходящим местом для хранения информации.

Я бы изменил ваш контроллер так, чтобы он брал строку культуры в качестве параметра, вместо того чтобы использовать разные методы действий для каждой потенциальной культуры. Добавить ссылку на страницу легко, и вам не нужно многократно писать один и тот же код всякий раз, когда требуется новая культура.

public class CultureController : Controller    
{
        public ActionResult SetCulture(string culture)
        {
            HttpContext.Session["culture"] = culture
            return RedirectToAction("Index", "Home");
        }        
}

<li><%= Html.ActionLink("French", "SetCulture", new {controller = "Culture", culture = "fr-FR"})%></li>
<li><%= Html.ActionLink("Spanish", "SetCulture", new {controller = "Culture", culture = "es-ES"})%></li>
5 голосов
/ 09 апреля 2015

Какое лучшее место - твой вопрос? Лучшее место - метод Controller.Initialize . MSDN пишет, что он вызывается после конструктора и перед методом действия. В отличие от переопределения OnActionExecuting, размещение вашего кода в методе Initialize позволяет вам воспользоваться возможностью локализации всех пользовательских аннотаций и атрибутов данных в ваших классах и ваших свойствах.

Например, моя логика локализации происходит от класса, который внедряется в мой пользовательский контроллер. У меня есть доступ к этому объекту, так как Initialize вызывается после конструктора. Я могу сделать назначение культуры Нити, и не все сообщения об ошибках отображаются правильно.

 public BaseController(IRunningContext runningContext){/*...*/}

 protected override void Initialize(RequestContext requestContext)
 {
     base.Initialize(requestContext);
     var culture = runningContext.GetCulture();
     Thread.CurrentThread.CurrentUICulture = culture;
     Thread.CurrentThread.CurrentCulture = culture;
 }

Даже если ваша логика не находится внутри класса, как в приведенном мной примере, у вас есть доступ к RequestContext , который позволяет вам иметь URL-адрес и HttpContext и RouteData , которые вы можете выполнять практически при любом анализе.

4 голосов
/ 22 ноября 2016

1: создайте пользовательский атрибут и метод переопределения следующим образом:

public class CultureAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
    // Retreive culture from GET
    string currentCulture = filterContext.HttpContext.Request.QueryString["culture"];

    // Also, you can retreive culture from Cookie like this :
    //string currentCulture = filterContext.HttpContext.Request.Cookies["cookie"].Value;

    // Set culture
    Thread.CurrentThread.CurrentCulture = new CultureInfo(currentCulture);
    Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(currentCulture);
    }
}

2: в App_Start найдите FilterConfig.cs, добавьте этот атрибут. (это работает для всего приложения)

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
    // Add custom attribute here
    filters.Add(new CultureAttribute());
    }
}    

Вот и все!

Если вы хотите определить культуру для каждого контроллера / действия вместо всего приложения, вы можете использовать этот атрибут следующим образом:

[Culture]
public class StudentsController : Controller
{
}

Или:

[Culture]
public ActionResult Index()
{
    return View();
}
4 голосов
/ 23 января 2013

Если вы используете субдомены, например, например, «pt.mydomain.com» для установки португальского, то использование Application_AcquireRequestState не будет работать, потому что оно не вызывается при последующих запросах кеша.

Чтобы решить эту проблему, я предлагаю такую ​​реализацию:

  1. Добавьте параметр VaryByCustom в OutPutCache следующим образом:

    [OutputCache(Duration = 10000, VaryByCustom = "lang")]
    public ActionResult Contact()
    {
        return View("Contact");
    }
    
  2. В global.asax.cs получить культуру с хоста с помощью вызова функции:

    protected void Application_AcquireRequestState(object sender, EventArgs e)
    {
        System.Threading.Thread.CurrentThread.CurrentUICulture = GetCultureFromHost();
    }
    
  3. Добавьте функцию GetCultureFromHost в global.asax.cs:

    private CultureInfo GetCultureFromHost()
    {
        CultureInfo ci = new CultureInfo("en-US"); // en-US
        string host = Request.Url.Host.ToLower();
        if (host.Equals("mydomain.com"))
        {
            ci = new CultureInfo("en-US");
        }
        else if (host.StartsWith("pt."))
        {
            ci = new CultureInfo("pt");
        }
        else if (host.StartsWith("de."))
        {
            ci = new CultureInfo("de");
        }
        else if (host.StartsWith("da."))
        {
            ci = new CultureInfo("da");
        }
    
        return ci;
    }
    
  4. И, наконец, переопределите GetVaryByCustomString (...), чтобы также использовать эту функцию:

    public override string GetVaryByCustomString(HttpContext context, string value)
    {
        if (value.ToLower() == "lang")
        {
            CultureInfo ci = GetCultureFromHost();
            return ci.Name;
        }
        return base.GetVaryByCustomString(context, value);
    }
    

Функция Application_AcquireRequestState вызывается при некэшируемых вызовах, что позволяет генерировать и кэшировать контент. GetVaryByCustomString вызывается для кэшированных вызовов, чтобы проверить, доступно ли содержимое в кэше, и в этом случае мы снова проверяем значение входящего домена хоста, вместо того, чтобы полагаться только на текущую информацию о культуре, которая могла бы измениться для нового запроса (потому что мы используем субдомены).

0 голосов
/ 09 октября 2013
protected void Application_AcquireRequestState(object sender, EventArgs e)
        {
            if(Context.Session!= null)
            Thread.CurrentThread.CurrentCulture =
                    Thread.CurrentThread.CurrentUICulture = (Context.Session["culture"] ?? (Context.Session["culture"] = new CultureInfo("pt-BR"))) as CultureInfo;
        }
...