ASP.NET MVC - Localization Helpers (本地化帮助方法)
翻译自:http://blog.eworldui.net/post/2008/05/ASPNET-MVC---Localization.aspx
你本地化你的应用程序了吗?当然,我敢打赌我们至少做到保存字条串资源到资源文件里面,以便以后本地化.但这并不意味着我们正在开发一个大的应用需要本地化成很多语言,所以我们不必考虑这个.本地化在asp.net 1.0/1.1中可以做到.不过asp.net 2.0引入了新的表达式语法,使本地化的容易得多,只需写下面的代码
Code
1 public class ProductsController : Controller
2 {
3 public ActionResult Create()
4 {
5 if (TempData["ErrorMessage"] != null)
6 {
7 ViewData["ErrorMessage"] = TempData["ErrorMessage"];
8 ViewData["Name"] = TempData["Name"];
9 ViewData["Price"] = TempData["Price"];
10 ViewData["Quantity"] = TempData["Quantity"];
11 }
12 return RenderView();
13 }
14
15 public ActionResult Submit()
16 {
17 string error = null;
18 string name = Request.Form["Name"];
19 if (string.IsNullOrEmpty(name))
20 error = this.Resource("Strings, NameIsEmpty");
21
22 decimal price;
23 if (!decimal.TryParse(Request.Form["Price"], out price))
24 error += this.Resource("Strings, PriceIsEmpty");
25 int quantity;
26 if (!int.TryParse(Request.Form["Quantity"], out quantity))
27 error += this.Resource("Strings, QuantityIsEmpty");
28
29 if (!string.IsNullOrEmpty(error))
30 {
31 TempData["ErrorMessage"] = error;
32 TempData["Name"] = Request.Form["Name"];
33 TempData["Price"] = Request.Form["Price"];
34 TempData["Quantity"] = Request.Form["Quantity"];
35 return RedirectToAction("Create");
36 }
37
38 return RedirectToAction("Confirm");
39 }
40
41 public ActionResult Confirm()
42 {
43 return RenderView();
44 }
45 }
46
47
下一步,把视图也改成使用新的资源扩展方法,下面是Create视图.
1 public class ProductsController : Controller
2 {
3 public ActionResult Create()
4 {
5 if (TempData["ErrorMessage"] != null)
6 {
7 ViewData["ErrorMessage"] = TempData["ErrorMessage"];
8 ViewData["Name"] = TempData["Name"];
9 ViewData["Price"] = TempData["Price"];
10 ViewData["Quantity"] = TempData["Quantity"];
11 }
12 return RenderView();
13 }
14
15 public ActionResult Submit()
16 {
17 string error = null;
18 string name = Request.Form["Name"];
19 if (string.IsNullOrEmpty(name))
20 error = this.Resource("Strings, NameIsEmpty");
21
22 decimal price;
23 if (!decimal.TryParse(Request.Form["Price"], out price))
24 error += this.Resource("Strings, PriceIsEmpty");
25 int quantity;
26 if (!int.TryParse(Request.Form["Quantity"], out quantity))
27 error += this.Resource("Strings, QuantityIsEmpty");
28
29 if (!string.IsNullOrEmpty(error))
30 {
31 TempData["ErrorMessage"] = error;
32 TempData["Name"] = Request.Form["Name"];
33 TempData["Price"] = Request.Form["Price"];
34 TempData["Quantity"] = Request.Form["Quantity"];
35 return RedirectToAction("Create");
36 }
37
38 return RedirectToAction("Confirm");
39 }
40
41 public ActionResult Confirm()
42 {
43 return RenderView();
44 }
45 }
46
47
下一步,把视图也改成使用新的资源扩展方法,下面是Create视图.
Code
1 <% using (Html.Form<ProductsController>(c => c.Submit())) { %>
2 <% if (!string.IsNullOrEmpty((string)ViewData["ErrorMessage"])) { %>
3 <div style="color:Red;">
4 <%= ViewData["ErrorMessage"] %>
5 </div>
6 <% } %>
7 <%= Html.Resource("Name") %> <%= Html.TextBox("Name", ViewData["Name"]) %><br />
8 <%= Html.Resource("Price") %> <%= Html.TextBox("Price", ViewData["Price"]) %><br />
9 <%= Html.Resource("Quantity") %> <%= Html.TextBox("Quantity", ViewData["Quantity"]) %><br />
10 <%= Html.SubmitButton("submitButton", Html.Resource("Save")) %>
11 <% } %>
12
这是Confirm视图:
1 <% using (Html.Form<ProductsController>(c => c.Submit())) { %>
2 <% if (!string.IsNullOrEmpty((string)ViewData["ErrorMessage"])) { %>
3 <div style="color:Red;">
4 <%= ViewData["ErrorMessage"] %>
5 </div>
6 <% } %>
7 <%= Html.Resource("Name") %> <%= Html.TextBox("Name", ViewData["Name"]) %><br />
8 <%= Html.Resource("Price") %> <%= Html.TextBox("Price", ViewData["Price"]) %><br />
9 <%= Html.Resource("Quantity") %> <%= Html.TextBox("Quantity", ViewData["Quantity"]) %><br />
10 <%= Html.SubmitButton("submitButton", Html.Resource("Save")) %>
11 <% } %>
12
这是Confirm视图:
Code
1<%= Html.Resource("Thanks") %><br /><br />
2<%= Html.Resource("CreateNew", Html.ActionLink<ProductsController>(c => c.Create(),Html.Resource("ClickHere"))) %>
你可以看到,我使用了两种混合的资源.如下:
就象你看到的,它同时支持Global Resources和 Local Resources. 做Controller action的时候,只有Global Resources(全局资源)起作用,没有local resource(本地资源的概念,Html.Resource的实现实际上是对先前我说的复杂方法的一个封装.不过它考虑到了它考虑到在表达式的语法和语境下,聪明的获取正确的资源.不过缺点是代码正适合WebFormViewEngine 获取不确定本地资源.原因是需要找到当前呈现视图引擎关联的虚拟路径.如果你使用其它的视图引擎,修改下面的视图路径.
1<%= Html.Resource("Thanks") %><br /><br />
2<%= Html.Resource("CreateNew", Html.ActionLink<ProductsController>(c => c.Create(),Html.Resource("ClickHere"))) %>
你可以看到,我使用了两种混合的资源.如下:
Code
1 // default global resource
2 Html.Resource("GlobalResource, ResourceName")
3
4 // global resource with optional arguments for formatting
5 Html.Resource("GlobalResource, ResourceName", "foo", "bar")
6
7 // default local resource
8 Html.Resource("ResourceName")
9
10 // local resource with optional arguments for formatting
11 Html.Resource("ResourceName", "foo", "bar")
12
1 // default global resource
2 Html.Resource("GlobalResource, ResourceName")
3
4 // global resource with optional arguments for formatting
5 Html.Resource("GlobalResource, ResourceName", "foo", "bar")
6
7 // default local resource
8 Html.Resource("ResourceName")
9
10 // local resource with optional arguments for formatting
11 Html.Resource("ResourceName", "foo", "bar")
12
就象你看到的,它同时支持Global Resources和 Local Resources. 做Controller action的时候,只有Global Resources(全局资源)起作用,没有local resource(本地资源的概念,Html.Resource的实现实际上是对先前我说的复杂方法的一个封装.不过它考虑到了它考虑到在表达式的语法和语境下,聪明的获取正确的资源.不过缺点是代码正适合WebFormViewEngine 获取不确定本地资源.原因是需要找到当前呈现视图引擎关联的虚拟路径.如果你使用其它的视图引擎,修改下面的视图路径.
Code
public static string Resource(this HtmlHelper htmlhelper,
string expression,
params object[] args)
{
string virtualPath = GetVirtualPath(htmlhelper);
return GetResourceString(htmlhelper.ViewContext.HttpContext, expression, virtualPath, args);
}
public static string Resource(this Controller controller,
string expression,
params object[] args)
{
return GetResourceString(controller.HttpContext, expression, "~/", args);
}
private static string GetResourceString(HttpContextBase httpContext,
string expression,
string virtualPath,
object[] args)
{
ExpressionBuilderContext context = new ExpressionBuilderContext(virtualPath);
ResourceExpressionBuilder builder = new ResourceExpressionBuilder();
ResourceExpressionFields fields = (ResourceExpressionFields)builder.ParseExpression(expression, typeof(string), context);
if (!string.IsNullOrEmpty(fields.ClassKey))
return string.Format((string)httpContext.GetGlobalResourceObject(
fields.ClassKey, fields.ResourceKey, CultureInfo.CurrentUICulture), args);
return string.Format((string)httpContext.GetLocalResourceObject(
virtualPath,
fields.ResourceKey,
CultureInfo.CurrentUICulture),
args);
}
private static string GetVirtualPath(HtmlHelper htmlhelper)
{
string virtualPath = null;
Controller controller = htmlhelper.ViewContext.Controller as Controller;
if (controller != null)
{
WebFormViewEngine viewEngine = controller.ViewEngine as WebFormViewEngine;
if (viewEngine != null)
{
virtualPath = viewEngine.ViewLocator.GetViewLocation(
new RequestContext(controller.HttpContext,
controller.RouteData),
htmlhelper.ViewContext.ViewName);
}
}
return virtualPath;
}
为了让你知道我没有说谎,给你看一下英语和西班牙语的界面:
压缩代码下载
PS:翻译处女作,如有错误,多多指证:
public static string Resource(this HtmlHelper htmlhelper,
string expression,
params object[] args)
{
string virtualPath = GetVirtualPath(htmlhelper);
return GetResourceString(htmlhelper.ViewContext.HttpContext, expression, virtualPath, args);
}
public static string Resource(this Controller controller,
string expression,
params object[] args)
{
return GetResourceString(controller.HttpContext, expression, "~/", args);
}
private static string GetResourceString(HttpContextBase httpContext,
string expression,
string virtualPath,
object[] args)
{
ExpressionBuilderContext context = new ExpressionBuilderContext(virtualPath);
ResourceExpressionBuilder builder = new ResourceExpressionBuilder();
ResourceExpressionFields fields = (ResourceExpressionFields)builder.ParseExpression(expression, typeof(string), context);
if (!string.IsNullOrEmpty(fields.ClassKey))
return string.Format((string)httpContext.GetGlobalResourceObject(
fields.ClassKey, fields.ResourceKey, CultureInfo.CurrentUICulture), args);
return string.Format((string)httpContext.GetLocalResourceObject(
virtualPath,
fields.ResourceKey,
CultureInfo.CurrentUICulture),
args);
}
private static string GetVirtualPath(HtmlHelper htmlhelper)
{
string virtualPath = null;
Controller controller = htmlhelper.ViewContext.Controller as Controller;
if (controller != null)
{
WebFormViewEngine viewEngine = controller.ViewEngine as WebFormViewEngine;
if (viewEngine != null)
{
virtualPath = viewEngine.ViewLocator.GetViewLocation(
new RequestContext(controller.HttpContext,
controller.RouteData),
htmlhelper.ViewContext.ViewName);
}
}
return virtualPath;
}
为了让你知道我没有说谎,给你看一下英语和西班牙语的界面:
压缩代码下载
PS:翻译处女作,如有错误,多多指证:
<asp:Label Text="<%$ Resources:Strings, MyGlobalResource %>" runat="server" />
<asp:Label Text="<%$Resources:MyLocalResource %>" runat="server" />
<asp:Label Text="<%$Resources:MyLocalResource %>" runat="server" />
当然,你可能经常使用更具体的方式,调用httpContext获到本地和全球的资源.不过我觉得上面的表达式的方式相对更好一些,因为当前的视图或者页面的上下文已经在代码中默认了,不用显示的获取.不过,你还是可以把上面例子下面下面这种方式:
<%=HttpContext.Current.GetGlobalResourceString("Strings", "MyGlobalResources",CultureInfo.CurrentUICulture) %>
<%= HttpContext.Current.GetLocalResourceString("~/views/products/create.aspx","MyLocalResource", CultureInfo.CurrentUICulture) %>
你或许已经着手接下来的大项目了,并且获得许可使用Asp.net mvc,不过,你的系统需要同时支持西班牙语.不过现在asp.net mvc居然还不能够直接使用本地资源,除非使用Literal 控件或者是比较费事的方法,但是这也不再合理.因为你使用mvc就是为了摆脱webform窗体模型和命名.好的,采用我先前的PRG模式的例子(http://blog.eworldui.net/post/2008/05/ASPNET-MVC---Using-Post2c-Redirect2c-Get-Pattern.aspx),我决定在你的一个示例项目中本地化它.首先,您需要创建您的全球和当地资源,添加一个"App_GlobalResources "文件夹到项目根目录。添加strings.resx文件,并开始输入您的文字,接下来,我们为视图添加两个本地化资源.在/Views/Products路径下面,创建一个名字为App_GlobalResources 的目录,添加两个资源文件Create.aspx.resx和Confirm.aspx.resx.
好的,现在都配置好了.让我们把代码转换成使用资源.您会看到我在Controller和View使用的一个新的扩展方法(如下):
<%= HttpContext.Current.GetLocalResourceString("~/views/products/create.aspx","MyLocalResource", CultureInfo.CurrentUICulture) %>
你或许已经着手接下来的大项目了,并且获得许可使用Asp.net mvc,不过,你的系统需要同时支持西班牙语.不过现在asp.net mvc居然还不能够直接使用本地资源,除非使用Literal 控件或者是比较费事的方法,但是这也不再合理.因为你使用mvc就是为了摆脱webform窗体模型和命名.好的,采用我先前的PRG模式的例子(http://blog.eworldui.net/post/2008/05/ASPNET-MVC---Using-Post2c-Redirect2c-Get-Pattern.aspx),我决定在你的一个示例项目中本地化它.首先,您需要创建您的全球和当地资源,添加一个"App_GlobalResources "文件夹到项目根目录。添加strings.resx文件,并开始输入您的文字,接下来,我们为视图添加两个本地化资源.在/Views/Products路径下面,创建一个名字为App_GlobalResources 的目录,添加两个资源文件Create.aspx.resx和Confirm.aspx.resx.
好的,现在都配置好了.让我们把代码转换成使用资源.您会看到我在Controller和View使用的一个新的扩展方法(如下):