samesite


【译】MVC3 20个秘方-(4)实现多语言

场景
互联网是由千百万个人使用。他们来自数百个不同的国家,使用数百种不同的语言。甚至在加拿大、美国和英国之间英语也有许多方言。

所以,不在你的网站上仅仅呈现一种语言是重要的。

解决方案

创建资源文件,并且以键值对的形式添加静态文本,通过CurrentUICulture来提供改变语言的能力。

讨论

资源文件是基于文本的XML文件,用来支持静态网站多国语言。你创建一个主要的资源文件,包含你的默认语言。为文本创建一些key/value pair。然后,你就可以在你的controller、view、model中的任何地方使用这些文本了。下图是个例子

 

注意圈红的地方要设置为public。如果一个文件的访问修饰符不是public ,它可能不能被访问到。

下面开始创建你的资源文件。右键单击project,添加->新建文件夹,添加新项,选择资源文件。创建。

正如你在上边例子看到的。我已经为书中的每一个字段创建了一个条目。下一步就是更新这个model ,在DisplayAttribute中引用这些值。

代码如下:

View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;
using MvcApplication.Validations;

namespace MvcApplication.Models
{
public class Book
{
public int ID { get; set; }
[Required]
[Display(Name = "TitleDisplay",
ResourceType = typeof(Resources.Resource1))]
public string Title { get; set; }
[Display(Name = "IsbnDisplay",
ResourceType = typeof(Resources.Resource1))]
[Required]
[IsbnValidation]
public string Isbn { get; set; }
[Display(Name = "SummaryDisplay",
ResourceType = typeof(Resources.Resource1))]
[Required]
public string Summary { get; set; }
[Display(Name = "AuthorDisplay",
ResourceType = typeof(Resources.Resource1))]
[Required]
public string Author { get; set; }
[Display(Name = "ThumbnailDisplay",
ResourceType = typeof(Resources.Resource1))]
public string Thumbnail { get; set; }
[Display(Name = "PriceDisplay",
ResourceType = typeof(Resources.Resource1))]
[Range(1, 100)]
public double Price { get; set; }
[Display(Name = "PublishedDisplay",
ResourceType = typeof(Resources.Resource1))]
[DataType(DataType.Date)]
[Required]
public DateTime Published { get; set; }
}
public class BookDBContext : DbContext
{
public DbSet<Book> Books { get; set; }
}
}

在上边的例子中,DisplayAttribute 特性中的“Name”是从资源文件检索的key。resource type 中定义了资源文件的名称。

提示:为了更方便调试资源文件,避免在view中处理数据。我强烈建议在controller里创建一个 ViewBag并且在view中引用这些值。因为如果直接在view里访问资源文件。View不由Visual studio 编译。如果你犯错了,你将遇到一个运行时的错误。而在controller里使用资源文件的好处就是如果定义的key文件没有找到。Visual studio 会编译失败。

下边的例子将更新Book Index view 把静态的文本放到资源文件里。如果你检查那个view 你会发现没有太多的项要放到资源文件里。

下边我们按照下边的表创建key/value pairs。

你们可以注意到,我把后边4个key/value pair 写的比较通用。

因为资源文件是整个网站都可以访问的。在其他的地方我们一样可以用他们来替换静态文本。

更新之后我们的资源文件应该变成这样了

创建完资源文件,让我们打开BookController 并且用如下代码替换Index() action:

View Code
 public ViewResult Index()
{
#region ViewBag Resources
ViewBag.Title =
Resources.Resource1.BookIndexTitle;
ViewBag.CreateLink =
Resources.Resource1.CreateLink;
ViewBag.TitleDisplay =
Resources.Resource1.TitleDisplay;
ViewBag.IsbnDisplay =
Resources.Resource1.IsbnDisplay;
ViewBag.SummaryDisplay =
Resources.Resource1.SummaryDisplay;
ViewBag.AuthorDisplay =
Resources.Resource1.AuthorDisplay;
ViewBag.ThumbnailDisplay =
Resources.Resource1.ThumbnailDisplay;
ViewBag.PriceDisplay = Resources.Resource1.PriceDisplay;
ViewBag.PublishedDisplay =
Resources.Resource1.PublishedDisplay;
ViewBag.EditLink =
Resources.Resource1.EditLink;
ViewBag.DetailsLink =
Resources.Resource1.DetailsLink;
ViewBag.DeleteLink =
Resources.Resource1.DeleteLink;
#endregion

return View(db.Books.ToList());
}

