MvcSiteMapProvider icon indicating copy to clipboard operation
MvcSiteMapProvider copied to clipboard

Implicit Localization not working

Open mvandiest opened this issue 10 years ago • 17 comments

Currently implicit localization looks at the RequestCacheableSiteMap.ResourceKey property to resolve the classKey when attempting to resolve implicit localization resources.

The classKey is used in the following code in StringLocalizer.cs:

globalResourceObject = httpContext.GetGlobalResourceObject(classKey, implicitResourceKey + "." + attributeName) as string;

From what I can tell the ResourceKey property is never set and thus the classKey value is always "".

Is this a bug or am I missing how to set this via configuration?

mvandiest avatar Sep 12 '13 19:09 mvandiest

I am pretty sure this is a bug. The explicit resource localization is setup in the MvcMusicStore demo and has been tested pretty thoroughly, but as far as I know the implicit resource localization has never been tested. You are correct in that the ResourceKey property of the SiteMap object never gets called.

I am digging through Microsoft's code to see if I can figure out where this property is supposed to be set. This could take awhile.

NightOwl888 avatar Sep 12 '13 20:09 NightOwl888

Thanks for the quick response. Hopefully there's a simple solution.

mvandiest avatar Sep 12 '13 20:09 mvandiest

Okay, I'm not sure I fully understand exactly how implicit localization is supposed to work, but Microsoft set the value to the name of the XML file. We can't do that because the SiteMap object isn't necessarily based on a file (in fact, the next release will have an option to turn off the XML file entirely).

However, there is a SiteMapCacheKey that is used throughout the provider as a way to reference a specific SiteMap. If I understand its usage correctly, that should suffice as the "object key".

There is a small problem, though - the default implementation uses the domain name as part of the key, so it would change between your local system and production. Currently, the only way to override it is to use an external DI container to inject your own implementation of ISiteMapCacheKeyGenerator as described here.

You are in luck that this particular property is public so setting it can be done without a breaking change. You are also in luck that you caught me right in the middle of a release and it isn't too late to add the fix (and this can be done in a few hours).

To address the changing cache key problem (assuming you want to use the internal DI container), I could probably add a configuration setting so you could just set the result of ISiteMapCacheKeyGenerator in web.config to whatever you want.

Does that work for you?

NightOwl888 avatar Sep 12 '13 21:09 NightOwl888

Well, the first problem - setting the key - has been addressed.

The second problem - the fact that the key changes when the domain name does - is not something that can be fixed without breaking existing DI configurations due to the way it wires up default classes. I am afraid in your case you must use an external DI container to inject your own ISiteMapCacheKeyGenerator implementation in order to use this feature.

NightOwl888 avatar Sep 12 '13 21:09 NightOwl888

Ok, v4.3.0 has been released. It is now setting the value of the objectKey to that of the SiteMapCacheKey.

Sorry it took so long :).

NightOwl888 avatar Sep 12 '13 22:09 NightOwl888

Awesome! Hopefully I can get around to testing soon. Really appreciate it.

mvandiest avatar Sep 12 '13 23:09 mvandiest

Do you have any idea how to answer #109, Resource from a different assembly? That one has been open for a while, but I really don't even know where to start. I would appreciate you providing an answer (if you know it) so the issue can be closed.

NightOwl888 avatar Sep 12 '13 23:09 NightOwl888

I do not believe .net intrinsically supports external resources for implicit or explicit sitemap localization.

I'd be happy to do some investigation as I am going to have the same issue soon.

mvandiest avatar Sep 13 '13 00:09 mvandiest

Cursory investigation points to Custom Resource Providers:

http://stackoverflow.com/questions/3395009/localization-of-web-sitemap-using-a-resx-file-in-other-project

http://msdn.microsoft.com/en-us/library/aa905797.aspx

I'll be testing this out in the near future and will confirm.

mvandiest avatar Sep 13 '13 00:09 mvandiest

I am curious to know if you got the implicit localization working after the key was set to the SiteMapCacheKey. Also, any updates on whether the resource provider allows you to use resources in a different assembly?

NightOwl888 avatar Sep 25 '13 14:09 NightOwl888

I got it working after implementing my own ISiteMapCacheKeyGenerator (which just returned filename as a cache key) and using resourceKey attribute in sitemap file. By default it generated domain based cache key which was not suitable for implicit localization.

siimv avatar Sep 25 '13 15:09 siimv

