【问题标题】:Best place to set CurrentCulture for multilingual ASP.NET MVC web applications为多语言 ASP.NET MVC Web 应用程序设置 CurrentCulture 的最佳位置
【发布时间】:2012-01-03 19:40:29
【问题描述】:

对于多语言 ASP.NET MVC 3 Web 应用程序,我在控制器工厂中确定 Thread.CurrentThread.CurrentCulture 和 Thread.CurrentThread.CurrentUICulture 如下:

public class MyControllerFactory : DefaultControllerFactory {

    protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType) {

        //Get the {language} parameter in the RouteData
        string UILanguage;
        if (requestContext.RouteData.Values["language"] == null)
            UILanguage = "tr";
        else
            UILanguage = requestContext.RouteData.Values["language"].ToString();

        //Get the culture info of the language code
        CultureInfo culture = CultureInfo.CreateSpecificCulture(UILanguage);
        Thread.CurrentThread.CurrentCulture = culture;
        Thread.CurrentThread.CurrentUICulture = culture;

        return base.GetControllerInstance(requestContext, controllerType);
    }

}

上面的代码已经快一年了!所以,我愿意接受建议。

我在 Global.asax 文件中注册了这个,例如:

ControllerBuilder.Current.SetControllerFactory(new MyControllerFactory());

这很好用,但我不确定这是否是执行此类操作的最佳做​​法和最佳位置。

ControllerFactory 的主要角色我没有深入研究,无法与ActionFilterAttribute 进行比较。

您认为进行此类操作的最佳地点是什么?

