maartenba / MvcSiteMapProvider

An ASP.NET MVC SiteMapProvider implementation for the ASP.NET MVC framework.
Microsoft Public License
538 stars 218 forks source link

Requirements for New Localization Extension Point #344

Open NightOwl888 opened 10 years ago

NightOwl888 commented 10 years ago

It has been brought to my attention that using Local and Global resources in conjunction with MVC is difficult to do and is best avoided because the way the default settings are set up by Visual Studio when adding these resource files will not compile down to something that can be accessed via MVC. Also there were a few requests from people who want to provide their localized resources in a separate library from the MVC application, and the current proposed way is not very flexible in that it

  1. Requires additional logic beyond providing configuration for a ResourceManager
  2. Only supports a single resource file in a single assembly

With that in mind, I think it is time to create a new extension point that accounts for these limitations and makes use of modern techniques and extension points of ASP.NET that are commonly used by MVC (without dropping support for the existing functionality).

I am not really a subject matter expert on globalization, so I would really appreciate any feedback on what use cases need to be solved for and how best to tackle them.

In reading Microsoft's extensive document on ASP.NET localization, I notice that there are actually 2 extensibility points that ASP.NET provides:

  1. ResourceManager - Reads resources from a specific file in a specific assembly.
  2. IResourceProvider/ResourceProviderFactory - Provides alternative logic for getting local and global resources rather than just from the stock ASP.NET folders.

If I understand correctly, the second extension point is designed to plug directly into ASP.NET and affects the HttpContext.Current.GetGlobalResourceObject method (which MvcSiteMapProvider already uses). Therefore, implementing these abstractions and then providing them via web.config is and always has been an option for providing resources in a separate assembly (even if not using DI). Also, it wouldn't make sense for MvcSiteMapProvider to utilize IResourceProvider or ResourceProviderFactory directly since these plug into ASP.NET.

So (I think) the only real nut to crack here is how to make it simple to extend MvcSiteMapProvider to allow the user to supply resources from assemblies that are external to the ASP.NET application in an extensible way. Let's start with a list of requirements. Feel free to add or provide feedback on the requirements list - I will update the list in this 1st post as the feedback rolls in so it is easy to read in one place.

  1. Personally, I am in favor of dropping support for implicit resources (or at least discouraging it until it can be dropped in a major version). This doesn't seem to have any place in a framework as complex as MvcSiteMapProvider - essentially, implicit resources are supposed to be "right next to" the thing you are localizing (assuming the thing is a file of some type) - see #228. However, MvcSiteMapProvider localizes properties of nodes, which can be provided in other ways than files. This only seems to have a place for localizing attributes if they are provided in XML and that XML is provided as a file (which is not a prerequisite of XmlSiteMapNodeProvider).
  2. For backward compatibility with the current syntax for specifying resources, the extension point should provide a method to lookup a value by "providerName" and "resourceKey".
  3. Support should be added for a default value as in microsoft's implementation http://msdn.microsoft.com/en-us/library/vstudio/ms178427(v=vs.100).aspx.
  4. There should be an in-the-box implementation that allows providing a ResourceManager instance.
  5. There should be an in-the-box implementation that allows the developer to specify assemblyName/fullyQualifiedResourceName pairs to wire up multiple resources automatically from various assemblies (and their localized counterparts).
  6. If a ResourceManager-based implementation fails to find a particular fully qualified resource file in an assembly, it should throw an exception that includes a list of all of the existing fully qualified resources in the assembly.
  7. There should be an implementation that calls the HttpContext.Current.GetGlobalResourceObject method that is wired up by default for backward compatibility with the existing functionality and to maintain support for the underlying IResourceProvider implementation provided by ASP.NET.

My current thinking is to add this extensibility point as a constructor argument to the default StringLocalizer class, replacing the explicit call to httpContext.GetGlobalResourceObject with the new provider. Essentially, that means that this feature can be added in a minor version without dropping support for any existing code that relies on the current implementation.

nrm101 commented 9 years ago

I think a well working implementation is like the System.ComponentModel.DataAnnotations;

https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.displayattribute.resourcetype(v=vs.95).aspx

[Display(ResourceType=typeof(DisplayResources), Name="LName", Description="LNameDescription")] public string LastName { get; set; }

herrquark commented 9 years ago

So where are we on this topic? Currently I'm forced to modify MvcSitemapProvider source code and manually reference custom .dll files which is no good

NightOwl888 commented 9 years ago

@herrquark - This implementation has not yet been started, and my current schedule doesn't allow much time to work on MvcSiteMapProvider.

However, there is no reason to modify the source code and compile your own version of MvcSiteMapProvider in order to implement this functionality. Also, this is exactly the way it was done in MvcSiteMapProvider v3.x, which is why this extension point hasn't been given a higher priority.

Current Approaches for Accessing External Resources

If you are using external DI. All you need to do is implement IStringLocalizer as shown in this example. Also note there is a working demo of the solution.

Alternatively, you could implement a ResourceProviderFactory as specified in the MSDN documentation to provide resources from an external assembly for your entire application (whenever you call GetGlobalResourceObject). Since MvcSiteMapProvider calls HttpContext.GetGlobalResourceObject it will automatically use your implementation. You can plug your implementation directly into ASP.NET by using the globalization section of Web.config.

<globalization uiCulture="auto" culture="auto" resourceProviderFactoryType="CustomResourceProviders.ExternalResourceProviderFactory, CustomResourceProviders, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f201d8942d9dbbb1" />