codingseb / Localization

A suite to localize C# and WPF projects easily based on file format you choose.
MIT License
37 stars 6 forks source link

Multiplayer using problem #6

Closed DamianMorozov closed 1 year ago

DamianMorozov commented 1 year ago

Thank you for this useful NuGet package. I have several applications, but some of them use multi-user mode, such as BlazorApp. I noticed that your examples use Singleton in Loc.Instance. Here is my base class

/// <summary>
/// Base class for localization.
/// </summary>
public class WsLocaleBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;
    public Loc Locale { get; set; } = Loc.Instance;
    private WsEnumLanguage _lang;
    public WsEnumLanguage Lang { get => _lang; private set { _lang = value; SetLanguage(_lang); } }

    protected WsLocaleBase()
    {
        Lang = WsEnumLanguage.Russian;
    }

    public virtual void SetLanguage(WsEnumLanguage language)
    {
        switch (Lang = language)
        {
            case WsEnumLanguage.Russian:
                Locale.CurrentLanguage = "ru";
                break;
            case WsEnumLanguage.English:
            default:
                Locale.CurrentLanguage = "en";
                break;
        }
    }

    #endregion
}

Here is my test in which you can see the problem

[TestFixture]
public sealed class WsLocalizationTests
{
    [Test]
    public void Create_2_instances_for_multiplayer()
    {
        Assert.DoesNotThrow(() =>
        {
            WsLocalizationLabelPrint wsLocalization1 = new();
            WsLocalizationLabelPrint wsLocalization2 = new();
            TestContext.WriteLine($"wsLocalization1.Locale.CurrentLanguage: {wsLocalization1.Locale.CurrentLanguage}");
            TestContext.WriteLine($"wsLocalization2.Locale.CurrentLanguage: {wsLocalization2.Locale.CurrentLanguage}");

            wsLocalization1.SetLanguage(WsEnumLanguage.English);
            TestContext.WriteLine($"wsLocalization1.Locale.CurrentLanguage: {wsLocalization1.Locale.CurrentLanguage}");
            TestContext.WriteLine($"wsLocalization2.Locale.CurrentLanguage: {wsLocalization2.Locale.CurrentLanguage}");

            Assert.That(wsLocalization1.Lang, Is.EqualTo(WsEnumLanguage.English));
            Assert.That(wsLocalization1.Locale.CurrentLanguage, Is.EqualTo("en"));
            Assert.That(wsLocalization2.Lang, Is.EqualTo(WsEnumLanguage.Russian));
            Assert.That(wsLocalization2.Locale.CurrentLanguage, Is.EqualTo("ru"));
        });
    }
}

Here is the Visual Studio Test output

wsLocalization1.Locale.CurrentLanguage: ru
wsLocalization2.Locale.CurrentLanguage: ru
wsLocalization1.Locale.CurrentLanguage: en
wsLocalization2.Locale.CurrentLanguage: en  // <--- here is the mistake, must be 'ru'

How can I use multiplayer localization mode?

codingseb commented 1 year ago

Hye @DamianMorozov,

Yes Loc has an Instance property for easy access but constructors of Loc are public. So multiple instances are normally supported :

public Loc Locale { get; set; } = new Loc();
codingseb commented 1 year ago

The The Dictionnary that contains translations is static so it need to be loaded in memory only once. But the property CurrentLanguage is per instance so it should work for multi users if you use :

wsLocalization.Local.Translate(...)
// In place of 
Loc.Tr(...)
DamianMorozov commented 1 year ago

Hye @DamianMorozov,

Yes Loc has an Instance property for easy access but constructors of Loc are public. So multiple instances are normally supported :

public Loc Locale { get; set; } = new Loc();

Unfortunately, it doesn't work that way. The translation stops working.

Test

