在开发国际化的Web应用程序时,本地化是一个重要的议题。对于ASP.NET MVC应用程序来说,数据注解(DataAnnotations)提供了一种方便的方式来实现模型验证和显示元数据的本地化。然而,直接在模型上使用资源文件进行本地化会导致代码冗余和难以维护。本文将介绍一种更简洁、更优雅的实现方式。
在传统的本地化实现中,可能会在ViewModel中直接使用资源文件来设置数据注解的ErrorMessage、DisplayName和Description等属性。例如:
public class UserViewModel
{
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Resources.LocalizedStrings))]
[LocalizedDisplayName(ErrorMessageResourceName = "UserId", ErrorMessageResourceType = typeof(Resources.LocalizedStrings))]
[LocalizedDescription(ErrorMessageResourceName = "UserIdDescription", ErrorMessageResourceType = typeof(Resources.LocalizedStrings))]
public int Id { get; set; }
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Resources.LocalizedStrings))]
[LocalizedDisplayName(ErrorMessageResourceName = "UserFirstName", ErrorMessageResourceType = typeof(Resources.LocalizedStrings))]
[LocalizedDescription(ErrorMessageResourceName = "UserFirstNameDescription", ErrorMessageResourceType = typeof(Resources.LocalizedStrings))]
public string FirstName { get; set; }
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Resources.LocalizedStrings))]
[LocalizedDisplayName(ErrorMessageResourceName = "UserLastName", ErrorMessageResourceType = typeof(Resources.LocalizedStrings))]
[LocalizedDescription(ErrorMessageResourceName = "UserLastNameDescription", ErrorMessageResourceType = typeof(Resources.LocalizedStrings))]
public string LastName { get; set; }
}
这种方式虽然可以实现本地化,但会导致ViewModel代码变得臃肿,且难以维护。
为了解决这个问题,可以通过继承数据注解属性并在派生类中实现本地化逻辑的方式来简化ViewModel。但这种方法会导致客户端验证失效,除非为派生属性创建新的适配器。
另一种更简单的解决方案是利用MVC3引入的元数据提供者(Metadata Providers)来实现本地化。这种方式可以让模型代码保持简洁。
public class UserViewModel
{
[Required]
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
}
在全局.asax文件中,需要指定新的提供者:
protected void Application_Start()
{
var stringProvider = new ResourceStringProvider(Resources.LocalizedStrings.ResourceManager);
ModelMetadataProviders.Current = new LocalizedModelMetadataProvider(stringProvider);
ModelValidatorProviders.Providers.Clear();
ModelValidatorProviders.Providers.Add(new LocalizedModelValidatorProvider(stringProvider));
}
这样,就不需要在模型上使用冗余的属性了。
为了实现这种本地化方式,创建了一个名为ILocalizedStringProvider的接口,该接口被两个提供者使用。这意味着不必使用字符串表(String Tables)。只需创建一个新类并实现该接口,就可以支持数据库、XML文件或其他任何选择。
在示例中,使用了字符串表,其中的字符串看起来像这样:
应该将字符串命名为“ModelName_PropertyName”和“ModelName_PropertyName_MetadataName”以包含元数据,如Watermark和NullDisplayText。有关元数据的详细信息,请参考文档。对于验证属性,字符串表的名称应该是属性名称,去掉“Attribute”后缀。