Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 53dbf8f

Browse files
committed
Merge pull request #1723 from steveharter/FixDefaultLocale
Add support for obtaining default locale in Linux and fix issue with collation not being passed to ICU
2 parents 8d80e96 + e3562c0 commit 53dbf8f

File tree

6 files changed

+116
-65
lines changed

6 files changed

+116
-65
lines changed

src/corefx/System.Globalization.Native/locale.cpp

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ Locale GetLocale(const UChar* localeName, bool canonize)
3636

3737
if (localeName != NULL)
3838
{
39-
int32_t len = u_strlen(localeName);
40-
u_UCharsToChars(localeName, localeNameTemp, len + 1);
39+
// use UnicodeString.extract instead of u_UCharsToChars; u_UCharsToChars considers '@' a variant and stops
40+
UnicodeString str(localeName, -1, ULOC_FULLNAME_CAPACITY);
41+
str.extract(0, str.length(), localeNameTemp);
4142
}
4243

4344
Locale loc;
@@ -64,9 +65,10 @@ UErrorCode u_charsToUChars_safe(const char *str, UChar* value, int32_t valueLeng
6465
return U_ZERO_ERROR;
6566
}
6667

67-
void FixupLocaleName(UChar* value, int32_t valueLength)
68+
int FixupLocaleName(UChar* value, int32_t valueLength)
6869
{
69-
for (int i = 0; i < valueLength; i++)
70+
int i = 0;
71+
for (; i < valueLength; i++)
7072
{
7173
if (value[i] == (UChar)'\0')
7274
{
@@ -77,8 +79,9 @@ void FixupLocaleName(UChar* value, int32_t valueLength)
7779
value[i] = (UChar)'-';
7880
}
7981
}
80-
}
8182

83+
return i;
84+
}
8285

8386
extern "C" int32_t GetLocaleName(const UChar* localeName, UChar* value, int32_t valueLength)
8487
{
@@ -100,3 +103,34 @@ extern "C" int32_t GetLocaleName(const UChar* localeName, UChar* value, int32_t
100103

101104
return UErrorCodeToBool(status);
102105
}
106+
107+
extern "C" int32_t GetDefaultLocaleName(UChar* value, int32_t valueLength)
108+
{
109+
Locale locale = GetLocale(NULL);
110+
if (locale.isBogus())
111+
{
112+
// ICU should be able to get default locale
113+
return UErrorCodeToBool(U_INTERNAL_PROGRAM_ERROR);
114+
}
115+
116+
UErrorCode status = u_charsToUChars_safe(locale.getBaseName(), value, valueLength);
117+
if (U_SUCCESS(status))
118+
{
119+
int localeNameLen = FixupLocaleName(value, valueLength);
120+
121+
// if collation is present, return that to managed side
122+
char collationValueTemp[ULOC_KEYWORDS_CAPACITY];
123+
if (locale.getKeywordValue("collation", collationValueTemp, ULOC_KEYWORDS_CAPACITY, status) > 0)
124+
{
125+
// copy the collation; managed uses a "_" to represent collation (not "@collation=")
126+
status = u_charsToUChars_safe("_", &value[localeNameLen], valueLength - localeNameLen);
127+
if (U_SUCCESS(status))
128+
{
129+
status = u_charsToUChars_safe(collationValueTemp, &value[localeNameLen + 1], valueLength - localeNameLen - 1);
130+
}
131+
}
132+
}
133+
134+
return UErrorCodeToBool(status);
135+
}
136+

src/corefx/System.Globalization.Native/locale.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ UErrorCode u_charsToUChars_safe(const char *str, UChar* value, int32_t valueLeng
4343
Function:
4444
FixupLocaleName
4545
46-
Replace underscores with hyphens to interop with existing .NET code
46+
Replace underscores with hyphens to interop with existing .NET code.
47+
Returns the length of the string.
4748
*/
48-
void FixupLocaleName(UChar* value, int32_t valueLength);
49+
int FixupLocaleName(UChar* value, int32_t valueLength);

src/mscorlib/corefx/Interop/Unix/System.Globalization.Native/Interop.Locale.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ internal static partial class GlobalizationInterop
1717
[return: MarshalAs(UnmanagedType.Bool)]
1818
internal unsafe static extern bool GetLocaleInfoString(string localeName, uint localeStringData, [Out] StringBuilder value, int valueLength);
1919

20+
[DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode)]
21+
[return: MarshalAs(UnmanagedType.Bool)]
22+
internal unsafe static extern bool GetDefaultLocaleName([Out] StringBuilder value, int valueLength);
23+
2024
[DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode)]
2125
[return: MarshalAs(UnmanagedType.Bool)]
2226
internal unsafe static extern bool GetLocaleTimeFormat(string localeName, bool shortFormat, [Out] StringBuilder value, int valueLength);