[Test]
public void Using_multiplayer()
{
    Assert.DoesNotThrow(() =>
    {
        WsLocalizationLabelPrint wsLocalization1 = new();
        WsLocalizationLabelPrint wsLocalization2 = new();
        TestContext.WriteLine($"wsLocalization1.Locale.CurrentLanguage: {wsLocalization1.Locale.CurrentLanguage}");
        TestContext.WriteLine($"wsLocalization2.Locale.CurrentLanguage: {wsLocalization2.Locale.CurrentLanguage}");

        wsLocalization1.Lang = WsEnumLanguage.English;
        TestContext.WriteLine($"wsLocalization1.Locale.CurrentLanguage: {wsLocalization1.Locale.CurrentLanguage}");
        TestContext.WriteLine($"wsLocalization2.Locale.CurrentLanguage: {wsLocalization2.Locale.CurrentLanguage}");
        TestContext.WriteLine($"{nameof(wsLocalization1.AppLoad)}: {wsLocalization1.AppLoad}");
        TestContext.WriteLine($"{nameof(wsLocalization2.AppLoad)}: {wsLocalization2.AppLoad}");

        Assert.That(wsLocalization1.Lang, Is.EqualTo(WsEnumLanguage.English));
        Assert.That(wsLocalization1.Locale.CurrentLanguage, Is.EqualTo("en"));
        Assert.That(wsLocalization2.Lang, Is.EqualTo(WsEnumLanguage.Russian));
        Assert.That(wsLocalization2.Locale.CurrentLanguage, Is.EqualTo("ru"));

        Assert.That(wsLocalization1.AppLoad, Is.EqualTo("Loading"));
        Assert.That(wsLocalization2.AppLoad, Is.EqualTo("Загрузка"));
    });
}

Test output

wsLocalization1.Locale.CurrentLanguage: ru
wsLocalization2.Locale.CurrentLanguage: ru
wsLocalization1.Locale.CurrentLanguage: en  // here is ok
wsLocalization2.Locale.CurrentLanguage: ru  // here is ok
AppLoad: AppLabelPrint.AppLoad  // here is error
AppLoad: AppLabelPrint.AppLoad  // here is error

The running main app have errors in Localization.

codingseb commented 1 year ago

I just tested it. And yes it does not work like it should.

I will correct the library and publish a new version as soon as possible. I also need to make some changes in the WPF part link to that.

codingseb commented 1 year ago

I published version 1.3.0 That support multi-users(instances) of Loc. But Warning it comes with some breaking changes to make it work. Here is the release note :

I hope it helps to resolve your issue.

Just think to use yourLocInstance.Translate("textId") in place of Loc.Tr("textId"). Or define how to resolve Loc.Instance (use in Loc.Tr("textId")):

Loc.GetInstance = () => currentUser.Loc;
DamianMorozov commented 1 year ago

Thank you so much!

The test.

[Test]
public void Using_multiplayer()
{
    Assert.DoesNotThrow(() =>
    {
        WsLocalizationLabelPrint wsLocalization1 = new();
        WsLocalizationLabelPrint wsLocalization2 = new();
        TestContext.WriteLine($"CurrentLanguage 1: {wsLocalization1.Locale.CurrentLanguage}");
        TestContext.WriteLine($"CurrentLanguage 2: {wsLocalization2.Locale.CurrentLanguage}");

        wsLocalization1.Lang = WsEnumLanguage.English;
        TestContext.WriteLine($"CurrentLanguage 1: {wsLocalization1.Locale.CurrentLanguage}");
        TestContext.WriteLine($"CurrentLanguage 2: {wsLocalization2.Locale.CurrentLanguage}");
        TestContext.WriteLine($"{nameof(wsLocalization1.AppLoad)}: {wsLocalization1.AppLoad}");
        TestContext.WriteLine($"{nameof(wsLocalization2.AppLoad)}: {wsLocalization2.AppLoad}");

        Assert.That(wsLocalization1.Lang, Is.EqualTo(WsEnumLanguage.English));
        Assert.That(wsLocalization1.Locale.CurrentLanguage, Is.EqualTo("en"));
        Assert.That(wsLocalization2.Lang, Is.EqualTo(WsEnumLanguage.Russian));
        Assert.That(wsLocalization2.Locale.CurrentLanguage, Is.EqualTo("ru"));

        Assert.That(wsLocalization1.AppLoad, Is.EqualTo("Loading"));
        Assert.That(wsLocalization2.AppLoad, Is.EqualTo("Загрузка"));
    });
}

The result

CurrentLanguage 1: ru
CurrentLanguage 2: ru
CurrentLanguage 1: en
CurrentLanguage 2: ru
AppLoad: Loading
AppLoad: Загрузка