LazZiya / XLocalizer

Localizer package for Asp.Net Core web applications, powered by online translation and auto resource creating.
https://docs.ziyad.info
128 stars 14 forks source link

Resource selection order #5

Closed FJGR65 closed 3 years ago

FJGR65 commented 3 years ago

First of all: thank you very much for the work you are doing, it is impressive.

I have encountered a couple of problems. To do this, I put my configuration: ASP NET Core 3.1

     services.Configure<RequestLocalizationOptions>(options =>
      {
        var supportedCultures = new[]
        {
          new CultureInfo("es-ES"),
          new CultureInfo("en-US")
        };
        options.DefaultRequestCulture = new RequestCulture(culture: "es-ES", uiCulture: "es-ES");
        options.SupportedCultures = supportedCultures;
        options.SupportedUICultures = supportedCultures;

        options.RequestCultureProviders.Insert(0, new RouteSegmentRequestCultureProvider(supportedCultures));
        options.RequestCultureProviders.Insert(1, new CookieRequestCultureProvider() { CookieName = "_KCul" });
      });

      services
        .AddRazorPages(options =>
        {
          options.RootDirectory = "/Pages";
          options.Conventions.AllowAnonymousToPage("/About");
          options.Conventions.AllowAnonymousToPage("/Contact");
          options.Conventions.AllowAnonymousToPage("/Error");
          options.Conventions.AllowAnonymousToPage("/Index");
          options.Conventions.AllowAnonymousToPage("/Privacy");
          options.Conventions.AllowAnonymousToPage("/StatusCode");
          options.Conventions.AllowAnonymousToPage("/Export");
          options.Conventions.AuthorizeAreaFolder("Identity", "/Account/Manage");

          options.Conventions.Insert(0, new RouteTemplateModelConventionRazorPages());
        })
        .AddXLocalizer<ViewLocalizationResource>(ops =>
        {
          ops.ResourcesPath = "LocalizationResources";
          ops.AutoTranslate = false;
          ops.AutoAddKeys = false;
          ops.UseExpressMemoryCache = !Development;
        })
        .AddXLocalizer<ViewLocalizationResource, MyMemoryTranslateService>(ops =>
        {
          ops.ResourcesPath = "LocalizationResources";
          ops.AutoTranslate = true;
          ops.AutoAddKeys = true;
          ops.UseExpressMemoryCache = !Development;
        });

      services.AddSingleton<IModelBindingErrorMessagesProvider, SpanishModelBindingErrors>();

      // Register the built-in Xml resource provider
      services.AddSingleton<IXResourceProvider, ResxResourceProvider>();
      services.AddTransient<IXResourceExporter, XmlResourceExporter>();
      services.AddSingleton<IXResourceProvider, XmlResourceProvider>();
      services.AddHttpClient<ITranslator, MyMemoryTranslateService>();`

1st In Development, debugging with Visual Studio, it never uses the resources that exist in the " .resx" file. If it exists in the " .xml", use it. If it doesn't exist, use "MyMemoryTranslateService" and add it to the " .xml" But it never looks for the resource in the " .resx" 2nd In production, never use the existing resources in the files " .resx" or " .xml", use "MyMemoryTranslateService". If the resource exists in " .resx" or in " .xml", it does not use them, but calls "MyMemoryTranslateService" and stores it in "ExpressMemoryCache".

How can the behavior be changed so that the search order of the resources is: 1st "ExpressMemoryCache" 2nd " .resx" 3rd " .xml" 4th "MyMemoryTranslateService"

I am sorry for my bad english. I use Google Translator

Thank you very much for your help

This is an update:

I have changed the order of the instructions and now it looks more like what I want:

` services.AddHttpClient<ITranslator, MyMemoryTranslateService>();

  services.Configure<RequestLocalizationOptions>(options =>
  {
    var supportedCultures = new[]
    {
      new CultureInfo("es-ES"),
      new CultureInfo("en-US")
    };
    options.DefaultRequestCulture = new RequestCulture(culture: "es-ES", uiCulture: "es-ES");
    options.SupportedCultures = supportedCultures;
    options.SupportedUICultures = supportedCultures;

    options.RequestCultureProviders.Insert(0, new RouteSegmentRequestCultureProvider(supportedCultures));
    options.RequestCultureProviders.Insert(1, new CookieRequestCultureProvider() { CookieName = "_KCul" });
  });

  services.AddLocalization(o => o.ResourcesPath = "LocalizationResources");

  // Register the built-in Xml resource provider
  services.AddSingleton<IXResourceProvider, XmlResourceProvider>();
  services.AddSingleton<IXResourceProvider, ResxResourceProvider>();
  services.AddTransient<IXResourceExporter, XmlResourceExporter>();

  services
    .AddRazorPages(options =>
    {
      options.RootDirectory = "/Pages";
      options.Conventions.AllowAnonymousToPage("/About");
      options.Conventions.AllowAnonymousToPage("/Contact");
      options.Conventions.AllowAnonymousToPage("/Error");
      options.Conventions.AllowAnonymousToPage("/Index");
      options.Conventions.AllowAnonymousToPage("/Privacy");
      options.Conventions.AllowAnonymousToPage("/StatusCode");
      options.Conventions.AllowAnonymousToPage("/Export");
      options.Conventions.AuthorizeAreaFolder("Identity", "/Account/Manage");

      options.Conventions.Insert(0, new RouteTemplateModelConventionRazorPages());
    })
    .AddXLocalizer<ViewLocalizationResource>(ops =>
    {
      ops.ResourcesPath = "LocalizationResources";
      ops.AutoTranslate = false;
      ops.AutoAddKeys = false;
      ops.UseExpressMemoryCache = !Development;
    })
    .AddXLocalizer<ViewLocalizationResource, MyMemoryTranslateService>(ops =>
    {
      ops.ResourcesPath = "LocalizationResources";
      ops.AutoTranslate = true;
      ops.AutoAddKeys = true;
      ops.UseExpressMemoryCache = !Development;
    });

  services.AddSingleton<IModelBindingErrorMessagesProvider, SpanishModelBindingErrors>();`

In this order, check first if the resource exists in the "* .resx" files. However, you cannot add new keys as you are using "ResxResourceProvider".

In other words, only one resource provider is used, the last one to register. So "XmlResourceProvider" doesn't work, so no new keys can be added.

LazZiya commented 3 years ago

Hi @FJGR65 and thanks for your kind comment :)

Unfortunately, XLocalizer can't use multiple source types at once. Merging multiple source types as xml and resx in one check will increase the complexity of the localization process. And using more than one implementation for XLocalizer is not supported, otherwise it will cause unexpected localization behaviour.

The resource looking order is like below: 1- ExpressMemoryCache 2- Registered source type (only one type; XML or RESX or DB) 3- Translation service

After the resource is translated, it will be added to the registered source type (XML, DB, etc.) and to ExpressMemoryCache for later use.

Back to your case, I suggest that during development you can use XML as a source with below settings:

services.AddSingleton<IModelBindingErrorMessagesProvider, SpanishModelBindingErrors>();
services.AddSingleton<IXResourceProvider, XmlResourceProvider>();
services.AddHttpClient<ITranslator, MyMemoryTranslateService>();

services
        .AddRazorPages(options =>
        {
            // ...
        })
        .AddXLocalizer<ViewLocalizationResource, MyMemoryTranslateService>(ops =>
        {
          ops.ResourcesPath = "LocalizationResources";
          ops.AutoTranslate = true;
          ops.AutoAddKeys = true;
          ops.UseExpressMemoryCache = !Development;
        });

Then when it comes to production; if you don't want to use XML as a source, you can use the exporter to export all XML resources to RESX files, and use the below settings:

services.AddSingleton<IModelBindingErrorMessagesProvider, SpanishModelBindingErrors>();

services
        .AddRazorPages(options =>
        {
            // ...
        })
        .AddXLocalizer<ViewLocalizationResource>(ops =>
        {
          ops.ResourcesPath = "LocalizationResources";
          ops.AutoTranslate = false;
          ops.AutoAddKeys = false;
          ops.UseExpressMemoryCache = !Development;
        });

Notices

Please let me know if you need any further assistance,

Kind regards, Ziya

FJGR65 commented 3 years ago

Thank you very much for your help. In the end I have understood how it works.

I will try only with XML. In development if you add new keys. I will test in production.

Thank you very much. I really appreciate your work and it is very helpful.

LazZiya commented 3 years ago

You are welcome @FJGR65 :)

I will close the issue for now, and please feel free to reopen in case you need more help.

Best, Ziya