LazZiya / XLocalizer

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

ISharedCultureLocalizer moved/missing? #11

Closed morgrowe closed 3 years ago

morgrowe commented 3 years ago

Hi Ziya,

I'm really hoping this isn't a case of adding a using statement for a Microsoft namespace again!

I'm migrating some of my services from using LazZiya.ExpressLocalization to XLocalizer. One of the tools I used heavily was the ISharedCultureLocalizer (ISharedCultureLocalizer.GetLocalizedString(string) and ISharedCultureLocalizer.GetLocalizedString(Type, string) specificaly). I can't seem to find it in XLocalizer nor can I figure out how to call those methods anymore.

Has it been moved or renamed? I used to use it like this:

` using LazZiya.ExpressLocalization;

public class LocalizationService : ILocalizationService { private readonly ISharedCultureLocalizer _loc;

    public LocalizationService(ISharedCultureLocalizer loc)
    {
        _loc = loc;
    }

    public TEntity Localize<TEntity>(string key)
    {
            ...
            return _loc.GetLocalizedString(key);
    }

} `

As always, thank you very much.

Morgan

LazZiya commented 3 years ago

Hi Morgan,

ISharedCultureLocalizer was in LazZiya.ExpressLocalization but it is not available in XLocalizer, instead you can use the standard interfaces of IStringLocalizer or IHtmlLocalizer.

To make it clear, see below comparisons:

// LazZiya.ExpressLocalization
public class Foo 
{
    private readonly ISharedCultureLocalizer _loc;

    public Foo(ISharedCultureLocalizer localizer)
    {
        _loc = localizer;
    }

    public void OnGet()
    {
        // This will get the localized value from the default shared resource
        var str1 = _loc.GetLocalizedString("Hello");

        // This will get the localized value from the specified resource Type
        var str2 = _loc.GetLocalizedString(Type, "Hello");
    }
}

// XLocalizer
public class Foo 
{
    private readonly IStringLocalizer _loc1;
    private readonly IStringLocalizer _loc2;

    public Foo(IStringLocalizer localizer1, IStringLocalizer<Type> localizer2)
    {
        _loc1 = localizer1;
        _loc2 = localizer2;
    }

    public void OnGet()
    {
        // This will get the localized value from the default shared resource
        var str = _loc1["Hello"];

        // This will get the localized value from the specified resource Type
        var str2 = _loc2["Hello"];
    }
}

It is worth to mention that when you use XLocalizer with XML resource files, it will create the relevant resource file for the given Type automatically. But if you will use DB for storing the localized values, the type passed into IStringLocalizer<T> must be a type of an existing db set.

The reasone behind this change is to make use of the standard localization interfaces, and this makes it easier to switch from the default localization system provided by Asp.Net Core to XLocalizer and vice versa.

Actually that was one of the main reasons to develop XLocalizer as a new project and not as a next version of LazZiya.ExpressLocalization.

Let me know if you still need any more details, Ziya

morgrowe commented 3 years ago

Right ok, I am using the DB storage for localized strings. I tried your suggestion, but it appears to always use the first TResource passed into IStringLocalizer, even if I explicitly call the other localizer. Given the following code, whenever I call _domainLoc or _userLoc, it only ever uses the UserLocalizationResource. If I step through the code, I can see it hits the correct _domainLoc [1] or _userLoc [2] lines, but seems to only use the resource that gets 'invoked' first.

    public enum LocalizationResource
    {
        Domain,
        User
    }

    public class LocalizationService : ILocalizationService
    {
        private readonly IStringLocalizer<DomainLocalizationResource> _domainLoc; 
        private readonly IStringLocalizer<UserLocalizationResource> _userLoc;

        public LocalizationService(IStringLocalizer<DomainLocalizationResource> domainLoc
            , IStringLocalizer<UserLocalizationResource> userLoc)
        {
            _domainLoc = domainLoc;
            _userLoc = userLoc;
        }

        public string GetString(LocalizationResource localizationResource, string key)
        {
            var value = string.Empty;

            if (localizationResource == LocalizationResource.Domain)
            {
                value = _domainLoc.GetString(key).Value; // [1] Should return DOMAINWelcome
            }
            else if (localizationResource == LocalizationResource.User)
            {
                value = _userLoc.GetString(key).Value; // [2] Should return USERWelcome
            }

            return value;
        }
    }

    public class DashboardHomeService : IDashboardHomeService
    {
        private readonly ILocalizationService _localization;

        public DashboardHomeService(ILocalizationService localization)
        {
            _localization = localization;
        }

        public string TestUserLocalization(string key)
        {
            var test = _localization.GetString(LocalizationResource.User, key);

            return test;
        }

        public string TestDomainLocalization(string key)
        {
            var test = _localization.GetString(LocalizationResource.Domain, key);

            return test;
        }
    }

    // Index.cshtml.cs
    public class IndexModel : PageModel
    {
        private readonly IDashboardHomeService _service;

        public IndexModel(IDashboardHomeService service)
        {
            _service = service;
        }

        public void OnGet()
        {
            ViewData["OUTPUT2"] = _service.TestUserLocalization("WELCOME"); // Always returns USERWelcome
            ViewData["OUTPUT"] = _service.TestDomainLocalization("WELCOME"); // Always returns USERWelcome
        }
    }

Both DomainLocalizationResource and UserLocalizationResource implement IXDbResource.

If I swap round the calls to the Localization service, it then always returns DOMAINWelcome instead of USERWelcome:

public void OnGet()
{
        ViewData["OUTPUT"] = _service.TestDomainLocalization("WELCOME"); // Always returns DOMAINWelcome
        ViewData["OUTPUT2"] = _service.TestUserLocalization("WELCOME"); // Always returns DOMAINWelcome
}

Any idea what's going on there? Maybe I've messed up my dependency injection somewhere. It didn't used to happen when using the ISharedCultureLocalizer, so I wonder if you implemented some sort of work around in the other project? Maybe it's caching the TResource type?

Thanks Morgan

LazZiya commented 3 years ago

Just answering shortly from mobile

Can you turn off ExpressMemoryCache in XLocalizer options in startıp and try:

ops.UseExpressMemoryCache = false;

It seems I’ve skipped caching the resource type 🙄

morgrowe commented 3 years ago

That worked, thank you very much, :)

I now see DOMAINWelcome and USERWelcome in my View!

Morgan

LazZiya commented 3 years ago

Glad to help :)

I will mark this issue as improvement to the caching system, so the resource type also will be cached along with the relevant strings.

LazZiya commented 3 years ago

Closing this issue, the enhancment will be followed in #13