Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I use IStringLocalizer in a class which is not a Controller

I understand how to use the IStringLocalizer interface in a class which derives from Controller, for example as described here or here.

What would be the correct way to use IStringLocalizer in a class which does not derive from Controller?

I cannot find any examples addressing this question.

Is it always necessary to pass in an IStringLocalizer or IStringLocalizerFactory to the constructor?

Note.
I know that this is a fairly generic question (and Stack Overflow is for concrete programming questions). The background is that I make a localization tool for .NET projects. I am trying to figure out what changes my tool has to make to the source code to support localization in ASP.NET Core projects.

like image 602
Phil Jollans Avatar asked May 27 '17 07:05

Phil Jollans


People also ask

What is globalisation and localisation in ASP net?

Globalization is the process of designing the application in such a way that it can be used by users from across the globe (multiple cultures). Localization, on the other hand, is the process of customization to make our application behave as per the current culture and locale.


1 Answers

It doesn't matter if a class derives from Controller.

It is important that your objects are created by the Dependency Injection Container and not by using the new operator, as described here. If you want to use the ASP.NET Core localization mechanism in all your classes, you must adopt this pattern for object creation.

This page gives a good description of how to create objects, but I will try to give an example of how it works with localization.

I have created a class MyHelper. The class expects the localizer object to be passed in to the constructor. It contains a single property Hello, which returns a localized string.

namespace AddingLocalization.Classes
{
  public class MyHelper
  {
    private readonly IStringLocalizer<MyHelper> _localizer;

    public MyHelper(IStringLocalizer<MyHelper> localizer)
    {
      _localizer = localizer;
    }

    public string Hello
    {
      get
      {
        return _localizer["Hello World."];
      }
    }

  }
}

In the ConfigureServices method in the Startup class, I have added the boilerplate code described here and added a line to register the class MyHelper with the Dependency Injection container.

public void ConfigureServices(IServiceCollection services)
{
  services.AddLocalization(opts => opts.ResourcesPath = "Resources");

  services.AddMvc()
          .AddViewLocalization (
               LanguageViewLocationExpanderFormat.Suffix,
               opts => opts.ResourcesPath = "Resources" )
          .AddDataAnnotationsLocalization();

  // This line registers the class MyHelper with the 
  // Dependency Injection Container.      
  services.AddTransient<MyHelper>();
}

In my controller class, I have added a parameter of type MyHelper to the constructor, which is stored in a member variable.

public class HomeController : Controller
{
  private readonly IStringLocalizer<HomeController> _localizer ;
  private readonly MyHelper                         _h ;

  public HomeController ( IStringLocalizer<HomeController> localizer,
                          MyHelper                         h )
  {
    _localizer = localizer;
    _h         = h ;
  }

  ...

  public IActionResult About()
  {
    ViewData["Message"] = _h.Hello ;
    return View();
  }

  ...
}  

Because the class MyHelper has been registered with the dependency injection container, it creates this object and passes it in to the constructor automatically. This is the magic performed by the dependency injection container.

In the About() method, I fetch the property from the MyHelper object.

That's about it for the code, but I wanted to be certain that it will actually read the string from a resource file.

The naming convention for resource files is described here:

It is based on the full name of the class, without the name of the assembly. In my case class is called AddingLocalization.Classes.MyHelper and the assembly is called AddingLocalization, so the relevant name is Classes.MyHelper.

There are actually two naming conventions, using dots or subdirectories, so we can call the resource file one of

  • Classes.MyHelper.resx
  • Classes\MyHelper.resx

The boilerplate code in ConfigureServices specified the ResourcesPath "Resources", so this is where we must place the resource file. I chose the second naming option, so my resource file is

  • Resources\Classes\MyHelper.resx

as you can see in the solution explorer

2017-05-28_09-30-59.png

We do not need Visual Studio to create a file MyHelper.Designer.cs to access the resources, so we should clear out the CustomTool property for the resource file.

enter image description here

This is important, because the names will not be valid resource names and will probably generate error messages, if we do not disable the custom tool.

(Previous localization methods (from Microsoft) have always used fabricated resource names (e.g. with underscrore instead of space) to access resources. The new ASP.NET core localization uses the original string as the resource name.)

Finally I have defined a resource string as shown below:

enter image description here

I haven't actually tried accessing resources in a different language (yet), but the localizer object did read the resource correctly.

like image 101
Phil Jollans Avatar answered Oct 19 '22 02:10

Phil Jollans