[多國語系] 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/