diff --git a/src/corefx/System.Globalization.Native/locale.cpp b/src/corefx/System.Globalization.Native/locale.cpp index e02c32d6807a..ca4569f9c935 100644 --- a/src/corefx/System.Globalization.Native/locale.cpp +++ b/src/corefx/System.Globalization.Native/locale.cpp @@ -36,8 +36,9 @@ Locale GetLocale(const UChar* localeName, bool canonize) if (localeName != NULL) { - int32_t len = u_strlen(localeName); - u_UCharsToChars(localeName, localeNameTemp, len + 1); + // use UnicodeString.extract instead of u_UCharsToChars; u_UCharsToChars considers '@' a variant and stops + UnicodeString str(localeName, -1, ULOC_FULLNAME_CAPACITY); + str.extract(0, str.length(), localeNameTemp); } Locale loc; @@ -64,9 +65,10 @@ UErrorCode u_charsToUChars_safe(const char *str, UChar* value, int32_t valueLeng return U_ZERO_ERROR; } -void FixupLocaleName(UChar* value, int32_t valueLength) +int FixupLocaleName(UChar* value, int32_t valueLength) { - for (int i = 0; i < valueLength; i++) + int i = 0; + for (; i < valueLength; i++) { if (value[i] == (UChar)'\0') { @@ -77,8 +79,9 @@ void FixupLocaleName(UChar* value, int32_t valueLength) value[i] = (UChar)'-'; } } -} + return i; +} extern "C" int32_t GetLocaleName(const UChar* localeName, UChar* value, int32_t valueLength) { @@ -100,3 +103,34 @@ extern "C" int32_t GetLocaleName(const UChar* localeName, UChar* value, int32_t return UErrorCodeToBool(status); } + +extern "C" int32_t GetDefaultLocaleName(UChar* value, int32_t valueLength) +{ + Locale locale = GetLocale(NULL); + if (locale.isBogus()) + { + // ICU should be able to get default locale + return UErrorCodeToBool(U_INTERNAL_PROGRAM_ERROR); + } + + UErrorCode status = u_charsToUChars_safe(locale.getBaseName(), value, valueLength); + if (U_SUCCESS(status)) + { + int localeNameLen = FixupLocaleName(value, valueLength); + + // if collation is present, return that to managed side + char collationValueTemp[ULOC_KEYWORDS_CAPACITY]; + if (locale.getKeywordValue("collation", collationValueTemp, ULOC_KEYWORDS_CAPACITY, status) > 0) + { + // copy the collation; managed uses a "_" to represent collation (not "@collation=") + status = u_charsToUChars_safe("_", &value[localeNameLen], valueLength - localeNameLen); + if (U_SUCCESS(status)) + { + status = u_charsToUChars_safe(collationValueTemp, &value[localeNameLen + 1], valueLength - localeNameLen - 1); + } + } + } + + return UErrorCodeToBool(status); +} + diff --git a/src/corefx/System.Globalization.Native/locale.hpp b/src/corefx/System.Globalization.Native/locale.hpp index 894ea909361c..0c100dae3c06 100644 --- a/src/corefx/System.Globalization.Native/locale.hpp +++ b/src/corefx/System.Globalization.Native/locale.hpp @@ -43,6 +43,7 @@ UErrorCode u_charsToUChars_safe(const char *str, UChar* value, int32_t valueLeng Function: FixupLocaleName -Replace underscores with hyphens to interop with existing .NET code +Replace underscores with hyphens to interop with existing .NET code. +Returns the length of the string. */ -void FixupLocaleName(UChar* value, int32_t valueLength); +int FixupLocaleName(UChar* value, int32_t valueLength); diff --git a/src/mscorlib/corefx/Interop/Unix/System.Globalization.Native/Interop.Locale.cs b/src/mscorlib/corefx/Interop/Unix/System.Globalization.Native/Interop.Locale.cs index e8dfe739a0c9..3c291e50eaf4 100644 --- a/src/mscorlib/corefx/Interop/Unix/System.Globalization.Native/Interop.Locale.cs +++ b/src/mscorlib/corefx/Interop/Unix/System.Globalization.Native/Interop.Locale.cs @@ -17,6 +17,10 @@ internal static partial class GlobalizationInterop [return: MarshalAs(UnmanagedType.Bool)] internal unsafe static extern bool GetLocaleInfoString(string localeName, uint localeStringData, [Out] StringBuilder value, int valueLength); + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + internal unsafe static extern bool GetDefaultLocaleName([Out] StringBuilder value, int valueLength); + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.Bool)] internal unsafe static extern bool GetLocaleTimeFormat(string localeName, bool shortFormat, [Out] StringBuilder value, int valueLength); diff --git a/src/mscorlib/corefx/System/Globalization/CultureData.Unix.cs b/src/mscorlib/corefx/System/Globalization/CultureData.Unix.cs index 336db749a033..f53d7ceb772a 100644 --- a/src/mscorlib/corefx/System/Globalization/CultureData.Unix.cs +++ b/src/mscorlib/corefx/System/Globalization/CultureData.Unix.cs @@ -13,9 +13,6 @@ namespace System.Globalization { internal partial class CultureData { - // Win32 constants - const string LOCALE_NAME_SYSTEM_DEFAULT = @"!x-sys-default-locale"; - // ICU constants const int ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY = 100; // max size of keyword or value const int ICU_ULOC_FULLNAME_CAPACITY = 157; // max size of locale name @@ -30,47 +27,35 @@ private unsafe bool InitCultureData() Contract.Assert(this.sRealName != null); string alternateSortName = string.Empty; - string realNameBuffer = null; - int index; + string realNameBuffer = this.sRealName; - bool useSystemDefault = (this.sRealName == LOCALE_NAME_SYSTEM_DEFAULT); - if (!useSystemDefault) //ICU uses null to obtain the default (system) locale + // Basic validation + if (realNameBuffer.Contains("@")) { - realNameBuffer = this.sRealName; - - // Basic validation - if (realNameBuffer.Contains("@")) - { - return false; // don't allow ICU variants to come in directly - } + return false; // don't allow ICU variants to come in directly + } - // Replace _ (alternate sort) with @collation= for ICU - index = realNameBuffer.IndexOf('_'); - if (index > 0) + // Replace _ (alternate sort) with @collation= for ICU + int index = realNameBuffer.IndexOf('_'); + if (index > 0) + { + if (index >= (realNameBuffer.Length - 1) // must have characters after _ + || realNameBuffer.Substring(index + 1).Contains("_")) // only one _ allowed { - if (index >= (realNameBuffer.Length - 1) // must have characters after _ - || realNameBuffer.Substring(index + 1).Contains("_")) // only one _ allowed - { - return false; // fail - } - alternateSortName = realNameBuffer.Substring(index + 1); - realNameBuffer = realNameBuffer.Substring(0, index) + ICU_COLLATION_KEYWORD + alternateSortName; + return false; // fail } + alternateSortName = realNameBuffer.Substring(index + 1); + realNameBuffer = realNameBuffer.Substring(0, index) + ICU_COLLATION_KEYWORD + alternateSortName; } // Get the locale name from ICU - StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_FULLNAME_CAPACITY); - if (!Interop.GlobalizationInterop.GetLocaleName(realNameBuffer, sb, sb.Capacity)) + if (!GetLocaleName(realNameBuffer, out this.sWindowsName)) { - StringBuilderCache.Release(sb); return false; // fail } - // Success - use the locale name returned which may be different than realNameBuffer (casing) - this.sWindowsName = StringBuilderCache.GetStringAndRelease(sb); // the name passed to subsequent ICU calls - // Replace the ICU collation keyword with an _ - index = realNameBuffer.IndexOf(ICU_COLLATION_KEYWORD, StringComparison.Ordinal); + index = this.sWindowsName.IndexOf(ICU_COLLATION_KEYWORD, StringComparison.Ordinal); if (index >= 0) { this.sName = this.sWindowsName.Substring(0, index) + "_" + alternateSortName; @@ -79,21 +64,13 @@ private unsafe bool InitCultureData() { this.sName = this.sWindowsName; } - this.sRealName = this.sName; this.sSpecificCulture = this.sRealName; // we don't attempt to find a non-neutral locale if a neutral is passed in (unlike win32) this.iLanguage = this.ILANGUAGE; if (this.iLanguage == 0) { - if (useSystemDefault) - { - this.iLanguage = LOCALE_CUSTOM_DEFAULT; - } - else - { - this.iLanguage = LOCALE_CUSTOM_UNSPECIFIED; - } + this.iLanguage = LOCALE_CUSTOM_UNSPECIFIED; } this.bNeutral = (this.SISO3166CTRYNAME.Length == 0); @@ -106,10 +83,41 @@ private unsafe bool InitCultureData() this.sName = this.sWindowsName.Substring(0, index); } } + return true; + } + internal static bool GetLocaleName(string localeName, out string windowsName) + { + // Get the locale name from ICU + StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_FULLNAME_CAPACITY); + if (!Interop.GlobalizationInterop.GetLocaleName(localeName, sb, sb.Capacity)) + { + StringBuilderCache.Release(sb); + windowsName = null; + return false; // fail + } + + // Success - use the locale name returned which may be different than realNameBuffer (casing) + windowsName = StringBuilderCache.GetStringAndRelease(sb); // the name passed to subsequent ICU calls + return true; + } + + internal static bool GetDefaultLocaleName(out string windowsName) + { + // Get the default (system) locale name from ICU + StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_FULLNAME_CAPACITY); + if (!Interop.GlobalizationInterop.GetDefaultLocaleName(sb, sb.Capacity)) + { + StringBuilderCache.Release(sb); + windowsName = null; + return false; // fail + } + + // Success - use the locale name returned which may be different than realNameBuffer (casing) + windowsName = StringBuilderCache.GetStringAndRelease(sb); // the name passed to subsequent ICU calls return true; } - + private string GetLocaleInfo(LocaleStringData type) { Contract.Assert(this.sWindowsName != null, "[CultureData.GetLocaleInfo] Expected this.sWindowsName to be populated already"); @@ -244,7 +252,7 @@ private static string GetRegionDisplayName(string isoCountryCode) private static CultureInfo GetUserDefaultCulture() { - return new CultureInfo(LOCALE_NAME_SYSTEM_DEFAULT); + return CultureInfo.GetUserDefaultCulture(); } } } diff --git a/src/mscorlib/corefx/System/Globalization/CultureData.cs b/src/mscorlib/corefx/System/Globalization/CultureData.cs index 217bda6445d5..aabe66e23877 100644 --- a/src/mscorlib/corefx/System/Globalization/CultureData.cs +++ b/src/mscorlib/corefx/System/Globalization/CultureData.cs @@ -1679,8 +1679,8 @@ private int IREADINGLAYOUT { // Note: Custom cultures might point at another culture's textinfo, however windows knows how // to redirect it to the desired textinfo culture, so this is OK. - Contract.Assert(this.sWindowsName != null, "[CultureData.STEXTINFO] Expected this.sWindowsName to be populated by already"); - return (this.sWindowsName); + Contract.Assert(this.sRealName != null, "[CultureData.STEXTINFO] Expected this.sRealName to be populated by already"); + return (this.sRealName); } } @@ -1689,8 +1689,8 @@ internal String SCOMPAREINFO { get { - Contract.Assert(this.sWindowsName != null, "[CultureData.SCOMPAREINFO] Expected this.sWindowsName to be populated by already"); - return (this.sWindowsName); + Contract.Assert(this.sRealName != null, "[CultureData.SCOMPAREINFO] Expected this.sRealName to be populated by already"); + return (this.sRealName); } } diff --git a/src/mscorlib/corefx/System/Globalization/CultureInfo.Unix.cs b/src/mscorlib/corefx/System/Globalization/CultureInfo.Unix.cs index a6f0b3009134..a786d11f1ae1 100644 --- a/src/mscorlib/corefx/System/Globalization/CultureInfo.Unix.cs +++ b/src/mscorlib/corefx/System/Globalization/CultureInfo.Unix.cs @@ -5,22 +5,26 @@ namespace System.Globalization { public partial class CultureInfo : IFormatProvider { - /// - /// Gets the default user culture from WinRT, if available. - /// - /// - /// This method may return null, if there is no default user culture or if WinRT isn't available. - /// private static CultureInfo GetUserDefaultCultureCacheOverride() { - // TODO: Implement this fully. - return null; + return null; // ICU doesn't provide a user override } - private static CultureInfo GetUserDefaultCulture() + internal static CultureInfo GetUserDefaultCulture() { - // TODO: Implement this fully. - return CultureInfo.InvariantCulture; + CultureInfo cultureInfo = null; + string localeName; + if (CultureData.GetDefaultLocaleName(out localeName)) + { + cultureInfo = GetCultureByName(localeName, true); + cultureInfo.m_isReadOnly = true; + } + else + { + cultureInfo = CultureInfo.InvariantCulture; + } + + return cultureInfo; } } -} \ No newline at end of file +}