bitfoundation / bitplatform

Build all of your apps using what you already know and love ❤️
https://bitplatform.dev
MIT License
1.04k stars 218 forks source link

`BitDatePicker` issue in blazor server in OS that have wrong culture info with `GregorianCalendar` as default calendar for `fa-IR` #2188

Closed Kamalifar closed 2 years ago

Kamalifar commented 2 years ago

Describe the bug I set the culture info in startup.configure as follows:

var cultureInfo = new CultureInfo("fa-IR");
CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
CultureInfo.CurrentCulture = cultureInfo;
CultureInfo.CurrentUICulture= cultureInfo;

However, the date picker shell will also be AD as follows, Only the names of the Gregorian months are shown in Persian.:

Untitled

The problem still persists even if the culture is set as follows in date picker:

<BitDatePicker @bind-Value="LetterModel.AtfPyroDate" Style="width: 300px" AllowTextInput="true" FormatDate="yyyy/MM/dd" GoToToday="امروز" Culture="@(new System.Globalization.CultureInfo("fa-IR"))"></BitDatePicker>

Now, if the Windows calendar is set to Hijri, this problem will be solved as follows:

Untitled

I also tried to customize the names of the months as shown below, but this time I did not succeed.

var cultureInfo = CultureInfo.CreateSpecificCulture("fa-IR");
cultureInfo.DateTimeFormat.MonthNames = new[] { "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند", "" };

The problem is that in Windows 7 or other operating systems, it may not be possible to set the calendar to Hijri. How should we set the culture info so that the date picker shell is displayed in Persian in any case?

Expected behavior It is better not to follow the system culture by setting the date picker culture anymore.

Bit Blazor client

Bit nuget package's version. 11.0.0 Blazor Server or Blazor Web Assembly or both? Blazor Server Which browser? Chrome Version 102.0.5005.63 (Official Build) (64-bit) Which blazor version? blazor 6

ysmoradi commented 2 years ago

I appreciate your detailed issue @Kamalifar By Windows 7, is aspnetcore server that runs blazor server is running in Windows 7? Or only the browser is running in Windows 7 and server is somewhere else?

Kamalifar commented 2 years ago

Thank you for your prompt reply @ysmoradi. Only the browser is running in Windows 7 and the server is somewhere else. However, For different modes. Suppose the server operating system is a CentOS 5 and users are using Windows 7 or even Ubuntu.

ysmoradi commented 2 years ago

Ok, could you please provide a small repo that reproduces this?

I also tried to customize the names of the months as shown below, but this time I did not succeed. var cultureInfo = CultureInfo.CreateSpecificCulture("fa-IR"); cultureInfo.DateTimeFormat.MonthNames = new[] { "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند", "" };

Note that we're using this line of code in BitDatePicker

private CultureInfo culture = CultureInfo.CurrentUICulture;
Kamalifar commented 2 years ago

Unfortunately, I did not understand what I have to provide?

ysmoradi commented 2 years ago

A sample source code that has this issue

Kamalifar commented 2 years ago

I used this code in the following ways.

1) <BitDatePicker @bind-Value="LetterModel.AtfPyroDate" Style="width: 300px" AllowTextInput="true" FormatDate="yyyy/MM/dd" GoToToday="امروز" Culture="@cultureInfo"></BitDatePicker>

@code { protected override void OnAfterRender(bool firstRender) { base.OnAfterRender(firstRender); if (firstRender) { cultureInfo.DateTimeFormat.MonthNames = new[] {"فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند", ""}; } } }

2) <BitDatePicker @bind-Value="LetterModel.AtfPyroDate" Style="width: 300px" AllowTextInput="true" FormatDate="yyyy/MM/dd" GoToToday="امروز"></BitDatePicker>

In startup: public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { var cultureInfo = CultureInfo.CreateSpecificCulture("fa-IR"); cultureInfo.DateTimeFormat.MonthNames = new[] { "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند", "" }; CultureInfo.DefaultThreadCurrentCulture = cultureInfo; CultureInfo.DefaultThreadCurrentUICulture = cultureInfo; CultureInfo.CurrentCulture = cultureInfo; CultureInfo.CurrentUICulture = cultureInfo;

3) <BitDatePicker @bind-Value="LetterModel.AtfPyroDate" Style="width: 300px" AllowTextInput="true" FormatDate="yyyy/MM/dd" GoToToday="امروز" Culture="@cultureInfo"></BitDatePicker>

@code { public CultureInfo cultureInfo = CultureInfo.CreateSpecificCulture("fa-IR"); protected override void OnInitialized() { base.OnInitialized(); cultureInfo.DateTimeFormat.MonthNames = new[] {"فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند", ""}; } }

In the first mode, nothing happens. But in the second and third modes, only the month changes, but the year and day remain the same(AD).

Untitled

msynk commented 2 years ago

@Kamalifar could you please create a new GitHub repository, push your code into it, and post the address of that repo?

msynk commented 2 years ago

BTW, I think it's an issue of the OS itself since in windows 10 and 11 we are not seeing such an experience

