* CultureInfo and CultureData use OS information to initialize culture routines on Windows
* PR Feedback
* Bring back removed assert
* Remove non meaninful asserts
* PR Feedback
AssertExtensions.Throws<ArgumentException>("name", () => new RegionInfo(name));
}
- [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalization))]
- public void CurrentRegion_Icu()
+ [Fact]
+ [PlatformSpecific(TestPlatforms.AnyUnix)]
+ public void CurrentRegion_Unix()
{
using (new ThreadCultureChange("en-US"))
{
}
}
- [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
- public void TestCurrentRegion_Nls()
+ [Fact]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public void CurrentRegion_Windows()
{
RemoteExecutor.Invoke(() =>
{
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\DebugProvider.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Stopwatch.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\RuntimeEventSourceHelper.Windows.cs" Condition="'$(FeaturePerfTracing)' == 'true'" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureData.Windows.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureInfo.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.Win32.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Guid.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\DriveInfoInternal.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.NoRegistry.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.Unix.GetFolderPathCore.cs" Condition="'$(TargetsiOS)' != 'true' and '$(TargetstvOS)' != 'true'" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureData.Unix.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureInfo.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Guid.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\DriveInfoInternal.Unix.cs" />
using System.Collections.Generic;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
namespace System.Globalization
{
// ICU constants
private const int ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY = 100; // max size of keyword or value
private const int ICU_ULOC_FULLNAME_CAPACITY = 157; // max size of locale name
- private const string ICU_COLLATION_KEYWORD = "@collation=";
-
- /// <summary>
- /// This method uses the sRealName field (which is initialized by the constructor before this is called) to
- /// initialize the rest of the state of CultureData based on the underlying OS globalization library.
- /// </summary>
- private unsafe bool IcuInitCultureData()
- {
- Debug.Assert(_sRealName != null);
-
- Debug.Assert(!GlobalizationMode.Invariant);
- Debug.Assert(!GlobalizationMode.UseNls);
-
- string realNameBuffer = _sRealName;
-
- // Basic validation
- if (realNameBuffer.Contains('@'))
- {
- return false; // don't allow ICU variants to come in directly
- }
-
- // Replace _ (alternate sort) with @collation= for ICU
- ReadOnlySpan<char> alternateSortName = default;
- int index = realNameBuffer.IndexOf('_');
- if (index > 0)
- {
- if (index >= (realNameBuffer.Length - 1) // must have characters after _
- || realNameBuffer.IndexOf('_', index + 1) >= 0) // only one _ allowed
- {
- return false; // fail
- }
- alternateSortName = realNameBuffer.AsSpan(index + 1);
- realNameBuffer = string.Concat(realNameBuffer.AsSpan(0, index), ICU_COLLATION_KEYWORD, alternateSortName);
- }
-
- // Get the locale name from ICU
- if (!GetLocaleName(realNameBuffer, out _sWindowsName))
- {
- return false; // fail
- }
-
- // Replace the ICU collation keyword with an _
- Debug.Assert(_sWindowsName != null);
- index = _sWindowsName.IndexOf(ICU_COLLATION_KEYWORD, StringComparison.Ordinal);
- if (index >= 0)
- {
- _sName = string.Concat(_sWindowsName.AsSpan(0, index), "_", alternateSortName);
- }
- else
- {
- _sName = _sWindowsName;
- }
- _sRealName = _sName;
-
- _iLanguage = LCID;
- if (_iLanguage == 0)
- {
- _iLanguage = CultureInfo.LOCALE_CUSTOM_UNSPECIFIED;
- }
-
- _bNeutral = TwoLetterISOCountryName.Length == 0;
-
- _sSpecificCulture = _bNeutral ? IcuLocaleData.GetSpecificCultureName(_sRealName) : _sRealName;
-
- // Remove the sort from sName unless custom culture
- if (index > 0 && !_bNeutral && !IsCustomCultureId(_iLanguage))
- {
- _sName = _sWindowsName.Substring(0, index);
- }
- return true;
- }
internal static unsafe bool GetLocaleName(string localeName, out string? windowsName)
{
return true;
}
- internal static unsafe bool GetDefaultLocaleName(out string? windowsName)
+ internal static unsafe bool GetDefaultLocaleName([NotNullWhen(true)] out string? windowsName)
{
// Get the default (system) locale name from ICU
char* buffer = stackalloc char[ICU_ULOC_FULLNAME_CAPACITY];
{
internal partial class CultureData
{
- /// <summary>
- /// Check with the OS to see if this is a valid culture.
- /// If so we populate a limited number of fields. If its not valid we return false.
- ///
- /// The fields we populate:
- ///
- /// sWindowsName -- The name that windows thinks this culture is, ie:
- /// en-US if you pass in en-US
- /// de-DE_phoneb if you pass in de-DE_phoneb
- /// fj-FJ if you pass in fj (neutral, on a pre-Windows 7 machine)
- /// fj if you pass in fj (neutral, post-Windows 7 machine)
- ///
- /// sRealName -- The name you used to construct the culture, in pretty form
- /// en-US if you pass in EN-us
- /// en if you pass in en
- /// de-DE_phoneb if you pass in de-DE_phoneb
- ///
- /// sSpecificCulture -- The specific culture for this culture
- /// en-US for en-US
- /// en-US for en
- /// de-DE_phoneb for alt sort
- /// fj-FJ for fj (neutral)
- ///
- /// sName -- The IETF name of this culture (ie: no sort info, could be neutral)
- /// en-US if you pass in en-US
- /// en if you pass in en
- /// de-DE if you pass in de-DE_phoneb
- ///
- /// bNeutral -- TRUE if it is a neutral locale
- ///
- /// For a neutral we just populate the neutral name, but we leave the windows name pointing to the
- /// windows locale that's going to provide data for us.
- /// </summary>
- private unsafe bool NlsInitCultureData()
- {
- Debug.Assert(!GlobalizationMode.Invariant);
- Debug.Assert(GlobalizationMode.UseNls);
-
- int result;
- string realNameBuffer = _sRealName;
- char* pBuffer = stackalloc char[Interop.Kernel32.LOCALE_NAME_MAX_LENGTH];
-
- result = GetLocaleInfoEx(realNameBuffer, Interop.Kernel32.LOCALE_SNAME, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LENGTH);
-
- // Did it fail?
- if (result == 0)
- {
- return false;
- }
-
- // It worked, note that the name is the locale name, so use that (even for neutrals)
- // We need to clean up our "real" name, which should look like the windows name right now
- // so overwrite the input with the cleaned up name
- _sRealName = new string(pBuffer, 0, result - 1);
- realNameBuffer = _sRealName;
-
- // Check for neutrality, don't expect to fail
- // (buffer has our name in it, so we don't have to do the gc. stuff)
-
- result = GetLocaleInfoEx(realNameBuffer, Interop.Kernel32.LOCALE_INEUTRAL | Interop.Kernel32.LOCALE_RETURN_NUMBER, pBuffer, sizeof(int) / sizeof(char));
- if (result == 0)
- {
- return false;
- }
-
- // Remember our neutrality
- _bNeutral = *((uint*)pBuffer) != 0;
-
- // Note: Parents will be set dynamically
-
- // Start by assuming the windows name will be the same as the specific name since windows knows
- // about specifics on all versions. Only for downlevel Neutral locales does this have to change.
- _sWindowsName = realNameBuffer;
-
- // Neutrals and non-neutrals are slightly different
- if (_bNeutral)
- {
- // Neutral Locale
-
- // IETF name looks like neutral name
- _sName = realNameBuffer;
-
- // Specific locale name is whatever ResolveLocaleName (win7+) returns.
- // (Buffer has our name in it, and we can recycle that because windows resolves it before writing to the buffer)
- result = Interop.Kernel32.ResolveLocaleName(realNameBuffer, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LENGTH);
-
- // 0 is failure, 1 is invariant (""), which we expect
- if (result < 1)
- {
- return false;
- }
- // We found a locale name, so use it.
- // In vista this should look like a sort name (de-DE_phoneb) or a specific culture (en-US) and be in the "pretty" form
- _sSpecificCulture = new string(pBuffer, 0, result - 1);
- }
- else
- {
- // Specific Locale
-
- // Specific culture's the same as the locale name since we know its not neutral
- // On mac we'll use this as well, even for neutrals. There's no obvious specific
- // culture to use and this isn't exposed, but behaviorally this is correct on mac.
- // Note that specifics include the sort name (de-DE_phoneb)
- _sSpecificCulture = realNameBuffer;
-
- _sName = realNameBuffer;
-
- // We need the IETF name (sname)
- // If we aren't an alt sort locale then this is the same as the windows name.
- // If we are an alt sort locale then this is the same as the part before the _ in the windows name
- // This is for like de-DE_phoneb and es-ES_tradnl that hsouldn't have the _ part
-
- result = GetLocaleInfoEx(realNameBuffer, Interop.Kernel32.LOCALE_ILANGUAGE | Interop.Kernel32.LOCALE_RETURN_NUMBER, pBuffer, sizeof(int) / sizeof(char));
- if (result == 0)
- {
- return false;
- }
-
- _iLanguage = *((int*)pBuffer);
-
- if (!IsCustomCultureId(_iLanguage))
- {
- // not custom locale
- int index = realNameBuffer.IndexOf('_');
- if (index > 0 && index < realNameBuffer.Length)
- {
- _sName = realNameBuffer.Substring(0, index);
- }
- }
- }
-
- // It succeeded.
- return true;
- }
-
// Wrappers around the GetLocaleInfoEx APIs which handle marshalling the returned
// data as either and Int or string.
internal static unsafe string? GetLocaleInfoEx(string localeName, uint field)
CultureInfo? ci;
if (CultureInfo.DefaultThreadCurrentUICulture != null &&
- ((ci = CultureInfo.NlsGetUserDefaultCulture()) != null) &&
+ ((ci = CultureInfo.GetUserDefaultCulture()) != null) &&
!CultureInfo.DefaultThreadCurrentUICulture.Name.Equals(ci.Name))
{
return NativeName;
return false;
}
}
-
- internal static unsafe CultureData NlsGetCurrentRegionData()
- {
- Debug.Assert(GlobalizationMode.UseNls);
- Span<char> geoIso2Letters = stackalloc char[10];
-
- int geoId = Interop.Kernel32.GetUserGeoID(Interop.Kernel32.GEOCLASS_NATION);
- if (geoId != Interop.Kernel32.GEOID_NOT_AVAILABLE)
- {
- int geoIsoIdLength;
- fixed (char* pGeoIsoId = geoIso2Letters)
- {
- geoIsoIdLength = Interop.Kernel32.GetGeoInfo(geoId, Interop.Kernel32.GEO_ISO2, pGeoIsoId, geoIso2Letters.Length, 0);
- }
-
- if (geoIsoIdLength != 0)
- {
- geoIsoIdLength -= geoIso2Letters[geoIsoIdLength - 1] == 0 ? 1 : 0; // handle null termination and exclude it.
- CultureData? cd = GetCultureDataForRegion(geoIso2Letters.Slice(0, geoIsoIdLength).ToString(), true);
- if (cd != null)
- {
- return cd;
- }
- }
- }
-
- // Fallback to current locale data.
- return CultureInfo.CurrentCulture._cultureData;
- }
}
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+
+namespace System.Globalization
+{
+ internal partial class CultureData
+ {
+ private const string ICU_COLLATION_KEYWORD = "@collation=";
+
+ /// <summary>
+ /// This method uses the sRealName field (which is initialized by the constructor before this is called) to
+ /// initialize the rest of the state of CultureData based on the underlying OS globalization library.
+ /// </summary>
+ private bool InitCultureDataCore()
+ {
+ Debug.Assert(_sRealName != null);
+ Debug.Assert(!GlobalizationMode.Invariant);
+
+ string realNameBuffer = _sRealName;
+
+ // Basic validation
+ if (realNameBuffer.Contains('@'))
+ {
+ return false; // don't allow ICU variants to come in directly
+ }
+
+ // Replace _ (alternate sort) with @collation= for ICU
+ ReadOnlySpan<char> alternateSortName = default;
+ int index = realNameBuffer.IndexOf('_');
+ if (index > 0)
+ {
+ if (index >= (realNameBuffer.Length - 1) // must have characters after _
+ || realNameBuffer.IndexOf('_', index + 1) >= 0) // only one _ allowed
+ {
+ return false; // fail
+ }
+ alternateSortName = realNameBuffer.AsSpan(index + 1);
+ realNameBuffer = string.Concat(realNameBuffer.AsSpan(0, index), ICU_COLLATION_KEYWORD, alternateSortName);
+ }
+
+ // Get the locale name from ICU
+ if (!GetLocaleName(realNameBuffer, out _sWindowsName))
+ {
+ return false; // fail
+ }
+
+ // Replace the ICU collation keyword with an _
+ Debug.Assert(_sWindowsName != null);
+ index = _sWindowsName.IndexOf(ICU_COLLATION_KEYWORD, StringComparison.Ordinal);
+ if (index >= 0)
+ {
+ _sName = string.Concat(_sWindowsName.AsSpan(0, index), "_", alternateSortName);
+ }
+ else
+ {
+ _sName = _sWindowsName;
+ }
+ _sRealName = _sName;
+
+ _iLanguage = LCID;
+ if (_iLanguage == 0)
+ {
+ _iLanguage = CultureInfo.LOCALE_CUSTOM_UNSPECIFIED;
+ }
+
+ _bNeutral = TwoLetterISOCountryName.Length == 0;
+
+ _sSpecificCulture = _bNeutral ? IcuLocaleData.GetSpecificCultureName(_sRealName) : _sRealName;
+
+ // Remove the sort from sName unless custom culture
+ if (index > 0 && !_bNeutral && !IsCustomCultureId(_iLanguage))
+ {
+ _sName = _sWindowsName.Substring(0, index);
+ }
+ return true;
+ }
+
+ internal bool IsWin32Installed => false;
+
+ internal static unsafe CultureData GetCurrentRegionData() => CultureInfo.CurrentCulture._cultureData;
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+
+namespace System.Globalization
+{
+ internal partial class CultureData
+ {
+ /// <summary>
+ /// Check with the OS to see if this is a valid culture.
+ /// If so we populate a limited number of fields. If its not valid we return false.
+ ///
+ /// The fields we populate:
+ ///
+ /// sWindowsName -- The name that windows thinks this culture is, ie:
+ /// en-US if you pass in en-US
+ /// de-DE_phoneb if you pass in de-DE_phoneb
+ /// fj-FJ if you pass in fj (neutral, on a pre-Windows 7 machine)
+ /// fj if you pass in fj (neutral, post-Windows 7 machine)
+ ///
+ /// sRealName -- The name you used to construct the culture, in pretty form
+ /// en-US if you pass in EN-us
+ /// en if you pass in en
+ /// de-DE_phoneb if you pass in de-DE_phoneb
+ ///
+ /// sSpecificCulture -- The specific culture for this culture
+ /// en-US for en-US
+ /// en-US for en
+ /// de-DE_phoneb for alt sort
+ /// fj-FJ for fj (neutral)
+ ///
+ /// sName -- The IETF name of this culture (ie: no sort info, could be neutral)
+ /// en-US if you pass in en-US
+ /// en if you pass in en
+ /// de-DE if you pass in de-DE_phoneb
+ ///
+ /// bNeutral -- TRUE if it is a neutral locale
+ ///
+ /// For a neutral we just populate the neutral name, but we leave the windows name pointing to the
+ /// windows locale that's going to provide data for us.
+ /// </summary>
+ private unsafe bool InitCultureDataCore()
+ {
+ Debug.Assert(!GlobalizationMode.Invariant);
+
+ int result;
+ string realNameBuffer = _sRealName;
+ char* pBuffer = stackalloc char[Interop.Kernel32.LOCALE_NAME_MAX_LENGTH];
+
+ result = GetLocaleInfoEx(realNameBuffer, Interop.Kernel32.LOCALE_SNAME, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LENGTH);
+
+ // Did it fail?
+ if (result == 0)
+ {
+ return false;
+ }
+
+ // It worked, note that the name is the locale name, so use that (even for neutrals)
+ // We need to clean up our "real" name, which should look like the windows name right now
+ // so overwrite the input with the cleaned up name
+ _sRealName = new string(pBuffer, 0, result - 1);
+ realNameBuffer = _sRealName;
+
+ // Check for neutrality, don't expect to fail
+ // (buffer has our name in it, so we don't have to do the gc. stuff)
+
+ result = GetLocaleInfoEx(realNameBuffer, Interop.Kernel32.LOCALE_INEUTRAL | Interop.Kernel32.LOCALE_RETURN_NUMBER, pBuffer, sizeof(int) / sizeof(char));
+ if (result == 0)
+ {
+ return false;
+ }
+
+ // Remember our neutrality
+ _bNeutral = *((uint*)pBuffer) != 0;
+
+ // Note: Parents will be set dynamically
+
+ // Start by assuming the windows name will be the same as the specific name since windows knows
+ // about specifics on all versions. Only for downlevel Neutral locales does this have to change.
+ _sWindowsName = realNameBuffer;
+
+ // Neutrals and non-neutrals are slightly different
+ if (_bNeutral)
+ {
+ // Neutral Locale
+
+ // IETF name looks like neutral name
+ _sName = realNameBuffer;
+
+ // Specific locale name is whatever ResolveLocaleName (win7+) returns.
+ // (Buffer has our name in it, and we can recycle that because windows resolves it before writing to the buffer)
+ result = Interop.Kernel32.ResolveLocaleName(realNameBuffer, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LENGTH);
+
+ // 0 is failure, 1 is invariant (""), which we expect
+ if (result < 1)
+ {
+ return false;
+ }
+
+ // We found a locale name, so use it.
+ // In vista this should look like a sort name (de-DE_phoneb) or a specific culture (en-US) and be in the "pretty" form
+ _sSpecificCulture = new string(pBuffer, 0, result - 1);
+ }
+ else
+ {
+ // Specific Locale
+
+ // Specific culture's the same as the locale name since we know its not neutral
+ // On mac we'll use this as well, even for neutrals. There's no obvious specific
+ // culture to use and this isn't exposed, but behaviorally this is correct on mac.
+ // Note that specifics include the sort name (de-DE_phoneb)
+ _sSpecificCulture = realNameBuffer;
+
+ _sName = realNameBuffer;
+
+ // We need the IETF name (sname)
+ // If we aren't an alt sort locale then this is the same as the windows name.
+ // If we are an alt sort locale then this is the same as the part before the _ in the windows name
+ // This is for like de-DE_phoneb and es-ES_tradnl that hsouldn't have the _ part
+
+ result = GetLocaleInfoEx(realNameBuffer, Interop.Kernel32.LOCALE_ILANGUAGE | Interop.Kernel32.LOCALE_RETURN_NUMBER, pBuffer, sizeof(int) / sizeof(char));
+ if (result == 0)
+ {
+ return false;
+ }
+
+ _iLanguage = *((int*)pBuffer);
+
+ if (!IsCustomCultureId(_iLanguage))
+ {
+ // not custom locale
+ int index = realNameBuffer.IndexOf('_');
+ if (index > 0)
+ {
+ _sName = realNameBuffer.Substring(0, index);
+ }
+ }
+ }
+
+ // It succeeded.
+ return true;
+ }
+
+ internal bool IsWin32Installed => true;
+
+ internal static unsafe CultureData GetCurrentRegionData()
+ {
+ Span<char> geoIso2Letters = stackalloc char[10];
+
+ int geoId = Interop.Kernel32.GetUserGeoID(Interop.Kernel32.GEOCLASS_NATION);
+ if (geoId != Interop.Kernel32.GEOID_NOT_AVAILABLE)
+ {
+ int geoIsoIdLength;
+ fixed (char* pGeoIsoId = geoIso2Letters)
+ {
+ geoIsoIdLength = Interop.Kernel32.GetGeoInfo(geoId, Interop.Kernel32.GEO_ISO2, pGeoIsoId, geoIso2Letters.Length, 0);
+ }
+
+ if (geoIsoIdLength != 0)
+ {
+ geoIsoIdLength -= geoIso2Letters[geoIsoIdLength - 1] == 0 ? 1 : 0; // handle null termination and exclude it.
+ CultureData? cd = GetCultureDataForRegion(geoIso2Letters.Slice(0, geoIsoIdLength).ToString(), true);
+ if (cd != null)
+ {
+ return cd;
+ }
+ }
+ }
+
+ // Fallback to current locale data.
+ return CultureInfo.CurrentCulture._cultureData;
+ }
+ }
+}
return true;
}
- private bool InitCultureDataCore() => GlobalizationMode.UseNls ?
- NlsInitCultureData() :
- IcuInitCultureData();
-
/// We'd rather people use the named version since this doesn't allow custom locales
internal static CultureData GetCultureData(int culture, bool bUseUserOverride)
{
internal bool IsInvariantCulture => string.IsNullOrEmpty(Name);
- internal bool IsWin32Installed => GlobalizationMode.UseNls;
-
internal bool IsReplacementCulture => GlobalizationMode.UseNls ? NlsIsReplacementCulture : false;
- internal static unsafe CultureData GetCurrentRegionData() => GlobalizationMode.UseNls ?
- NlsGetCurrentRegionData() :
- CultureInfo.CurrentCulture._cultureData;
-
/// <summary>
/// Get an instance of our default calendar
/// </summary>
{
public partial class CultureInfo : IFormatProvider
{
- internal static CultureInfo IcuGetUserDefaultCulture()
- {
- Debug.Assert(!GlobalizationMode.UseNls);
-
- if (GlobalizationMode.Invariant)
- return CultureInfo.InvariantCulture;
-
- CultureInfo cultureInfo;
- string? localeName;
- if (CultureData.GetDefaultLocaleName(out localeName))
- {
- Debug.Assert(localeName != null);
- cultureInfo = GetCultureByName(localeName);
- }
- else
- {
- cultureInfo = CultureInfo.InvariantCulture;
- }
-
- return cultureInfo;
- }
-
private static CultureInfo IcuGetPredefinedCultureInfo(string name)
{
Debug.Assert(!GlobalizationMode.UseNls);
return GetCultureInfo(name);
}
-
- private static CultureInfo IcuGetUserDefaultUICulture()
- {
- Debug.Assert(!GlobalizationMode.UseNls);
-
- return InitializeUserDefaultCulture();
- }
}
}
{
public partial class CultureInfo : IFormatProvider
{
- internal static CultureInfo NlsGetUserDefaultCulture()
- {
- Debug.Assert(GlobalizationMode.UseNls);
-
- if (GlobalizationMode.Invariant)
- return CultureInfo.InvariantCulture;
-
- string? strDefault = CultureData.GetLocaleInfoEx(Interop.Kernel32.LOCALE_NAME_USER_DEFAULT, Interop.Kernel32.LOCALE_SNAME);
- if (strDefault == null)
- {
- strDefault = CultureData.GetLocaleInfoEx(Interop.Kernel32.LOCALE_NAME_SYSTEM_DEFAULT, Interop.Kernel32.LOCALE_SNAME);
-
- if (strDefault == null)
- {
- // If system default doesn't work, use invariant
- return CultureInfo.InvariantCulture;
- }
- }
-
- return GetCultureByName(strDefault);
- }
-
private static CultureInfo NlsGetPredefinedCultureInfo(string name)
{
Debug.Assert(GlobalizationMode.UseNls);
return culture;
}
-
- private static unsafe CultureInfo NlsGetUserDefaultUICulture()
- {
- Debug.Assert(GlobalizationMode.UseNls);
-
- if (GlobalizationMode.Invariant)
- return CultureInfo.InvariantCulture;
-
- const uint MUI_LANGUAGE_NAME = 0x8; // Use ISO language (culture) name convention
- uint langCount = 0;
- uint bufLen = 0;
-
- if (Interop.Kernel32.GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &langCount, null, &bufLen) != Interop.BOOL.FALSE)
- {
- char[] languages = new char[bufLen];
- fixed (char* pLanguages = languages)
- {
- if (Interop.Kernel32.GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &langCount, pLanguages, &bufLen) != Interop.BOOL.FALSE)
- {
- int index = 0;
- while (languages[index] != (char)0 && index < languages.Length)
- {
- index++;
- }
-
- return GetCultureByName(new string(languages, 0, index));
- }
- }
- }
-
- return InitializeUserDefaultCulture();
- }
}
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+
+namespace System.Globalization
+{
+ public partial class CultureInfo : IFormatProvider
+ {
+ internal static CultureInfo GetUserDefaultCulture()
+ {
+ if (GlobalizationMode.Invariant)
+ return CultureInfo.InvariantCulture;
+
+ CultureInfo cultureInfo;
+ if (CultureData.GetDefaultLocaleName(out string? localeName))
+ {
+ Debug.Assert(localeName != null);
+ cultureInfo = GetCultureByName(localeName);
+ }
+ else
+ {
+ cultureInfo = CultureInfo.InvariantCulture;
+ }
+
+ return cultureInfo;
+ }
+
+ private static CultureInfo GetUserDefaultUICulture()
+ {
+ return InitializeUserDefaultCulture();
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Globalization
+{
+ public partial class CultureInfo : IFormatProvider
+ {
+ internal static CultureInfo GetUserDefaultCulture()
+ {
+ if (GlobalizationMode.Invariant)
+ return CultureInfo.InvariantCulture;
+
+ string? strDefault = CultureData.GetLocaleInfoEx(Interop.Kernel32.LOCALE_NAME_USER_DEFAULT, Interop.Kernel32.LOCALE_SNAME) ??
+ CultureData.GetLocaleInfoEx(Interop.Kernel32.LOCALE_NAME_SYSTEM_DEFAULT, Interop.Kernel32.LOCALE_SNAME);
+
+ return strDefault != null ?
+ GetCultureByName(strDefault) :
+ CultureInfo.InvariantCulture;
+ }
+
+ private static unsafe CultureInfo GetUserDefaultUICulture()
+ {
+ if (GlobalizationMode.Invariant)
+ return CultureInfo.InvariantCulture;
+
+ const uint MUI_LANGUAGE_NAME = 0x8; // Use ISO language (culture) name convention
+ uint langCount = 0;
+ uint bufLen = 0;
+
+ if (Interop.Kernel32.GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &langCount, null, &bufLen) != Interop.BOOL.FALSE)
+ {
+ Span<char> languages = bufLen <= 256 ? stackalloc char[(int)bufLen] : new char[bufLen];
+ fixed (char* pLanguages = languages)
+ {
+ if (Interop.Kernel32.GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &langCount, pLanguages, &bufLen) != Interop.BOOL.FALSE)
+ {
+ return GetCultureByName(languages.ToString());
+ }
+ }
+ }
+
+ return InitializeUserDefaultCulture();
+ }
+ }
+}
return new GregorianCalendar();
}
- internal static CultureInfo GetUserDefaultCulture() => GlobalizationMode.UseNls ?
- NlsGetUserDefaultCulture() :
- IcuGetUserDefaultCulture();
-
- private static CultureInfo GetUserDefaultUICulture() => GlobalizationMode.UseNls ?
- NlsGetUserDefaultUICulture() :
- IcuGetUserDefaultUICulture();
-
/// <summary>
/// Return/set the default calendar used by this culture.
/// This value can be overridden by regional option if this is a current culture.