Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implicit Localization not working #228

Open
mvandiest opened this issue Sep 12, 2013 · 17 comments
Open

Implicit Localization not working #228

mvandiest opened this issue Sep 12, 2013 · 17 comments

Comments

@mvandiest
Copy link

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?

@NightOwl888
Copy link
Collaborator

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.

@mvandiest
Copy link
Author

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

@NightOwl888
Copy link
Collaborator

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
Copy link
Collaborator

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
Copy link
Collaborator

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 :).

@mvandiest
Copy link
Author

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

@NightOwl888
Copy link
Collaborator

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.

@mvandiest
Copy link
Author

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
Copy link
Author

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.

@NightOwl888
Copy link
Collaborator

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?

@siimv
Copy link

siimv commented Sep 25, 2013

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.

@NightOwl888
Copy link
Collaborator

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.

@siimv
Copy link

siimv commented Oct 2, 2013

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.

@mvandiest
Copy link
Author

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.

@NightOwl888
Copy link
Collaborator

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
Copy link
Collaborator

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.

@mvandiest
Copy link
Author

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
#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
#228 (comment)
.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants