Skip to content
This repository has been archived by the owner on Nov 29, 2018. It is now read-only.

How To Localize Controller that lives in a class library? #152

Closed
joeaudette opened this issue Nov 24, 2015 · 5 comments
Closed

How To Localize Controller that lives in a class library? #152

joeaudette opened this issue Nov 24, 2015 · 5 comments

Comments

@joeaudette
Copy link

I have this modified version of LocalizationWebSite

https://github.com/joeaudette/experiments

I can localize the HomeController with

IHtmlLocalizer<HomeController> localizer

because it lives in the main webapp and the resx file is

 /Resources/LocalizationWebSite.Controllers.HomeController.fr.resx

The example solution also has a class library with a FooController.cs

I use

IHtmlLocalizer<FooController>

in a similar way and in the webapp project I have

/Resources/SomeWebLib.Controllers.FooController.fr.resx

but this does not get localized using the same technique

Is this a bug or am I supposed to use a different approach for localizing controllers that are not in the main webapp?

@joeaudette joeaudette changed the title How To Localize Contoller that lives in a class library? How To Localize Controller that lives in a class library? Nov 24, 2015
@joeaudette
Copy link
Author

I managed to determine the cause and a solution to this problem, but I'm reluctant to propose it as a fix or make a pull request because I don't fully understand the design goals and no-one has commented so far to indicate if this is considered a bug or whether this is expected behavior and some other solution should be used for localizing controllers that live in a class library.

I've updated my example here:
https://github.com/joeaudette/experiments

I made a CustomResourceManagerStringLocalizerFactory by copying the standard one

I know there is some problem with tooling that prevents some localization, I tried using VS to debug with kestrel instead of IIS Express but it seemed the tooling problem happens also in that scenario so I added logging since I was not able to debug with correct localization. I just launched it from the command line and used logging to figure out what was going on.

What I found was at line 66 in ResourceManagerStringLocalizerFactory

var assembly = typeInfo.Assembly;

The assembly passed into ResourceManager is the assembly containing the type to be localized which would make sense if we wanted ResourceManager to look for the resources inside that assembly.

I have not figured out how to make it work in that scenario at all, maybe there is a way with embedded resx files?

In any case my preferred situation is to have all the needed resx files in a single folder in the web app and not be embedded inside assemblies because it if far easier to add translations and to get others to make translations by copying and modifying existing resx files.
This is similar to the old App_GlobalResources approach except we don't use a default resx file like SomeResource.resx, instead we only have culture specific resx files and the default string is whatever we pass in to the IStringLocalizer

Since we want it to look for resx files in the web app in the "Resources" folder as defined in our Startup

services.AddMvc().AddViewLocalization(options => options.ResourcesPath = "Resources");

we really need it to pass in the assembly of the webapp not the assembly of the class library, otherwise it will not look where we want it to look.

I added this right after line 66:

if(!(assembly.FullName.StartsWith(_applicationEnvironment.ApplicationName)))
{
assembly = Assembly.Load(_applicationEnvironment.ApplicationName);
}

and sure enough it worked

Now FooController which lives in the SomeWebLib calss library project in my example can be localized with

IHtmlLocalizer<FooController>

and by putting the resx file in the webapp under the Resources folder as

SomeWebLib.Controllers.FooController.fr.resx

Hope this is helpful. I would certainly appreciate some feedback on whether this issue is a bug.

I guess what I have learned is if this is not considered a bug I could get my desired behavior by implementing a custom IStringLocalizerFactory. That would be acceptable if it is really needed but I would prefer if this scenario could work without doing that. I " think" using a shared resource folder is going to be a very common usage scenario.

@Eilon
Copy link
Member

Eilon commented Dec 1, 2015

You generally don't access the resources of one project from another project. String resources and localization are an implementation detail of a library. Only libraries that build in a notion of getting resources from other locations would support such behavior, but that is very rare. BTW this is how localization of apps in .NET has always worked.

One example of a library that supports localizing some built-in resources is MVC, which has a ModelBindingMessageProvider for localizing specific error messages (but not arbitrary messages).

@Eilon Eilon closed this as completed Dec 1, 2015
@joeaudette
Copy link
Author

Thanks @Eilon that seems reasonable that ResourceManagerStringLocalizerFactory will not pass in the assembly of the web app to ResourceManager therefore ResourceManager will not look for the resx file for a class library type in the resource folder configured for the web app.

But then if my controller FooController lives in a class library and it takes a dependency on

IHtmlLocalizer<FooController> 

how do I make it work so that ResourceManager will find my resx file when ResourceManagerStrinbLocalizerFactory passes my assembly into ResourceManager?

Or more specifically how do I package the resx file in the class library. Am I supposed to still do it without a default .resx file and without a .designer.cs, ie the same approach as in my web app but now as an embedded resource instead of in a "Resources" folder?
Does it have to be in any particular folder and is the naming convention for the resx file the same as in the web app ie

SomeWebLib.Controllers.FooController.fr.resx

That would make sense to me but I think I tried to do that and it did not work.

Is it just the tooling problem that is making this not work right now?

Or am I supposed to use a default .resx file with a designer.cs to localize a controller if it lives in a class library. I think with that approach we can't have spaces in the key so that would not play well with the normal way to use IHtmlLocalizer.

So my question is what is the correct approach for localization of a controller that lives in a class library, clearly it is a little different than the approach for localizing a controller that lives directly in the web app.

@Eilon
Copy link
Member

Eilon commented Dec 2, 2015

For a class library the resx files are "source files" - they are used only at compile time and never at runtime. The data in the resx files will be embedded into satellite DLLs and then ResourceManager is used to load the resources from there. The ResourceManagerStringLocalizer can pull resources from these DLLs and use them in the app.

@DamianEdwards is working on updating his sample app to use all the latest features of localization so I think once that's published, some of these scenarios should be clearer.

@joeaudette
Copy link
Author

Ok, that is how I thought it should work but it does not work and I think #157 is why it doesn't work. I was going to point that out but @martinba1 beat me to it.

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

No branches or pull requests

2 participants