[多國語系] MVC ViewModel自動載入語系資源
DRY原則
上方的程式碼整個看起來就是令人眼花瞭亂,每個Property要冠上Display及設定ResourceType,且差異的部份只Name的指定,程式碼重複的部分一大堆...。根據DIY的原則....不不不..是DRY的原則,不要一直重覆你自已,我們需要更乾淨的作法。改善目標
為了保持較乾淨的類別及加速日後的開發與維護,列出以下幾點需求:- 不要設定語系名稱及語系型別
- 每個屬性不要冠上[Display]
- 自動為Model的Property跟資源檔作結合
1.先為資源名稱規則化
如下圖所示:以Model+底線+Property來命名2.建立語系提供者的介面
public interface ILocalizedStringProvider
{
string GetModelString(Type model, string propertyName);
string GetModelString(Type model, string propertyName, string metadataName);
}
3.實作語系提供者的介面-語系資源檔提供者
此提供者主要的功能為,1.建構式先將所需要的ResourceManager載入,2.根據目前傳進來的ModelType及Property名稱去ResourceManager取得對應的語系值。
public class ResourceStringProvider : ILocalizedStringProvider
{
private readonly List<ResourceManager> _resourceManagers = new List<ResourceManager>();
public ResourceStringProvider(params ResourceManager[] resourceManager)
{
_resourceManagers.AddRange(resourceManager);
}
public string GetModelString(Type model, string propertyName)
{
return GetString(Format(model, propertyName));
}
public string GetModelString(Type model, string propertyName, string metadataName)
{
return GetString(Format(model, propertyName, metadataName));
}
protected virtual string Format(Type type, string propertyName, params string[] extras)
{
var baseStr = string.Format("{0}_{1}", type.Name, propertyName);
return extras.Aggregate(baseStr, (current, extra) => current + ("_" + extra));
}
protected virtual string Format(Type attributeType)
{
return attributeType.Name.Replace("Attribute", "");
}
protected virtual string GetString(string name)
{
var result = _resourceManagers.Select(resourceManager => resourceManager.GetString(name)).FirstOrDefault(value => value != null);
return result;
}
}
4.覆寫DataAnnotationsModelMetadataProvider
MVC的Model Metadata提供者預設為DataAnnotationsModelMetadataProvider,藉由繼承它並改寫成我們需要的功能。如下程式主要為覆寫CreateMetadata,在判斷若未設定Display相關屬性時則到ResourceStringProvider 去找。
public class LocalizedModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
private ILocalizedStringProvider _stringProvider;
public LocalizedModelMetadataProvider(ILocalizedStringProvider stringProvider)
{
if (stringProvider == null) throw new ArgumentNullException("stringProvider");
_stringProvider = stringProvider;
}
private ILocalizedStringProvider Provider
{
get
{
return _stringProvider;
}
}
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType,
Func<object> modelAccessor, Type modelType, string propertyName)
{
var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
if (containerType == null || propertyName == null)
return metadata;
if (metadata.DisplayName == null)
{
metadata.DisplayName = Translate(containerType, propertyName) ;
}
if (metadata.Watermark == null)
metadata.Watermark = Translate(containerType, propertyName, "Watermark");
if (metadata.Description == null)
metadata.Description = Translate(containerType, propertyName, "Description");
if (metadata.NullDisplayText == null)
metadata.NullDisplayText = Translate(containerType, propertyName, "NullDisplayText");
if (metadata.ShortDisplayName == null)
metadata.ShortDisplayName = Translate(containerType, propertyName, "ShortDisplayName");
return metadata;
}
protected virtual string Translate(Type type, string propertyName)
{
return Provider.GetModelString(type, propertyName);
}
protected virtual string Translate(Type type, string propertyName, string metadataName)
{
return Provider.GetModelString(type, propertyName, metadataName);
}
}
5.在Global.asax將預設的MatadataProvider換掉
- 建立ResourceStringProvider,在建構式加入Local.ResourceManager
- 指定ModelMetadataProviders為LocalizedModelMetadataProvider
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ResourceStringProvider rp = new ResourceStringProvider(Local.ResourceManager);
ModelMetadataProviders.Current = new LocalizedModelMetadataProvider(rp);
}
6.將Model的Display全部移除
public class ProductModel
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public DateTime CreateDate { get; set; }
public decimal Price { get; set; }
public int Qty { get; set; }
}
7.大功告成-顯示成果
中文英文
參考來源
http://blog.gauffin.org/2011/09/easy-model-and-validation-localization-in-asp-net-mvc3/本篇原始碼
https://github.com/kimx/LocalizedModelProviderLab/