src/mscorlib/corefx/System/Globalization/CultureData.Unix.cs

Lines changed: 50 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ namespace System.Globalization
1313
{
1414
internal partial class CultureData
1515
{
16-
// Win32 constants
17-
const string LOCALE_NAME_SYSTEM_DEFAULT = @"!x-sys-default-locale";
18-
1916
// ICU constants
2017
const int ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY = 100; // max size of keyword or value
2118
const int ICU_ULOC_FULLNAME_CAPACITY = 157; // max size of locale name
@@ -30,47 +27,35 @@ private unsafe bool InitCultureData()
3027
Contract.Assert(this.sRealName != null);
3128

3229
string alternateSortName = string.Empty;
33-
string realNameBuffer = null;
34-
int index;
30+
string realNameBuffer = this.sRealName;
3531

36-
bool useSystemDefault = (this.sRealName == LOCALE_NAME_SYSTEM_DEFAULT);
37-
if (!useSystemDefault) //ICU uses null to obtain the default (system) locale
32+
// Basic validation
33+
if (realNameBuffer.Contains("@"))
3834
{
39-
realNameBuffer = this.sRealName;
40-
41-
// Basic validation
42-
if (realNameBuffer.Contains("@"))
43-
{
44-
return false; // don't allow ICU variants to come in directly
45-
}
35+
return false; // don't allow ICU variants to come in directly
36+
}
4637

47-
// Replace _ (alternate sort) with @collation= for ICU
48-
index = realNameBuffer.IndexOf('_');
49-
if (index > 0)
38+
// Replace _ (alternate sort) with @collation= for ICU
39+
int index = realNameBuffer.IndexOf('_');
40+
if (index > 0)
41+
{
42+
if (index >= (realNameBuffer.Length - 1) // must have characters after _
43+
|| realNameBuffer.Substring(index + 1).Contains("_")) // only one _ allowed
5044
{
51-
if (index >= (realNameBuffer.Length - 1) // must have characters after _
52-
|| realNameBuffer.Substring(index + 1).Contains("_")) // only one _ allowed
53-
{
54-
return false; // fail
55-
}
56-
alternateSortName = realNameBuffer.Substring(index + 1);
57-
realNameBuffer = realNameBuffer.Substring(0, index) + ICU_COLLATION_KEYWORD + alternateSortName;
45+
return false; // fail
5846
}
47+
alternateSortName = realNameBuffer.Substring(index + 1);
48+
realNameBuffer = realNameBuffer.Substring(0, index) + ICU_COLLATION_KEYWORD + alternateSortName;
5949
}
6050

6151
// Get the locale name from ICU
62-
StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_FULLNAME_CAPACITY);
63-
if (!Interop.GlobalizationInterop.GetLocaleName(realNameBuffer, sb, sb.Capacity))
52+
if (!GetLocaleName(realNameBuffer, out this.sWindowsName))
6453
{
65-
StringBuilderCache.Release(sb);
6654
return false; // fail
6755
}
6856

69-
// Success - use the locale name returned which may be different than realNameBuffer (casing)
70-
this.sWindowsName = StringBuilderCache.GetStringAndRelease(sb); // the name passed to subsequent ICU calls
71-
7257
// Replace the ICU collation keyword with an _
73-
index = realNameBuffer.IndexOf(ICU_COLLATION_KEYWORD, StringComparison.Ordinal);
58+
index = this.sWindowsName.IndexOf(ICU_COLLATION_KEYWORD, StringComparison.Ordinal);
7459
if (index >= 0)
7560
{
7661
this.sName = this.sWindowsName.Substring(0, index) + "_" + alternateSortName;
@@ -79,21 +64,13 @@ private unsafe bool InitCultureData()
7964
{
8065
this.sName = this.sWindowsName;
8166
}
82-
8367
this.sRealName = this.sName;
8468
this.sSpecificCulture = this.sRealName; // we don't attempt to find a non-neutral locale if a neutral is passed in (unlike win32)
8569

8670
this.iLanguage = this.ILANGUAGE;
8771
if (this.iLanguage == 0)
8872
{
89-
if (useSystemDefault)
90-
{
91-
this.iLanguage = LOCALE_CUSTOM_DEFAULT;
92-
}
93-
else
94-
{
95-
this.iLanguage = LOCALE_CUSTOM_UNSPECIFIED;
96-
}
73+
this.iLanguage = LOCALE_CUSTOM_UNSPECIFIED;
9774
}
9875

9976
this.bNeutral = (this.SISO3166CTRYNAME.Length == 0);
@@ -106,10 +83,41 @@ private unsafe bool InitCultureData()
10683
this.sName = this.sWindowsName.Substring(0, index);
10784
}
10885
}
86+
return true;
87+
}
10988

