寻找多语言的最佳实践方式
如果你的网站需要被世界各地的人访问,访问者会使用各种不同的语言和文字书写习惯,那么创建一个支持多语言的网站就是十分必要的了,这一篇文章就讲述怎么快速合理的创建网站对多语言的支持。接下来通过一个实例来讲述实践方式。
首先创建一个ASP.NET MVC5应用程序,命名为Internationalization:
然后在Models中添加一个示例的模型类:
public class Employee { [Display(Name = "Name", ResourceType = typeof(Resources.Resource))] [Required(ErrorMessageResourceType = typeof(Resources.Resource), ErrorMessageResourceName = "NameRequired")] public string Name { get; set; } }
这里提到了Resources.Resource命名空间,接下来就创建它:
这是一个单独的项目,用来存放各种语言的资源文件,我们创建了三个资源文件,分别存放了中文(默认)、英文和阿拉伯文,资源文件中存放了如下资源项:
注意:这里的资源因为需要在项目外部使用,所以需要将访问修饰符修改为Public
接下来就是如何来确定访问者要使用的语言了,在每个请求中,都会有一个Accept-language的头,其中定义了可接受的语言类型,但是我们仅可以从它来判断浏览器中设置的语言,而这个语言类型可能并不是访问者实际需要的语言类型,所以,我们将设计一个可供选择的语言列表,然后在服务器端使用发回Cookie的方式保存浏览器端实际需要的语言。
首先需要创建一个CultureHelper类,这个类的功能就是来判断访问者实际需要的语言类型:
public class CultureHelper { private static readonly List<string> validCultures = new List<string>() { "zh-cn", "en-us","ar" }; private static readonly List<string> cultures = new List<string>() { "zh-cn", "en-us","ar" }; public static bool IsRightToLeft() { return Thread.CurrentThread.CurrentCulture.TextInfo.IsRightToLeft; } public static string GetImplementedCulture(string name) { if (string.IsNullOrEmpty(name)) { return GetDefaultCulture(); } if (!validCultures.Where(c => c.Equals(name, StringComparison.InvariantCultureIgnoreCase)).Any()) { return GetDefaultCulture(); } if (cultures.Where(c => c.Equals(name, StringComparison.InvariantCultureIgnoreCase)).Any()) { return name; } var n = GetNeutralCulture(name); foreach (var c in cultures) { if (c.StartsWith(n)) { return c; } } return GetDefaultCulture(); } public static string GetDefaultCulture() { return cultures[0]; } public static string GetCurrentCulture() { return Thread.CurrentThread.CurrentCulture.Name; } public static string GetCurrentNeutralCulture() { return GetNeutralCulture(Thread.CurrentThread.CurrentCulture.Name); } public static string GetNeutralCulture(string name) { if (!name.Contains("-")) { return name; } else { return name.Split('-')[0]; } } }
接下来创建一个BaseController类,并且确保之后创建的所有控制器继承自它:
public class BaseController : Controller { protected override IAsyncResult BeginExecuteCore(AsyncCallback callback, object state) { string cultureName = string.Empty; HttpCookie cultureCookie = Request.Cookies["_culture"]; if (cultureCookie != null) { cultureName = cultureCookie.Value; } else { cultureName = Request.UserLanguages != null && Request.UserLanguages.Length > 0 ? Request.UserLanguages[0] : null; } cultureName = CultureHelper.GetImplementedCulture(cultureName); Thread.CurrentThread.CurrentCulture = new CultureInfo(cultureName); Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture; return base.BeginExecuteCore(callback,state); } }
创建一个EmployeeController,作为多语言实践的一个示例:
public class EmployeeController : BaseController { // // GET: /User/ public ActionResult Index() { return View(); } public ActionResult SetCulture(string culture) { culture = CultureHelper.GetImplementedCulture(culture); HttpCookie cookie = Request.Cookies["_culture"]; if (cookie != null) { cookie.Value = culture; } else { cookie = new HttpCookie("_culture"); cookie.Value = culture; cookie.Expires = DateTime.Now.AddDays(1); } Response.Cookies.Add(cookie); return RedirectToAction("Index"); } }
在程序包管理控制台中,使用PS命令,安装Bootstrap对RightToLeft文字习惯的支持:
Install-Package Twitter.Bootstrap.RTL
然后在App_Start中的BundleConfig.cs中添加两个资源文字的虚拟捆绑路径:
bundles.Add(new ScriptBundle("~/bundles/bootstrap-rtl").Include( "~/Scripts/bootstrap-rtl.js", "~/Scripts/respond.js")); bundles.Add(new StyleBundle("~/Content/css-rtl").Include( "~/Content/css/bootstrap-rtl.css", "~/Content/site.css"));
最后,创建Index视图:
@model Internationalization.Models.Employee @{ ViewBag.Title = "Index"; var culture = System.Threading.Thread.CurrentThread.CurrentCulture.Name.ToLowerInvariant(); } @helper selected(string c, string culture) { if (c == culture) { @:checked="checked" } } @*选择语言列表*@ @using (Html.BeginForm("SetCulture", "Employee")) { <fieldset> <legend></legend> <div class="control-group"> <div class=" "> <label for="zh-cn"> <input name="culture" id="zh-cn" value="zh-cn" type="radio" @selected("zh-cn", culture) /> 中文 </label> </div> </div> <div class="control-group"> <div class="controls"> <label for="en-us"> <input name="culture" id="en-us" value="en-us" type="radio" @selected("en-us", culture) /> English </label> </div> </div> <div class="control-group"> <div class="controls"> <label for="en-us"> <input name="culture" id="ar" value="ar" type="radio" @selected("ar", culture) /> عربي </label> </div> </div> </fieldset> } @*创建员工表单*@ @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-horizontal"> <h2>@Resources.Resource.AddEmployee</h2> <hr /> @Html.ValidationSummary(true) <div class="form-group"> @Html.LabelFor(model => model.Name, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Name) @Html.ValidationMessageFor(model => model.Name) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Create" class="btn btn-default" /> </div> </div> </div> } @*在选择语言后,自动提交*@ @section Scripts { @Scripts.Render("~/bundles/jqueryval") <script type="text/javascript"> (function ($) { $("input[type = 'radio']").click(function () { $(this).parents("form").submit(); // post form }); })(jQuery); </script> }
还需要更改_Layout.cshtml文件,当文字习惯为右到左时,需要切换bootstrap的样式文件:
<!DOCTYPE html> <html lang="@Internationalization.Helpers.CultureHelper.GetCurrentNeutralCulture()" dir="@(Internationalization.Helpers.CultureHelper.IsRightToLeft() ? "rtl" : "ltr")"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>@ViewBag.Title - 我的 ASP.NET 应用程序</title> @Styles.Render("~/Content/css" + (Internationalization.Helpers.CultureHelper.IsRightToLeft() ? "-rtl" : "")) @Scripts.Render("~/bundles/modernizr") </head> <body> <div class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> @Html.ActionLink("应用程序名称", "Index", "Home", null, new { @class = "navbar-brand" }) </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li>@Html.ActionLink("主页", "Index", "Home")</li> <li>@Html.ActionLink("关于", "About", "Home")</li> <li>@Html.ActionLink("联系方式", "Contact", "Home")</li> </ul> </div> </div> </div> <div class="container body-content"> @RenderBody() <hr /> <footer> <p>© @DateTime.Now.Year - 我的 ASP.NET 应用程序</p> </footer> </div> @Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/bootstrap" + (Internationalization.Helpers.CultureHelper.IsRightToLeft() ? "-rtl" : "")) @RenderSection("scripts", required: false) </body> </html>
演示效果:
如果您觉得这篇文章对您有用,劳烦给个赞!
如果您觉得这篇文章可能对别人游泳,劳烦您推荐一个!
ASP.NET MVC 过滤器(五)
ASP.NET MVC 过滤器(五)
前言
上篇对了行为过滤器的使用做了讲解,如果在控制器行为的执行中遇到了异常怎么办呢?没关系,还好框架给我们提供了异常过滤器,在本篇中将会对异常过滤器的使用做一个大概的讲解。
ASP.NET MVC过滤器
- 过滤器在系统框架中的整体对象模型
- IAuthorizationFilter授权认证过滤器的执行过程
- 使用IAuthorizationFilter过滤器
- IActionFilter行为过滤器的执行过程
- 自定义实现IActionFilter行为过滤器
- 异常过滤器的使用
异常过滤器的使用
在使用异常过滤器之前,我们要看一下它们的结构:
public interface IExceptionFilter { void OnException(ExceptionContext filterContext); }
IExceptionFilter接口类型是异常过滤器必须实现的接口,当然了还有FilterAttribute特性类型这是必须要实现的。在此不对FilterAttribute特性类型做什么介绍。
我们看一下IExceptionFilter接口类型中的OnException()方法中的有个ExceptionContext类型的参数,ExceptionContext类型是遗产过滤器参数上下文对象,继承自ControllerContext类型,这种编程模式在MVC框架中随处可见了不做多的解释了。
在MVC框架中给我们默认的提供了一个异常过滤器类型:
代码1-1
public class HandleErrorAttribute : FilterAttribute, IExceptionFilter { // 摘要: // 初始化 System.Web.Mvc.HandleErrorAttribute 类的新实例。 public HandleErrorAttribute(); public Type ExceptionType { get; set; } public string Master { get; set; } public string View { get; set; } public virtual void OnException(ExceptionContext filterContext); }
ExceptionType属性对应要处理的异常类型,如果设置某种异常类型值那么异常过滤器只会针对这个类型的异常来进行做处理,默认值是处理所有异常类型。
Master属性则是设置在处理异常之后所要显示视图所用的布局。
View属性是设置在处理异常之后所要显示的视图名称。
来看示例:
示例还是接着上个篇幅的示例,没看过前篇的朋友也没什么影响很简单的示例。
代码1-2
public ActionResult Index() { int i = 0; int j = 3 / i; return View(_DataStandard.GetProducts()); }
首先我们在控制器的Index()方法中定义个错误处理,0是不能作为除数的处理到这必须报异常的。运行的结果如图1所示。
图1
很明确的报出了异常的所在,这是我们不希望的看见的,接下来我们把异常过滤器给使用上:
代码1-3
[HandleError] public ActionResult Index() { int i = 0; int j = 3 / i; return View(_DataStandard.GetProducts()); }
这时你会发现运行的结果和图1中所示的一样,这是坑爹呢??!!!!当然不是我们需要在Web.config文件的中的<system.web>节点下做相对应的配置才可以,
代码1-4
<customErrors mode="On"" />
需要添加代码1-4中的内容到system.web节点下,这是我们再来看一下运行的结果图。
图2
为什么会显示这个页面,这跟HandleErrorAttribute类型中的OnException(ExceptionContext filterContext)方法默认实现有关系,它默认设置就是服务器错误信息,那我们怎么自定义的指定我们想显示的页面,或者是看到我们想详细看到的错误信息呢?我们再来重写一下OnException()方法吧。
代码1-5
public class MyCustomHandleErrorAttribute : HandleErrorAttribute { public override void OnException(ExceptionContext filterContext) { if (filterContext == null) { throw new ArgumentNullException("filterContext"); } if (filterContext.Exception!=null) { Exception innerException = filterContext.Exception; if (!filterContext.ExceptionHandled) { string controllerName = (string)filterContext.RouteData.Values["controller"]; string actionName = (string)filterContext.RouteData.Values["action"]; HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName); ViewResult result = new ViewResult { ViewName = "Error", MasterName = this.Master, ViewData = new ViewDataDictionary<HandleErrorInfo>(model), TempData = filterContext.Controller.TempData }; filterContext.Result = result; filterContext.ExceptionHandled = true; } } } }
在上面的MyCustomHandleErrorAttribute类型实现中,我们只需要关注ExceptionContext filterContext参数中的Result的设置,在代码1-5中我们指定了Error这个视图,并且还实例化了HandleErrorInfo类型作为视图模型传递到视图中,我们现在要新建一个视图,在当前控制器的文件夹下View/[controllername]/Error.cshtml新建这个视图文件,并且选择为强类型视图,类型则为HandleErrorInfo类型,我们看一下视图的定义。
代码1-6:
@model HandleErrorInfo @{ ViewBag.Title = "Error"; } <h2> Error</h2> <p> 异常类型:<b>@Model.Exception.GetType().Name</b> 异常所在控制器:<b>@Model.ControllerName</b> 异常所在控制器行为:<b>@Model.ActionName</b> </p> <p> 详细信息:<b>@Model.Exception.Message</b> </p>
现在我们修改一下代码1-3中的所使用的过滤器,修改为我们代码1-5中定义的自定义类型,再看一下运行结果:
到这里异常过滤器的基础使用也就讲解完毕了。还有一种结果过滤器就不做讲解了,基础结构和行为过滤器近乎相同使用方式也是,使用一下便知其用法。
作者:金源
出处:http://www.cnblogs.com/jin-yuan/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面