49

Hi I have a question about the SharedResources file. It is glanced over in the tutorial here: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization, and I'm not sure if I get it correctly.

I am supposed to create a SharedResources.cs class, but where should i put it and should it be empty or do I need to fill it with some data?

Same goes for the resource file, should I create a SharedResources.da.resx file and put all my shared strings there? Where should it go?

And when I use IHtmlLocalizer<SharedResources> do I just write @using and point it to the namespace where SharedResources.cs resides?

I tried putting SharedResources.cs and SharedResources.da.resx in the Resources folder and use it to change website language to Danish, but it does not work. Using dedicated Resource file like Index.da.resx and IViewLocalizer works fine, but IHtmlLocalizer<SharedResources> does not seem to work.

When I looked at the example project linked to at the bottom of the page I didn't find any place where SharedResources is used, it would be great if somebody updated it with an example of that.

Here's how I tried to do it:

Views/Home/Index.cshtml:

@using Funkipedia.Resources
@using Microsoft.AspNetCore.Mvc.Localization
@inject IHtmlLocalizer<Shared> SharedLocalizer
...
<p>@SharedLocalizer["Hei"]</p>
...

At top of ConfigureServices in Startup.cs:

services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddMvc()
  .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
  .AddDataAnnotationsLocalization();

At top of Configure in Startup.cs:

var supportedCultures = new List<CultureInfo>
{
       new CultureInfo("nb-NO"),
       new CultureInfo("sv-SE"),
       new CultureInfo("da-DK")
};

app.UseRequestLocalization(new RequestLocalizationOptions
{
       DefaultRequestCulture = new RequestCulture("nb-NO"),
       SupportedCultures = supportedCultures,
       SupportedUICultures = supportedCultures
});

Resources folder contains empty class called Shared.cs and Shared.da.resx which contains shared strings. Do I maybe need to change the name of it to SharedResources.cs and SharedResources.da.resx?

Daniel Aaron Salwerowicz
  • 1,713
  • 2
  • 11
  • 16

3 Answers3

106

Okay, after some digging around and even more trial and error I've found answers to my questions and got everything to work. Here's what I found:

I am supposed to create a SharedResources.cs class, but where should i put it and should it be empty or do I need to fill it with some data?

ANSWER: SharedResources.cs can be placed in the root folder of project or in Resources folder, but the most important thing is that namespace should be set to the root of the project. In my case namespace Funkipedia. And it does not need to contain any data, just the class declaration.

Same goes for the resource file, should I create a SharedResources.da.resx file and put all my shared strings there? Where should it go?

ANSWER: Yes, you need to create a resource file called the same as the .cs file and it needs to be put in the Resources folder.

And when I use IHtmlLocalizer<SharedResources> do I just write @using and point it to the namespace where SharedResources.cs resides?

ANSWER: When it comes to using IHtmlLocalizer and/or IStringLocalizer in view you need to write this at the top of .cshtml file:

@using Microsoft.AspNetCore.Mvc.Localization
@using Microsoft.Extensions.Localization
@inject IViewLocalizer Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@inject IHtmlLocalizer<SharedResources> SharedHtmlLocalizer

Note that @using Microsoft.Extensions.Localization is only needed if you use IStringLocalizer

I hope that this will help others who might be new to resource files and localization of ASP.NET Core applications.