89+
internal static bool GetLocaleName(string localeName, out string windowsName)
90+
{
91+
// Get the locale name from ICU
92+
StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_FULLNAME_CAPACITY);
93+
if (!Interop.GlobalizationInterop.GetLocaleName(localeName, sb, sb.Capacity))
94+
{
95+
StringBuilderCache.Release(sb);
96+
windowsName = null;
97+
return false; // fail
98+
}
99+
100+
// Success - use the locale name returned which may be different than realNameBuffer (casing)
101+
windowsName = StringBuilderCache.GetStringAndRelease(sb); // the name passed to subsequent ICU calls
102+
return true;
103+
}
104+
105+
internal static bool GetDefaultLocaleName(out string windowsName)
106+
{
107+
// Get the default (system) locale name from ICU
108+
StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_FULLNAME_CAPACITY);
109+
if (!Interop.GlobalizationInterop.GetDefaultLocaleName(sb, sb.Capacity))
110+
{
111+
StringBuilderCache.Release(sb);
112+
windowsName = null;
113+
return false; // fail
114+
}
115+
116+
// Success - use the locale name returned which may be different than realNameBuffer (casing)
117+
windowsName = StringBuilderCache.GetStringAndRelease(sb); // the name passed to subsequent ICU calls
110118
return true;
111119
}
112-
120+
113121
private string GetLocaleInfo(LocaleStringData type)
114122
{
115123
Contract.Assert(this.sWindowsName != null, "[CultureData.GetLocaleInfo] Expected this.sWindowsName to be populated already");
@@ -244,7 +252,7 @@ private static string GetRegionDisplayName(string isoCountryCode)
244252

245253
private static CultureInfo GetUserDefaultCulture()
246254
{
247-
return new CultureInfo(LOCALE_NAME_SYSTEM_DEFAULT);
255+
return CultureInfo.GetUserDefaultCulture();
248256
}
249257
}
250258
}

src/mscorlib/corefx/System/Globalization/CultureData.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1679,8 +1679,8 @@ private int IREADINGLAYOUT
16791679
{
16801680
// Note: Custom cultures might point at another culture's textinfo, however windows knows how
16811681
// to redirect it to the desired textinfo culture, so this is OK.
1682-
Contract.Assert(this.sWindowsName != null, "[CultureData.STEXTINFO] Expected this.sWindowsName to be populated by already");
1683-
return (this.sWindowsName);
1682+
Contract.Assert(this.sRealName != null, "[CultureData.STEXTINFO] Expected this.sRealName to be populated by already");
1683+
return (this.sRealName);
16841684
}
16851685
}
16861686

@@ -1689,8 +1689,8 @@ internal String SCOMPAREINFO
16891689
{
16901690
get
16911691
{
1692-
Contract.Assert(this.sWindowsName != null, "[CultureData.SCOMPAREINFO] Expected this.sWindowsName to be populated by already");
1693-
return (this.sWindowsName);
1692+
Contract.Assert(this.sRealName != null, "[CultureData.SCOMPAREINFO] Expected this.sRealName to be populated by already");
1693+
return (this.sRealName);
16941694
}
16951695
}
16961696

src/mscorlib/corefx/System/Globalization/CultureInfo.Unix.cs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,26 @@ namespace System.Globalization
55
{
66
public partial class CultureInfo : IFormatProvider
77
{
8-
/// <summary>
9-
/// Gets the default user culture from WinRT, if available.
10-
/// </summary>
11-
/// <remarks>
12-
/// This method may return null, if there is no default user culture or if WinRT isn't available.
13-
/// </remarks>
148
private static CultureInfo GetUserDefaultCultureCacheOverride()
159
{
16-
// TODO: Implement this fully.
17-
return null;
10+
return null; // ICU doesn't provide a user override
1811
}
1912

20-
private static CultureInfo GetUserDefaultCulture()
13+
internal static CultureInfo GetUserDefaultCulture()
2114
{
22-
// TODO: Implement this fully.
23-
return CultureInfo.InvariantCulture;
15+
CultureInfo cultureInfo = null;
16+
string localeName;
17+
if (CultureData.GetDefaultLocaleName(out localeName))
18+
{
19+
cultureInfo = GetCultureByName(localeName, true);
20+
cultureInfo.m_isReadOnly = true;
21+
}
22+
else
23+
{
24+
cultureInfo = CultureInfo.InvariantCulture;
25+
}
26+
27+
return cultureInfo;
2428
}
2529
}
26-
}
30+
}

0 commit comments

Comments
 (0)