由于需要大量代码,我只会说明如何完成它的想法。
您可以RazorViewEngine
像这样子类化:
public class I18NRazorViewEngine : RazorViewEngine
{
public I18NRazorViewEngine() : this(null)
{ }
protected string[] I18NAreaViewLocationFormats;
protected string[] I18NAreaMasterLocationFormats;
protected string[] I18NAreaPartialViewLocationFormats;
protected string[] I18NViewLocationFormats;
protected string[] I18NMasterLocationFormats;
protected string[] I18NPartialViewLocationFormats;
public I18NRazorViewEngine(IViewPageActivator viewPageActivator)
: base(viewPageActivator)
{
this.I18NAreaViewLocationFormats = new string[]
{
"~/Areas/{3}/{2}/Views/{1}/{0}.cshtml",
"~/Areas/{3}/{2}/Views/{1}/{0}.vbhtml",
"~/Areas/{3}/{2}/Views/Shared/{0}.cshtml",
"~/Areas/{3}/{2}/Views/Shared/{0}.vbhtml"
};
this.I18NAreaMasterLocationFormats = new string[]
{
"~/Areas/{3}/{2}/Views/{1}/{0}.cshtml",
"~/Areas/{3}/{2}/Views/{1}/{0}.vbhtml",
"~/Areas/{3}/{2}/Views/Shared/{0}.cshtml",
"~/Areas/{3}/{2}/Views/Shared/{0}.vbhtml"
};
this.I18NAreaPartialViewLocationFormats = new string[]
{
"~/Areas/{3}/{2}/Views/{1}/{0}.cshtml",
"~/Areas/{3}/{2}/Views/{1}/{0}.vbhtml",
"~/Areas/{3}/{2}/Views/Shared/{0}.cshtml",
"~/Areas/{3}/{2}/Views/Shared/{0}.vbhtml"
};
this.I18NViewLocationFormats = new string[]
{
"~/Views/{2}/{1}/{0}.cshtml",
"~/Views/{2}/{1}/{0}.vbhtml",
"~/Views/{2}/Shared/{0}.cshtml",
"~/Views/{2}/Shared/{0}.vbhtml"
};
this.I18NMasterLocationFormats = new string[]
{
"~/Views/{2}/{1}/{0}.cshtml",
"~/Views/{2}/{1}/{0}.vbhtml",
"~/Views/{2}/Shared/{0}.cshtml",
"~/Views/{2}/Shared/{0}.vbhtml"
};
this.I18NPartialViewLocationFormats = new string[]
{
"~/Views/{2}/{1}/{0}.cshtml",
"~/Views/{2}/{1}/{0}.vbhtml",
"~/Views/{2}/Shared/{0}.cshtml",
"~/Views/{2}/Shared/{0}.vbhtml"
};
}
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
var langValue = controllerContext.Controller.ValueProvider.GetValue("lang");
if (langValue == null || String.IsNullOrEmpty(langValue.AttemptedValue))
return base.FindView(controllerContext, viewName, masterName, useCache);
//Code here
}
public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
var langValue = controllerContext.Controller.ValueProvider.GetValue("lang");
if (langValue == null || String.IsNullOrEmpty(langValue.AttemptedValue))
return base.FindPartialView(controllerContext, partialViewName, useCache);
//Code here
}
}
接下来你应该做的是查看VirtualPathProviderViewEngine
FindView 和实现的内部FindPartialView
。反映的代码是这样的:
public virtual ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (string.IsNullOrEmpty(partialViewName))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "partialViewName");
}
string requiredString = controllerContext.RouteData.GetRequiredString("controller");
string[] searchedLocations;
string path = this.GetPath(controllerContext, this.PartialViewLocationFormats, this.AreaPartialViewLocationFormats, "PartialViewLocationFormats", partialViewName, requiredString, "Partial", useCache, out searchedLocations);
if (string.IsNullOrEmpty(path))
{
return new ViewEngineResult(searchedLocations);
}
return new ViewEngineResult(this.CreatePartialView(controllerContext, path), this);
}
和
public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (string.IsNullOrEmpty(viewName))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "viewName");
}
string requiredString = controllerContext.RouteData.GetRequiredString("controller");
string[] first;
string path = this.GetPath(controllerContext, this.ViewLocationFormats, this.AreaViewLocationFormats, "ViewLocationFormats", viewName, requiredString, "View", useCache, out first);
string[] second;
string path2 = this.GetPath(controllerContext, this.MasterLocationFormats, this.AreaMasterLocationFormats, "MasterLocationFormats", masterName, requiredString, "Master", useCache, out second);
if (string.IsNullOrEmpty(path) || (string.IsNullOrEmpty(path2) && !string.IsNullOrEmpty(masterName)))
{
return new ViewEngineResult(first.Union(second));
}
return new ViewEngineResult(this.CreateView(controllerContext, path, path2), this);
}
两种方法都依赖于私有GetPath
方法:
private string GetPath(ControllerContext controllerContext, string[] locations, string[] areaLocations, string locationsPropertyName, string name, string controllerName, string cacheKeyPrefix, bool useCache, out string[] searchedLocations)
{
searchedLocations = VirtualPathProviderViewEngine._emptyLocations;
if (string.IsNullOrEmpty(name))
{
return string.Empty;
}
string areaName = AreaHelpers.GetAreaName(controllerContext.RouteData);
List<VirtualPathProviderViewEngine.ViewLocation> viewLocations = VirtualPathProviderViewEngine.GetViewLocations(locations, (!string.IsNullOrEmpty(areaName)) ? areaLocations : null);
if (viewLocations.Count == 0)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.Common_PropertyCannotBeNullOrEmpty, new object[]
{
locationsPropertyName
}));
}
bool flag = VirtualPathProviderViewEngine.IsSpecificPath(name);
string text = this.CreateCacheKey(cacheKeyPrefix, name, flag ? string.Empty : controllerName, areaName);
if (useCache)
{
return this.ViewLocationCache.GetViewLocation(controllerContext.HttpContext, text);
}
if (!flag)
{
return this.GetPathFromGeneralName(controllerContext, viewLocations, name, controllerName, areaName, text, ref searchedLocations);
}
return this.GetPathFromSpecificName(controllerContext, name, text, ref searchedLocations);
}
你应该做的是重新实现它。大多数代码您可以重用,但您应该创建自己的方法而不是VirtualPathProviderViewEngine.GetViewLocations
. 这里它的反映代码:
private static List<VirtualPathProviderViewEngine.ViewLocation> GetViewLocations(string[] viewLocationFormats, string[] areaViewLocationFormats)
{
List<VirtualPathProviderViewEngine.ViewLocation> list = new List<VirtualPathProviderViewEngine.ViewLocation>();
if (areaViewLocationFormats != null)
{
for (int i = 0; i < areaViewLocationFormats.Length; i++)
{
string virtualPathFormatString = areaViewLocationFormats[i];
list.Add(new VirtualPathProviderViewEngine.AreaAwareViewLocation(virtualPathFormatString));
}
}
if (viewLocationFormats != null)
{
for (int j = 0; j < viewLocationFormats.Length; j++)
{
string virtualPathFormatString2 = viewLocationFormats[j];
list.Add(new VirtualPathProviderViewEngine.ViewLocation(virtualPathFormatString2));
}
}
return list;
}
您还可以重用大部分代码,但您应该使用自己的类,而不是 VirtualPathProviderViewEngine.ViewLocation 和 VirtualPathProviderViewEngine.AreaAwareViewLocation。他们可能是这样的:
class ViewLocation
{
protected string _virtualPathFormatString;
public ViewLocation(string virtualPathFormatString)
{
this._virtualPathFormatString = virtualPathFormatString;
}
public virtual string Format(string viewName, string controllerName, string areaName, string lang)
{
return string.Format(CultureInfo.InvariantCulture, this._virtualPathFormatString, new object[]
{
viewName,
controllerName,
lang
});
}
}
和:
class AreaAwareViewLocation : VirtualPathProviderViewEngine.ViewLocation
{
public AreaAwareViewLocation(string virtualPathFormatString) : base(virtualPathFormatString)
{
}
public override string Format(string viewName, string controllerName, string areaName, string lang)
{
return string.Format(CultureInfo.InvariantCulture, this._virtualPathFormatString, new object[]
{
viewName,
controllerName,
areaName,
lang
});
}
}
然后当您调用Format
方法时,您应该将langValue.AttemptedValue
(它来自第一个代码块的范围FindView
和FindPartialView
)传递给 lang 参数。通常它会被调用VirtualPathProviderViewEngine.GetPathFromGeneralName
。
主要建议是使用 ILSpy 或其他反汇编程序来探索 System.Web.Mvc 的代码(甚至更好 - 下载其源代码)。目标是重新实现FindView
和FindPartialView
. 提供的其余代码是为了说明它是如何在 mvc 框架中完成的。
寻找在我们的新视图引擎中声明的数组而不是那些已经存在并被默认类使用的没有 I18N 前缀的数组也很重要
尽管答案是间接的,但希望它会有所帮助。如果您遇到任何困难,您可以提出其他问题。
PS 不要忘记在 global.asax.cs 中注册您的视图引擎,之后它将被开发。
protected virtual void Application_Start()
{
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new I18NRazorViewEngine());
}