Daniel Aaron Salwerowicz
  • 1,713
  • 2
  • 11
  • 16
  • 8
    You are a Genius! – Max May 26 '17 at 17:10
  • 1
    Hi, Do you have any examples of Github? – sintetico82 Jun 08 '17 at 15:54
  • 8
    Sorry for late reply, here's an easy example I threw together. https://github.com/MormonJesus69420/SharedResourcesExample – Daniel Aaron Salwerowicz Jun 10 '17 at 16:07
  • 2
    keep in mind that the ResourcesPath and folders will be case sensitive – animalito maquina Nov 08 '17 at 08:39
  • thx for sharing that, helped a lot. Based on @superjobs comment, i found out that the SharedResources.cs can be placed anywhere in project. The tricky part is name the resx file correctly. I have edit the answer to make it clear. – bruno.almeida Jan 12 '18 at 12:22
  • Great answer. The number of up votes is a good indicator of how ambiguous the MS documentation is on this subject. – Darren Lewis Feb 26 '18 at 14:47
  • 1
    Lets say you have a .Net core website project with two class libraries referenced. You then can't localise anything in the class libraries as they know nothing about a class placed in the resources folder in the web project? – leen3o Nov 27 '18 at 15:48
  • Thank you dude. After hours of frustration this solved my problem. Also MS documentation is so bad. – r.mirzojonov Jan 02 '19 at 19:08
  • 1
    I followed your guidance, but in my case (VS 2019), no codebehind file was generated. I had to set the Access Modifier of the resx file to `Internal` or `Public`. After I did this it worked. – Johan Vergeer Jun 18 '19 at 09:07
  • This should be in the official documentation. It took me a while to figure that out and just found out your post because I was wondering how we could inject it in the view. – Sauleil Nov 11 '19 at 18:22
  • Just a heads up: if you are placing SharedResources.cs in the Resources directory alongside your resource files (SharedResources.xx.resx) AND you are using Visual Studio (in my case 2019), you may experience issues with your .resx files not being found at runtime. I can only deduce that this is a side-effect of VS nesting similarly named files. – nokturnal Jun 19 '20 at 13:09
8

I'd like to add the setup that's working for my team as well. It's based on the same principle as yours (of course), but I believe it allows some more flexibility on your files location, as it does not force you to put resource-related files in project root.

My understanding is that IStringLocalizer<T> has the concept of a placeholder Type, whose fullname will be converted into a relative path and used to find the actual resource file. To do this conversion, it also uses info from LocalizationOptions.ResourcesPath, if any.

Say you have:

// in ProjectRoot\Startup.cs

services.AddLocalization(opts =>
{
  opts.ResourcesPath = "Localized";
});

// in ProjectRoot\Area\Whatever\SomeClass.cs

namespace Com.Company.Project.Area.Whatever
{
  public class SomeClass
  {
    public SomeClass(IStringLocalizer<SomeClass> localizer)
    {
      // ...
    }
  }
}

So here's what happens, step-by-step, just to give an idea:

  1. SomeClass fullname: Com.Company.Project.Area.Whatever.SomeClass
  2. convert that to .resx file path: Com\Company\Project\Area\Whatever\SomeClass.resx
  3. prefix that with ResourcesPath content: Localized\Com\Company\Project\Area\Whatever\SomeClass.resx

That's the actual path where resource file(s) will be looked up.

So, all in all, you can place your SharedResources.cs empty class wherever you want, as long as you replicate its fullname as a path under ResourcesPath folder under project root.

In the example:

\
--Area
  --Whatever
    --SomeClass.cs
--Localized
  --Com
    --Company
      --Project
        --Area
          --Whatever
            --SomeClass.resx
            --SomeClass.fr.resx
            --SomeClass.da.resx

Under the cover, that directory tree is needed because classes generated out of resx file will take their namespace from the directory tree, and also because string localizer will not strip root namespace when prefixing placeholder type with ResourcesPath.

superjos
  • 10,834
  • 4
  • 79
  • 120
  • That's really clever, haven't though about that, thanks a lot superjos – Daniel Aaron Salwerowicz Jul 07 '17 at 17:06
  • 1
    Glad it might help! I've also fiddled with not having any ResourcesPath, thinking that I could place resx files in same directory next to placeholder class, but that did not work and I quit trying shortly after. – superjos Jul 07 '17 at 23:52
8

Here is what worked for me (in ASP.NET Core 2.0):

  1. Place SharedResources.cs in a folder called Resources.
  2. Place SharedResources.xx-yy.resx resource files in Resources folder too.
  3. Call services.AddLocalization() with no ResourcesPath option.
  • Adding the culture details to the SharedResources filename isn't necessary (i.e. `SharedResource.resx` also works) if you are meaning to use SharedResources file across all cultures or just as a culture agnostic string collection. – Menol Oct 23 '19 at 16:01