Kamalifar commented 2 years ago

I think it's an issue of the OS itself since in windows 10 and 11 we are not seeing such an experience

Exactly, I am testing this on Windows 10. This problem occurs if the Gregorian calendar is selected, but if the Hijri calendar is selected, the problem will be solved.

Untitled

Untitledshamsi

Kamalifar commented 2 years ago

you please create a new GitHub repository, push your code into it, and post the address of that repo?

https://github.com/Kamalifar/TestBitComponentDatePicker/

msynk commented 2 years ago

@Kamalifar thanks a lot for providing the repo. I've cloned your test repo and ran it on both my laptop and PC. the component works fine on both of them without changing anything on the OS Region settings. I think we need to investigate it a little bit more. one of my colleagues will contact you soon 😊

maryamhdr commented 2 years ago

@Kamalifar Thanks a lot for contributing and submitting such detailed and clear issues. If you want to use the BitPlatform products widely in your projects, we can give you priority support for free, so in case you have an issue, you can contact the BitPlatform development team at Microsoft Teams quickly and get your answer ASAP. If you would like to have this support plan, let us know and send us your contact info (email address and full name)

Kamalifar commented 2 years ago

Dear @maryamhdr,

Thanks for your support and explanation. I'd like to use the BitDatePicker widely in my projects, So my email address is kamalifar@chmail.ir.

Best Regards, Ali Kamalifar

ysmoradi commented 2 years ago

You've been invited to the Microsoft teams. Give it a try! @Kamalifar

Kamalifar commented 2 years ago

I really appreciate your efforts, my friends, to look into this issue in your team. Unfortunately, I can no longer enter the account that I was with you as a guest. Because of Microsoft Authenticator. Therefore, with the permission of my friends, I will continue the discussion here. I was able to check this issue on several other systems (IIS Express Windows10 with Gregorian calendar), but the problem persists. I made some changes in the codes and push them in the repository.

`protected override void OnInitialized() { base.OnInitialized(); var culture = new CultureInfo("fa-IR");// Set user culture here culture.DateTimeFormat.MonthNames = new[] { "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند", "" }; culture.DateTimeFormat.AbbreviatedMonthNames = new string[] { "فروردین", "ارديبهشت", "خرداد", "تير", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند", "" }; culture.DateTimeFormat.AbbreviatedDayNames = new string[] { "ی", "د", "س", "چ", "پ", "ج", "ش" }; culture.DateTimeFormat.DayNames = new string[] { "یکشنبه", "دوشنبه", "ﺳﻪشنبه", "چهارشنبه", "پنجشنبه", "جمعه", "شنبه" }; culture.DateTimeFormat.AMDesignator = "ق.ظ"; culture.DateTimeFormat.PMDesignator = "ب.ظ"; culture.DateTimeFormat.ShortDatePattern = "yyyy/MM/dd"; //culture.DateTimeFormat.Calendar = new PersianCalendar(); culture.DateTimeFormat.FirstDayOfWeek = DayOfWeek.Saturday; PersianCalendar cal = new PersianCalendar();

    culture.DateTimeFormat.Calendar = cal;

    var info = culture.DateTimeFormat;

    var field = typeof(DateTimeFormatInfo).GetField("calendar", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
    field?.SetValue(info, cal);
    var fieldInfo = typeof(DateTimeFormatInfo).GetField("m_cultureTableRecord", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
    if (fieldInfo != null)
    {
        object obj = fieldInfo.GetValue(info);
        var methodInfo = obj.GetType().GetMethod("UseCurrentCalendar", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
        if (
            methodInfo !=
            null)
        {
            var propertyInfo = cal.GetType().GetProperty("ID", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
            if (
                propertyInfo !=
                null)
                methodInfo.Invoke(obj, new object[] { propertyInfo.GetValue(cal, null) });
        }
    }
    var field1 = typeof(CultureInfo).GetField("calendar", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
    field1?.SetValue(culture, cal);
    var fieldInfo1 = typeof(CultureInfo).GetField("calendar", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
    fieldInfo1?.SetValue(culture, cal);

    //CultureInfo.CurrentCulture = culture;
    //CultureInfo.CurrentUICulture = culture;
    CultureInfo.DefaultThreadCurrentCulture = culture;
    CultureInfo.DefaultThreadCurrentUICulture = culture;
    CultureInfo.CurrentCulture.DateTimeFormat = culture.DateTimeFormat;
    CultureInfo.CurrentUICulture.DateTimeFormat = culture.DateTimeFormat;
}`

As you can see, almost everything is manually given to the culture, but whatever I do, the calendar itself keeps its Gregorian state again. That is, even when you choose the sixth, you are actually choosing today's Gregorian date, but as you can see, the date is finally shown as today's shamsi equivalent. Untitled If it was possible to somehow attribute the Persian calendar to this date picker, I think the problem would be solved.

Even I tried to give this calendar assignment with reflection, but it returns null in below line. var fieldInfo = typeof(DateTimeFormatInfo).GetField("m_cultureTableRecord", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); Of course, the reason for this is that this reflection can be used for pre-Blazor, and there is not really anything like that here. Anyway, I put the code so that if you have an idea, you can apply it for reflection.