Given this new information, I am marking this issue closed.

The ISiteMapCacheKeyGenerator fix is a breaking change, and will need to be addressed in version 5. In the meantime, at least there is a workable solution by implementing this interface yourself.

I am still curious if there is any particular reason the implicit resource key needs to be a file name, or if it just needs to be a key associated with a given SiteMap instance.

NightOwl888 avatar Oct 02 '13 09:10 NightOwl888

As far as I understand, implicit resource key is used to locate proper resx file. It doesn't have to be named same as sitemap file (if there even is a file), but basically it's the resx file name which is located in the App_GlobalResources directory.

siimv avatar Oct 02 '13 10:10 siimv

Well, not exactly. Implicit Keys are used with App_LocalResources.

From my research I've come accross the following realizations:

  • App_GlobalResources are resolved by the asp.net runtime passing the full namespace path of a Type to the ResourceManager.
  • App_LocalResources only pass the TypeName which is resolved by the resource file residing directly next to the thing your localizing.
  • By following best practices of separating all resources to an external assembly, Implicit Keys cannot work. Since only TypeName is passed, external assemblies are unable to resolve them properly.

mvandiest avatar Oct 02 '13 15:10 mvandiest

Okay, I am reopening this issue.

Upon further investigation, the Microsoft SiteMapNode object has a GetImplicitResourceString() method that is implemented as follows:

protected string GetImplicitResourceString(string attributeName)
{
    if (attributeName == null)
    {
        throw new ArgumentNullException("attributeName");
    }
    string globalResourceObject = null;
    if (!string.IsNullOrEmpty(this._resourceKey))
    {
        try
        {
            globalResourceObject = ResourceExpressionBuilder.GetGlobalResourceObject(this.Provider.ResourceKey, this.ResourceKey + "." + attributeName) as string;
        }
        catch
        {
        }
    }
    return globalResourceObject;
}

The corresponding method in MvcSiteMapProvider is:

        protected virtual string GetImplicitResourceString(string attributeName, string implicitResourceKey, string classKey)
        {
            if (attributeName == null)
            {
                throw new ArgumentNullException("attributeName");
            }
            string globalResourceObject = null;
            if (!string.IsNullOrEmpty(implicitResourceKey))
            {
                var httpContext = mvcContextFactory.CreateHttpContext();
                try
                {
                    globalResourceObject = httpContext.GetGlobalResourceObject(classKey, implicitResourceKey + "." + attributeName) as string;
                }
                catch
                {
                }
            }
            return globalResourceObject;
        }

The primary difference between the two is the fact that Microsoft's implementation calls the GetGlobalResourceObject method on the ResourceExpressionBuilder. This method gets a global resource provider based on the classKey that is passed in and uses that provider to get the resource rather than using the httpContext object.

The "thing that you are localizing" can be the SiteMapCacheKey (which is effectively its name), it is just more intuitive to use the name of the file if there is one. Microsoft's implementation ends up using the GlobalResources instead of LocalResources because of the limitation of true implicit localization you describe. Microsoft follows the intuitive pattern to name the GlobalResource file as Web.sitemap.resx to map to Web.sitemap.

From what I can tell this can be done (after all, Microsoft did it), it is just that it needs some additional implementation to be complete. Although, some additional thought will need to be given for resources that apply to nodes derived from external sources - it seems the best approach might be to map the IResourceProvider 1 to 1 with ISiteMapNodeProvider (or at least make it possible to do so).

NightOwl888 avatar Oct 02 '13 17:10 NightOwl888

FYI - I have opened a new issue #344 to gather requirements for a new extension point for localization. I am considering dropping support for implicit localization in a future major version release. Your feedback is appreciated.

NightOwl888 avatar Aug 09 '14 15:08 NightOwl888

I read through the post and currently can't think of any other requirements. Thanks.

On Sat, Aug 9, 2014 at 11:06 AM, NightOwl888 [email protected] wrote:

FYI - I have opened a new issue #344 https://github.com/maartenba/MvcSiteMapProvider/issues/344 to gather requirements for a new extension point for localization. I am considering dropping support for implicit localization in a future major version release. Your feedback is appreciated.

— Reply to this email directly or view it on GitHub https://github.com/maartenba/MvcSiteMapProvider/issues/228#issuecomment-51689038 .

mvandiest avatar Aug 11 '14 11:08 mvandiest