dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.36k stars 4.75k forks source link

Different behavior of CultureInfo.GetCultureInfo(int) with ICU between Windows and Linux #71124

Closed vonzshik closed 2 years ago

vonzshik commented 2 years ago

Description

While getting an unsupported locale on Linux will throw an exception:

System.Globalization.CultureNotFoundException: Culture is not supported. (Parameter 'culture')
19465 (0x4c09) is an invalid culture identifier.
   at System.Globalization.CultureInfo.GetCultureInfo(Int32 culture)

On Windows it might return a culture, but with a different LCID:

Got culture 'en-AE', LCID 4096 for LCID 19465

Reproduction Steps


using System.Globalization;

const int cultureID = 19465;

try
{
    var culture = CultureInfo.GetCultureInfo(cultureID);
    Console.WriteLine($"Got culture '{culture.Name}', LCID {culture.LCID} for LCID {cultureID}");
}
catch (Exception ex)
{
    Console.WriteLine("Unable to get culture due to an exception:");
    Console.WriteLine(ex);
}

Expected behavior

While in ICU mode, CultureInfo.GetCultureInfo(int) should always throw an exception for unsupported cultures.

Actual behavior

CultureInfo.GetCultureInfo(int) doesn't always throw an exception for unsupported cultures on Windows.

Regression?

No response

Known Workarounds

No response

Configuration

.NET 6 Windows 10 21H2 19044.1766 (x64) Ubuntu 22.04 (x64)

Other information

The main problem here is the fact that on windows CultureData.LCIDToLocaleName completely ignores whether it's in ICU mode or NLS, and instead always calls kernel32.

https://github.com/dotnet/runtime/blob/9b4f152802b6305438bc1f6ddf4733104bb57f6e/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs#L185-L205

I think it should work the same way as CultureData.InitCultureDataCore does, which checks whether it's running in ICU mode.

https://github.com/dotnet/runtime/blob/9b4f152802b6305438bc1f6ddf4733104bb57f6e/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs#L43-L50

ghost commented 2 years ago

Tagging subscribers to this area: @dotnet/area-system-globalization See info in area-owners.md if you want to be subscribed.

Issue Details
### Description While getting an unsupported locale on Linux will throw an exception: ``` System.Globalization.CultureNotFoundException: Culture is not supported. (Parameter 'culture') 19465 (0x4c09) is an invalid culture identifier. at System.Globalization.CultureInfo.GetCultureInfo(Int32 culture) ``` On Windows it might return a culture, but with a different LCID: ``` Got culture 'en-AE', LCID 4096 for LCID 19465 ``` ### Reproduction Steps ```csharp using System.Globalization; const int cultureID = 19465; try { var culture = CultureInfo.GetCultureInfo(cultureID); Console.WriteLine($"Got culture '{culture.Name}', LCID {culture.LCID} for LCID {cultureID}"); } catch (Exception ex) { Console.WriteLine("Unable to get culture due to an exception:"); Console.WriteLine(ex); } ``` ### Expected behavior While in ICU mode, `CultureInfo.GetCultureInfo(int)` should always throw an exception for unsupported cultures. ### Actual behavior `CultureInfo.GetCultureInfo(int)` doesn't always throw an exception for unsupported cultures on Windows. ### Regression? _No response_ ### Known Workarounds _No response_ ### Configuration .NET 6 Windows 10 21H2 19044.1766 (x64) Ubuntu 22.04 (x64) ### Other information The main problem here is the fact that on windows `CultureData.LCIDToLocaleName` completely ignores whether it's in ICU mode or NLS, and instead always calls kernel32. https://github.com/dotnet/runtime/blob/9b4f152802b6305438bc1f6ddf4733104bb57f6e/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs#L185-L205 I think it should work the same way as `CultureData.InitCultureDataCore` does, which checks whether it's running in ICU mode. https://github.com/dotnet/runtime/blob/9b4f152802b6305438bc1f6ddf4733104bb57f6e/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs#L43-L50
Author: vonzshik
Assignees: -
Labels: `area-System.Globalization`, `untriaged`
Milestone: -
tarekgh commented 2 years ago

@vonzshik thanks for your report. LCID concept is obsolete, and we highly encourage all users to move away from using it. This was Windows specific behavior and now many cultures introduced in the system that can share LCID value LOCALE_CUSTOM_UNSPECIFIED 0x1000. We are not investing in fixing any LCID related issues. Also, we are calling Windows to map the LCID's even in ICU mode because we ran before into issues that ICU was not carrying all supported LCID mapping. Anyway, please don't use LCID's and always use culture names instead.

vonzshik commented 2 years ago

@tarekgh shouln't it be marked as obsolete in the api and the docs then?

tarekgh commented 2 years ago

shouln't it be marked as obsolete in the api and the docs then?

Yes, we just didn't get a chance to do that. We can add something from now to the doc to warn about using it. For marking it for obsoletion, we need to be a little careful as I am seeing many legacy codes using it and we want to ensure we are not causing much noise when marking it as obsolete. We are trying to advise users as we go to stop using it. But at some point, we'll mark it as obsolete.