Skip to content

Commit 4eb1693

Browse files
authored
Fix culture creation with undetermined lang tag (#115166)
1 parent 100ba61 commit 4eb1693

File tree

2 files changed

+25
-2
lines changed

2 files changed

+25
-2
lines changed

src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ internal sealed partial class CultureData
1919
/// </summary>
2020
/// <param name="name">The locale name that ICU returns.</param>
2121
/// <param name="extension">The extension part in the original culture name.</param>
22+
/// <param name="originalName">The original input culture name.</param>
2223
/// <param name="collationStart">The index of the collation in the name.</param>
2324
/// <remarks>
2425
/// BCP 47 specifications allow for extensions in the locale name, following the format language-script-region-extensions-collation. However,
@@ -29,7 +30,7 @@ internal sealed partial class CultureData
2930
/// between names with extensions and those without. For example, we may have a name like en-US and en-US-u-xx. Although .NET doesn't support the extension xx,
3031
/// we still include it in the name to distinguish it from the name without the extension.
3132
/// </remarks>
32-
private static string NormalizeCultureName(string name, ReadOnlySpan<char> extension, out int collationStart)
33+
private static string NormalizeCultureName(string name, ReadOnlySpan<char> extension, string originalName, out int collationStart)
3334
{
3435
Debug.Assert(name is not null);
3536
Debug.Assert(name.Length <= ICU_ULOC_FULLNAME_CAPACITY);
@@ -39,6 +40,16 @@ private static string NormalizeCultureName(string name, ReadOnlySpan<char> exten
3940
Span<char> buffer = stackalloc char[ICU_ULOC_FULLNAME_CAPACITY];
4041
int bufferIndex = 0;
4142

43+
// Using `Undetermined` language (e.g. `und-US`), the returned name from ICU getName is `_US`. We return back the original language `und`
44+
if (name.Length > 1 && (name[0] == '-' || name[0] == '_') && originalName.StartsWith("und", StringComparison.OrdinalIgnoreCase))
45+
{
46+
buffer[bufferIndex++] = 'u';
47+
buffer[bufferIndex++] = 'n';
48+
buffer[bufferIndex++] = 'd';
49+
50+
changed = true;
51+
}
52+
4253
for (int i = 0; i < name.Length && bufferIndex < ICU_ULOC_FULLNAME_CAPACITY; i++)
4354
{
4455
char c = name[i];
@@ -141,7 +152,7 @@ private bool InitIcuCultureDataCore()
141152

142153
Debug.Assert(_sWindowsName != null);
143154

144-
_sRealName = NormalizeCultureName(_sWindowsName, indexOfExtensions > 0 ? _sRealName.AsSpan(indexOfExtensions) : ReadOnlySpan<char>.Empty, out int collationStart);
155+
_sRealName = NormalizeCultureName(_sWindowsName, indexOfExtensions > 0 ? _sRealName.AsSpan(indexOfExtensions) : ReadOnlySpan<char>.Empty, _sRealName, out int collationStart);
145156

146157
_iLanguage = LCID;
147158
if (_iLanguage == 0)

src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoCtor.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,18 @@ public static IEnumerable<object[]> Ctor_String_TestData()
388388
}
389389
}
390390

391+
[ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalization))]
392+
[InlineData("und-us", "und-US", "und-US")]
393+
[InlineData("und-us_tradnl", "und-US", "und-US_tradnl")]
394+
[InlineData("und-es-u-co-phoneb", "und-ES", "und-ES_phoneb")]
395+
[InlineData("und-es-t-something", "und-ES", "und-ES")]
396+
public void CtorUndeterminedLanguageTag(string cultureName, string expectedCultureName, string expectedSortName)
397+
{
398+
CultureInfo culture = new CultureInfo(cultureName);
399+
Assert.Equal(expectedCultureName, culture.Name);
400+
Assert.Equal(expectedSortName, culture.CompareInfo.Name);
401+
}
402+
391403
[Theory]
392404
[MemberData(nameof(Ctor_String_TestData))]
393405
public void Ctor_String(string name, string[] expectedNames)

0 commit comments

Comments
 (0)