【问题标题】:ASP.NET MVC 2 / Localization / Dynamic Default Value?ASP.NET MVC 2 / 本地化 / 动态默认值?
【发布时间】:2010-03-08 17:16:03
【问题描述】:

在 ASP.NET MVC 2 应用程序中,我的路由如下:

            routes.MapRoute(
            "Default",                                       // Route name
            "{lang}/{controller}/{action}/{id}",             // URL with parameters
            new                                              // Parameter defaults 
            {                                             
                controller = "Home",
                action = "Index",
                lang = "de",
                id = UrlParameter.Optional
            },
            new 
            { 
                lang = new AllowedValuesRouteConstraint(new string[] { "de", "en", "fr", "it" },
                                                        StringComparison.InvariantCultureIgnoreCase) 
            }

现在,基本上我想根据传入的语言设置线程的文化。但有一个例外: 如果用户第一次请求该页面,例如调用“http://www.mysite.com”,我想尽可能将初始语言设置为“浏览器首选”。

如果由于默认值而设置了默认参数或通过 URL 明确提及,我如何在早期处理阶段(如 global.asax)区分? (我更喜欢不解析请求 URL 的解决方案)。

有没有办法为参数动态提供默认值?钩子之类的东西?或者我可以在哪里覆盖默认值(好的应用程序事件?)。

这是我实际试验的代码:

        protected void Application_AcquireRequestState(object sender, EventArgs e)
    {
        string activeLanguage;
        string[] validLanguages;
        string defaultLanguage;
        string browsersPreferredLanguage;

        try
        {
            HttpContextBase contextBase = new HttpContextWrapper(Context);
            RouteData activeRoute = RouteTable.Routes.GetRouteData(new HttpContextWrapper(Context));                

            if (activeRoute == null)
            {
                return;
            }

            activeLanguage = activeRoute.GetRequiredString("lang");
            Route route = (Route)activeRoute.Route;
            validLanguages = ((AllowedValuesRouteConstraint)route.Constraints["lang"]).AllowedValues;
            defaultLanguage = route.Defaults["lang"].ToString();
            browsersPreferredLanguage = GetBrowsersPreferredLanguage();

            //TODO: Better way than parsing the url
            bool defaultInitialized = contextBase.Request.Url.ToString().IndexOf(string.Format("/{0}/", defaultLanguage), StringComparison.InvariantCultureIgnoreCase) > -1;
            string languageToActivate = defaultLanguage;
            if (!defaultInitialized)
            {
                if (validLanguages.Contains(browsersPreferredLanguage, StringComparer.InvariantCultureIgnoreCase))
                {
                    languageToActivate = browsersPreferredLanguage;
                }
            }

            //TODO: Where and how to overwrtie the default value that it gets passed to the controller? 
            contextBase.RewritePath(contextBase.Request.Path.Replace("/de/", "/en/"));

            SetLanguage(languageToActivate);
        }
        catch (Exception ex)
        {
            //TODO: Log
            Console.WriteLine(ex.Message);
        }

    }
    protected string GetBrowsersPreferredLanguage()
    {
        string acceptedLang = string.Empty;

        if (HttpContext.Current.Request.UserLanguages != null && HttpContext.Current.Request.UserLanguages.Length > 0)
        {
            acceptedLang = HttpContext.Current.Request.UserLanguages[0].Substring(0, 2);
        }

        return acceptedLang;
    }

    protected void SetLanguage(string languageToActivate)
    {
        CultureInfo cultureInfo = new CultureInfo(languageToActivate);

        if (!Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName.Equals(languageToActivate, StringComparison.InvariantCultureIgnoreCase))
        {
            Thread.CurrentThread.CurrentUICulture = cultureInfo;
        }

        if (!Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName.Equals(languageToActivate, StringComparison.InvariantCultureIgnoreCase))
        {
            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cultureInfo.Name);
        }
    }

重现示例的 RouteConstraint:

    public class AllowedValuesRouteConstraint : IRouteConstraint
{

    private string[] _allowedValues;
    private StringComparison _stringComparism;

    public string[] AllowedValues
    {
        get { return _allowedValues;  }
    }

    public AllowedValuesRouteConstraint(string[] allowedValues, StringComparison stringComparism)
    {
        _allowedValues = allowedValues;
        _stringComparism = stringComparism;
    }

    public AllowedValuesRouteConstraint(string[] allowedValues)
    {
        _allowedValues = allowedValues;
        _stringComparism = StringComparison.InvariantCultureIgnoreCase;
    }

    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        if (_allowedValues != null)
        {
            return _allowedValues.Any(a => a.Equals(values[parameterName].ToString(), _stringComparism));
        }
        else
        {
            return false;
        }
    }
}

有人可以帮我解决这个问题吗?

谢谢,马丁

【问题讨论】:

    标签: asp.net-mvc localization multilingual default-value routes


    【解决方案1】:

    为了防止不一致,我更喜欢在根目录上重定向到浏览器语言,然后让用户根据自己的喜好更改语言。

    只需将此行放在 default.aspx 中即可。

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
        Select Case Mid(Request.UserLanguages(0).ToString(), 1, 2).ToLower
            Case "en"
                Response.Redirect("/en/")
            Case "pt"
                Response.Redirect("/pt/")
            Case Else
                Response.Redirect("/es/")
        End Select
    
    End Sub
    

    【讨论】:

    • 如果有人访问该页面,例如 Posts.aspx 而不是 Default.aspx,该怎么办。那是行不通的。这应该在 httpmodule 或 global.asax 上。
    【解决方案2】:

    使用动作过滤器怎么样?

    可能是这样的:

    public class LocalizationAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (filterContext.RouteData.Values["lang"] != null)
            {
                var lang = filterContext.RouteData.Values["lang"].ToString();
                //TODO: Validate the obtained value.
                Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(lang);
            }
            else
            {
                var langHeader = filterContext.HttpContext.Request.UserLanguages[0];
                //TODO: Validate the obtained value.
                Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(langHeader);
            }
        }
    }
    

    这只是一个想法,当然你需要改进它。 然后您只需将此属性添加到您的控制器,或者您创建一个基本控制器并在其中设置属性(并使所有控制器成为此基本控制器的子类)。

    问候。

    【讨论】:

    • 感谢您的建议。当我在默认路由中使用 lang 的默认值时,“lang”永远不会为空。例如,如果我输入“ru”,我会得到 404 什么是正确的(请参阅约束)。但是我如何确定“de”是作为默认值还是显式传入?如果没有明确传入的值(如果可能,不是通过解析 url),我只想使用浏览器语言。然后我会遇到的第二个问题是更改“lang”参数,以便所有呈现的链接都获得更新的 URL。
    • 示例:-我的参数“lang”的默认值为“de”-现在我输入“mysite.com”-我所有的链接看起来像这样:mysite.com/de/about 现在假设,我的浏览器更喜欢“en”,我打电话给“mysite.com”。 - 现在应该将默认语言覆盖为“en” - 链接应该呈现为 mysite.com/en/about
    • 是的,你是对的,对此感到抱歉。也许您应该改为创建自己的路由处理程序。祝你好运!
    【解决方案3】:

    试试这个:

            RouteData data=RouteTable.Routes.GetRouteData(httpContext);
            if (data != null)
            {
                Route route = data.Route as Route;
                if (route != null && route.Defaults != null)
                    route.Defaults["lang"] = lang.Name;
            }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-10
      • 2013-01-31
      • 1970-01-01
      • 2023-03-18
      • 1970-01-01
      • 2021-09-03
      相关资源
      最近更新 更多