Thanks to Brad, he identified a few areas that made things MUCH simpler from my prior implementation. Notably, while you still have your ViewEngine specify "here's your view's path" there's no longer a need for having a new helper, derived classes, etc. This implementation goes back to the simplified HtmlHelper extension in which you can use in any ViewEngine regardless. It also allows you to "replace" the implementation by just changing the namespace, just as you can do with Html and Ajax.
Simplified View Engine
As previously stated, you still need a new view engine and view, because this is the only point in which you can truly grab which view is being rendered. What's nice now, is that it has been simplified down to using the base implementation and only setting a ViewData field in which the Resource extension method picks up and uses.
1: public class LocalizationWebFormViewEngine : WebFormViewEngine
2: {
3: protected override IView CreateView(ControllerContext controllerContext,
4: string viewPath, string masterPath)
5: {
6: return new LocalizationWebFormView(viewPath, masterPath);
7: }
8:
9: protected override IView CreatePartialView(ControllerContext controllerContext,
10: string partialPath)
11: {
12: return new LocalizationWebFormView(partialPath, null);
13: }
14: }
15:
16: public class LocalizationWebFormView : WebFormView
17: {
18: internal const string ViewPathKey = "__ViewPath__";
19:
20: public LocalizationWebFormView(string viewPath) : base(viewPath)
21: {
22: }
23:
24: public LocalizationWebFormView(string viewPath, string masterPath)
25: : base(viewPath, masterPath)
26: {
27: }
28:
29: public override void Render(ViewContext viewContext, TextWriter writer)
30: {
31: // there seems to be a bug with RenderPartial tainting the page's view data
32: // so we should capture the current view path, and revert back after rendering
33: string originalViewPath = (string) viewContext.ViewData[ViewPathKey];
34:
35: viewContext.ViewData[ViewPathKey] = ViewPath;
36: base.Render(viewContext, writer);
37:
38: viewContext.ViewData[ViewPathKey] = originalViewPath;
39: }
40: }
As you can see, it's much simpler now. Also, it seems as if there's a bug with the current Beta bits when you have a RenderPartial on your page, there's no isolation and the view's path is not correct later. Simple fix by simply grabbing the original view path, setting it to the new path, rendering, and then reverting back.
Getting back to Extension Methods
And now that we have our path again in our ViewData, we can revert back to using extension methods to extract the path when doing both Global and Local resources. As you can see below, should you be using any other ViewEngine it will always work for Global resources.
1: public static class ResourceExtensions
2: {
3: public static string Resource(this Controller controller, string expression,
4: params object[] args)
5: {
6: ResourceExpressionFields fields = GetResourceFields(expression, "~/");
7: return GetGlobalResource(fields, args);
8: }
9:
10: public static string Resource(this HtmlHelper htmlHelper,
11: string expression, params object[] args)
12: {
13: string path = (string)htmlHelper.ViewData[LocalizationWebFormView.ViewPathKey];
14: if (string.IsNullOrEmpty(path))
15: path = "~/";
16:
17: ResourceExpressionFields fields = GetResourceFields(expression, path);
18: if (!string.IsNullOrEmpty(fields.ClassKey))
19: return GetGlobalResource(fields, args);
20:
21: return GetLocalResource(path, fields, args);
22: }
23:
24: static string GetLocalResource(string path, ResourceExpressionFields fields,
25: object[] args)
26: {
27: return string.Format((string)HttpContext.GetLocalResourceObject(path,
28: fields.ResourceKey, CultureInfo.CurrentUICulture), args);
29: }
30:
31: static string GetGlobalResource(ResourceExpressionFields fields, object[] args)
32: {
33: return string.Format((string)HttpContext.GetGlobalResourceObject(
34: fields.ClassKey, fields.ResourceKey, CultureInfo.CurrentUICulture), args);
35: }
36:
37: static ResourceExpressionFields GetResourceFields(string expression,
38: string virtualPath)
39: {
40: var context = new ExpressionBuilderContext(virtualPath);
41: var builder = new ResourceExpressionBuilder();
42: return (ResourceExpressionFields)builder.ParseExpression(
43: expression, typeof(string), context);
44: }
45: }
Again, thanks to Brad, this implementation seems "less dirty". You can download the source for this here.
5a8fe5de-931f-4709-8ec1-ba00c72c6782|6|3.0