最后,要更新Books Index view去引用这些ViewBag的属性替换掉静态文本:

View Code
@model IEnumerable<MvcApplication.Models.Book>
@{
ViewBag.Title = "Index";
}
<h2>
Index</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
@ViewBag.TitleDisplay
</th>
<th>
@ViewBag.IsbnDisplay
</th>
<th>
@ViewBag.SummaryDisplay
</th>
<th>
@ViewBag.AuthorDisplay
</th>
<th>
@ViewBag.ThumbnailDisplay
</th>
<th>
@ViewBag.PriceDisplay
</th>
<th>
@ViewBag.PublishedDisplay
</th>
<th>
</th>
</tr>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.Isbn)
</td>
<td>
@Html.DisplayFor(modelItem => item.Summary)
</td>
<td>
@Html.DisplayFor(modelItem => item.Author)
</td>
<td>
@Html.DisplayFor(modelItem => item.Thumbnail)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.DisplayFor(modelItem => item.Published)
</td>
<td>
@Html.ActionLink((string)ViewBag.EditLink,"Edit", new { id = item.ID }) |
@Html.ActionLink((string)ViewBag.DetailsLink,"Details", new { id = item.ID }) |
@Html.ActionLink((string)ViewBag.DeleteLink,"Delete", new { id = item.ID })
</td>
</tr>
}
</table>

在剩余的controller 中action 和view中应该使用相同的方法去替换资源文件。

现在我们来创建一个中文的资源文件(原著是创建的法语的)。

在资源文件上点击右键。复制。再点击黏贴(ctrl V)。这样我们得到了一个名字为:

副本Resource1.resx的资源文件。我们把它重新命名为Resource1.cn.resx。双击打开这个文件。

并且更改响应的values 为中文。如下图:

下边我们要实现语言转换了。在每次请求发生的时候都要通过CurrentUICulture 来判断当前的语言。

打开Global.asax.cs这个文件并在Application_AcquireRequestState() 加入如下代码:

  protected void Application_AcquireRequestState(object sender, EventArgs e)
{
if (HttpContext.Current.Session != null)
{
CultureInfo ci =
(CultureInfo)this.Session["CurrentLanguage"];
if (ci == null)
{
ci = new CultureInfo("en");
this.Session["CurrentLanguage"] = ci;
}
Thread.CurrentThread.CurrentUICulture = ci;
Thread.CurrentThread.CurrentCulture =
CultureInfo.CreateSpecificCulture(ci.Name);
}
}

在上边的例子里, CurrentUICulture 是基于 CurrentLanguagesession 变量的. 如果一个合法的CultureInfo没被找到,将默认为英文。默认的,这个session 变量是不存在的。我们应该在Home Controller创建一个action 让用户切换语言:

public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult ChangeLanguage(string language)
{
Session["CurrentLanguage"] =
new CultureInfo(language);
return Redirect("Index");
}
public ActionResult About()
{
return View();
}
}

新的action ChangeLanguage 接受一个参数:新的语言名字。这个存在session 变量里,它引用自Global.asax.cs 文件.

最后,我们要创建一个切换语言的链接。这应该是在每个页面都要有的。所以我们把它放到Views/shared文件夹下的_Layout.cshtml(类似于模板页):

[ @Html.ActionLink("English", "ChangeLanguage", "Home",new { language = "en" }, null) ] 
[ @Html.ActionLink("Chinese", "ChangeLanguage", "Home",new { language = "cn" }, null) ]

我把它放在logon link 的边上了。现在如果再有新的需求要求你的网站支持更多的语言,这将是很容易的事。你只需要添加一个新的资源文件并且添加一个新的链接让用户去选择新的语言。

在最开始我们讨论的问题:在加拿大、美国和英国中可能会存在很多方言。如果你愿意根据国家来分离语言。你可以添加一个连字符(-)链接语言和国家代码。例如en-GB表示英国的英语。你也需要更新你的链接去支持新的语言。

另请参阅

CurrentUICulture


 

posted @ 2011-11-23 15:41  技术弟弟  阅读(4735)  评论(8编辑  收藏  举报