ysmoradi commented 2 years ago

I was able to achieve followings

image

Source code:

https://github.com/bitfoundation/bit-components-playground/tree/issue-2188

There is an issue with CultureInfo.TextInfo.IsRightToLeft property which needs more investigation

Kamalifar commented 2 years ago

Thank you so much for all you have done. I used your code and the result is the same as before, as you can see in the picture. Untitled

Source code: https://github.com/Kamalifar/TestBitComponentDatePicker/blob/master/Pages/TestCultureInfoHelper.razor

Although today's date is selected, which means the 9th day of July, it displays the calendar of Mehr 2022, which means that only the month array mapping is done and the main date picker of the Gregorian calendar remains. July is the seventh month of the year and Mehr is the seventh month of the Shamsi year. Please, first change the server calendar to Gregorian and then run the project. If the server calendar is changed while running, then the problem will not be seen.

Regards.

ysmoradi commented 2 years ago

Please use latest version of "Bit.BlazorUI" package, not "Bit.Client.Web.BlazorUI". And could you please let me know if https://github.com/bitfoundation/bit-components-playground/tree/master/src/client/Pages/Issues/2188 Works for you by cloning and running that repo Thanks in advance

Kamalifar commented 2 years ago

Thank you and congratulations. https://github.com/bitfoundation/bit-components-playground/tree/master/src/client/Pages/Issues/2188 works for me very well, but it only works for WebAssembly, and unfortunately it doesn't work for BlazorServer. It seems that the reflection has been useful, but probably some changes should be made for BlazorServer.

I updated https://github.com/Kamalifar/TestBitComponentDatePicker based on "Bit.BlazorUI" package, however, the result is the same as before, but, If I create a new WebAssembly project and copy your code into it, it works correctly.

ysmoradi commented 2 years ago

I ran your sample and it works like a chart. It seems that there is something OS-dependent is causing the issue Could you please run your sample in Clean Windows 10 in Virtual Machine or elsewhere? We can test Windows Server / Linux / Windows 7 after that. Plus provide me cultureData values in your current setup as followings: image This is your sample repo working totally fine on Windows 10 It seems that you've made it better by using fonts (Looks better than my sample) image

Kamalifar commented 2 years ago

It's my cultureData value in my current setup: Untitled This is exactly like your value.

This is very strange. Why the Blazor WebAssembly is working properly, but Blazor Server is not on my OS? However, I try to check this sample on another OS and I will announce the result here.

Thanks in advance, Best regards.

ysmoradi commented 2 years ago

It has some differences. Your default calendar is GreorianCalendar, mine is PersianCalendar That's the issue

Kamalifar commented 2 years ago

Yes, exactly, but why is it still affected by the operating system calendar? only in Blazor Server.

The below picture is my cultureData value in WebAssembly test project. Untitled

ysmoradi commented 2 years ago

Try this approach and let us know if this works or not https://stackoverflow.com/a/15826881/2720104

Kamalifar commented 2 years ago

I tried to do this, but it can't do the cast on the following line.

newOptionalCalendarIDs[i] = (Int32) idProperty.GetValue(optionalCalendars[i], null);

I will leave the address of the code, please do a test.

ysmoradi commented 2 years ago

I'm afraid we can't truly test this because this needs an OS which problem exists in there and we don't have such an OS installed. And believe me or not, this is not BitDatePicker's issue, but we'll merge any PR you make if you wanna put more effort into this

Kamalifar commented 2 years ago

It has some differences. Your default calendar is GreorianCalendar, mine is PersianCalendar That's the issue

@ysmoradi Is it possible to force the default calendar value of the cultureData to be Persian calendar instead of Gregorian Calendar in CultureInfoHelper?

Regards.

ysmoradi commented 2 years ago

We're going to use CultureInfo.DateTimeFormat.Calendar instead of CultureInfo.Calendar This property is writable and you can change that to PersianCalendar easily

msynk commented 2 years ago

The issue is accepted and planned. Resolving it will start ASAP.

msynk commented 2 years ago

Resolving this issue has started. It will be announced when this issue is resolved.

msynk commented 2 years ago

This issue has been resolved and it will be available in the next pre-release version. It will be announced when the pre-release becomes ready.

Kamalifar commented 2 years ago

We're going to use CultureInfo.DateTimeFormat.Calendar instead of CultureInfo.Calendar This property is writable and you can change that to PersianCalendar easily

You're right, however, I do this too but still the calendar stays in Gregorian mode. Untitled

ysmoradi commented 2 years ago

In the new pre-release we're using CultureInfo.DateTimeInfo.Calendar which is writable No need to change the default calendar anymore.

msynk commented 2 years ago

The pre-release has been published. Please give it a try and give feedback.

Kamalifar commented 2 years ago

Dear @msynk @ysmoradi @maryamhdr, Congratulations! The problem has been resolved definitely. Thank you so much,

Best regards.