【问题讨论】:

  • 我还没有找到改变 CurrentCulture 的充分理由。虽然它使日期和时间格式自动工作,但我认为仅仅为此改变文化是过度的(例如,在打开与使用文化 Y 的服务器的 SQL 连接时,您是否希望线程文化为 X?我宁愿离开 CurrentCulture当我需要格式化日期和数字时,单独使用用户文化。(不过,CurrentUICulture 在我看来是可以的。)
  • @HectorCorrea 谢谢赫克托。实际上我从来没有想过这一点,最好的部分是你是对的。我只为资源文件更改它。你认为 CurrentUICulture 就足够了吗?
  • 是的,CurrentUICulture 对于资源来说已经足够了。 [有趣的是,今年早些时候我在将文化切换到土耳其语时使用 CurrentCulture 被咬了,因为大写“I”和小写“i”的问题。与此链接中描述的非常相似。我想知道您对此有何看法,因为您似乎在处理土耳其语。 west-wind.com/weblog/posts/2005/May/23/…]
  • 不,它适用于 UICulture。
  • 我知道这是旧的,但我不得不不同意。 CurrentCulture 是用来操纵的。需要明确设置的是对数据库等的写入(例如,使用 InvariantCulture)。这就是依赖 CurrentCulture 会给您带来麻烦的地方。

标签: asp.net-mvc asp.net-mvc-3 action-filter controller-factory


【解决方案1】:

如果你不使用 ControllerActivator,可以使用 BaseController 类并继承它。

public class BaseController : Controller
{
    public BaseController()
    {
          //Set CurrentCulture and CurrentUICulture of the thread
    }
}

public class HomeController: BaseController    
{
    [HttpGet]
    public ActionResult Index()
    {
        //..............................................
    }
}

【讨论】:

    【解决方案2】:

    我知道已经选择了一个分析器。我们使用的选项是在应用程序的 OnBeginRequest 事件中初始化线程当前区域性。这样可以确保在每个请求中都能发现文化

    public void OnBeginRequest(object sender, EventArgs e)
    {
       var culture = YourMethodForDiscoveringCulutreUsingCookie();
       System.Threading.Thread.CurrentThread.CurrentCulture = culture;
       System.Threading.Thread.CurrentThread.CurrentUICulture = culture;
    }
    

    【讨论】:

    • 我们可以选择在 BeginRequest 方法中设置文化。我们还可以在 AcquireRequestState 方法中设置状态和文化特定信息。BeginRequest 和 AcquireRequestState 方法都针对每个请求执行。BeginRequest 先执行,然后是 AcquireRequestState。
    【解决方案3】:

    我为此使用了全局 ActionFilter,但最近我意识到,在某些情况下,在 OnActionExecuting 方法中设置当前文化为时已晚。例如,当 POST 请求后的模型到达控制器时,ASP.NET MVC 会为模型创建元数据。它发生在执行任何操作之前。因此,DisplayName 属性值和其他数据注释内容此时使用默认区域性进行处理。

    最终,我将当前文化设置为自定义 IControllerActivator 实现,它就像一个魅力。我想从请求生命周期的角度来看,在自定义控制器工厂中托管这个逻辑几乎是一样的,就像你今天一样。它比使用全局ActionFilter 可靠得多。

    CultureAwareControllerActivator.cs:

    public class CultureAwareControllerActivator: IControllerActivator
    {
        public IController Create(RequestContext requestContext, Type controllerType)
        {
            //Get the {language} parameter in the RouteData
            string language = requestContext.RouteData.Values["language"] == null ?
                "tr" : requestContext.RouteData.Values["language"].ToString();
    
            //Get the culture info of the language code
            CultureInfo culture = CultureInfo.GetCultureInfo(language);
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = culture;
    
            return DependencyResolver.Current.GetService(controllerType) as IController;
        }
    }
    

    Global.asax.cs:

    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            ...
            ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(new CultureAwareControllerActivator()));
        }
    }
    

    【讨论】:

    • @shanabus 不幸的是,我不再拥有此代码,但我尝试重建它并使用代码 sn-ps 更新了答案。我不确定CultureAwareControllerActivator 的注册是否正确,但它可以工作。
    • 此时,RouteData 尚不包含任何操作参数。当文化设置基于 URL 的固定部分(主机名或 url 路径的第一部分)时仍然有用
    • 您为什么不想在 onRequestBegin 事件中执行此行为?在我看来,这会影响所有可能的情况。
    • @AgileJedi OnBeginRequest 不会为您提供有关请求的任何信息(路线数据等)。这只是新请求创建的信号。因此,在这种情况下,文化选择的能力非常有限。
    • ActionFilters 确实 在某些情况下为时已晚(如果不是全部)主要是因为模型绑定(您的字符串将被转换为数字和日期等等)发生 before 动作过滤器。
    【解决方案4】:

    您可以像这样在此处覆盖Initialize,而不是覆盖OnActionExecuting

    protected override void Initialize(RequestContext requestContext)
    {
            string culture = null;
            var request = requestContext.HttpContext.Request;
            string cultureName = null;
    
            // Attempt to read the culture cookie from Request
            HttpCookie cultureCookie = request.Cookies["_culture"];
            if (cultureCookie != null)
                cultureName = cultureCookie.Value;
            else
                cultureName = request.UserLanguages[0]; // obtain it from HTTP header AcceptLanguages
    
            // Validate culture name
            cultureName = CultureHelper.GetValidCulture(cultureName); // This is safe
    
            if (request.QueryString.AllKeys.Contains("culture"))
            {
                culture = request.QueryString["culture"];
            }
            else
            {
                culture = cultureName;
            }
    
            Uitlity.CurrentUICulture = culture;
    
            base.Initialize(requestContext);
        }
    

    【讨论】:

    • 感谢分享。用于设置 CurrentUICulture 的 Utility 类是什么样的?
    • 实用程序类将具有如下定义的属性code public static string CurrentUICulture { get { return Thread.CurrentThread.CurrentUICulture.Name; } 设置 { Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(CultureHelper.GetValidCulture(value)); Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(CultureHelper.GetValidCulture(value)); } }
    【解决方案5】:

    另一种方法是将代码放在自定义 ActionFilter 的 OnActionExecuting 方法中,该方法可以注册到 GlobalFilters 集合中:

    http://weblogs.asp.net/gunnarpeipman/archive/2010/08/15/asp-net-mvc-3-global-action-filters.aspx

    【讨论】:

    • 谢谢!这也是我考虑过的一个选项。我主要在这里寻找最佳实践。使用 ActionFilterAttribute 执行此操作的优缺点是什么?
    • +1,就是这样。这是我制作的示例属性:github.com/jgauffin/griffin.mvccontrib/blob/master/source/…
    • @jgauffin 放置 CookieName 属性是个好主意。你让它在那里不可改变。如果属性在单独的项目中,最好有这个。
    • @jgauffin 我认为通过 ActionFilter 本地化应用程序有一个缺点。如果您缓存您的操作并且缓存仍然有效,则永远不会执行 ActionFilter。进行本地化的最佳方法是设置新的控制器工厂。
    • @tugberk:不。您需要指定缓存应该根据参数变化。 (使用OutputCache属性+global.asax/GetVaryByCustomString
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-17
    • 1970-01-01
    • 2019-04-21
    • 1970-01-01
    • 2014-01-05
    • 2017-08-11
    相关资源
    最近更新 更多