mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-20 00:50:25 +02:00
589 lines
20 KiB
Beef
589 lines
20 KiB
Beef
using System.Diagnostics;
|
|
using System.Collections.Generic;
|
|
// This file contains portions of code released by Microsoft under the MIT license as part
|
|
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
|
// The original source was submitted to https://github.com/Microsoft/referencesource
|
|
|
|
namespace System.Globalization
|
|
{
|
|
class CultureData
|
|
{
|
|
private const uint LOCALE_NOUSEROVERRIDE = 0x80000000; // do not use user overrides
|
|
|
|
private const uint LOCALE_SLOCALIZEDDISPLAYNAME = 0x00000002; // localized name of locale, eg "German (Germany)" in UI language
|
|
private const uint LOCALE_SENGLISHDISPLAYNAME = 0x00000072; // Display name (language + country usually) in English, eg "German (Germany)"
|
|
private const uint LOCALE_SNATIVEDISPLAYNAME = 0x00000073; // Display name in native locale language, eg "Deutsch (Deutschland)
|
|
|
|
private const uint LOCALE_SLOCALIZEDLANGUAGENAME = 0x0000006f; // Language Display Name for a language, eg "German" in UI language
|
|
private const uint LOCALE_SENGLISHLANGUAGENAME = 0x00001001; // English name of language, eg "German"
|
|
private const uint LOCALE_SNATIVELANGUAGENAME = 0x00000004; // native name of language, eg "Deutsch"
|
|
|
|
private const uint LOCALE_SLOCALIZEDCOUNTRYNAME = 0x00000006; // localized name of country, eg "Germany" in UI language
|
|
private const uint LOCALE_SENGLISHCOUNTRYNAME = 0x00001002; // English name of country, eg "Germany"
|
|
private const uint LOCALE_SNATIVECOUNTRYNAME = 0x00000008; // native name of country, eg "Deutschland"
|
|
|
|
private const uint LOCALE_STIME = 0x0000001E; // time separator (derived from LOCALE_STIMEFORMAT, use that instead)
|
|
private const uint LOCALE_STIMEFORMAT = 0x00001003; // time format string
|
|
|
|
const int32 undef = -1;
|
|
|
|
// Override flag
|
|
private String sRealName ~ delete _; // Name you passed in (ie: en-US, en, or de-DE_phoneb)
|
|
private String sWindowsName ~ delete _; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
|
|
|
|
// Identity
|
|
private String sName ~ delete _; // locale name (ie: en-us, NO sort info, but could be neutral)
|
|
private String sParent; // Parent name (which may be a custom locale/culture)
|
|
private String sLocalizedDisplayName; // Localized pretty name for this locale
|
|
private String sEnglishDisplayName; // English pretty name for this locale
|
|
private String sNativeDisplayName; // Native pretty name for this locale
|
|
private String sSpecificCulture ~ delete _; // The culture name to be used in CultureInfo.CreateSpecificCulture(), en-US form if neutral, sort name if sort
|
|
|
|
// Language
|
|
private String sISO639Language; // ISO 639 Language Name
|
|
private String sLocalizedLanguage; // Localized name for this language
|
|
private String sEnglishLanguage; // English name for this language
|
|
private String sNativeLanguage; // Native name of this language
|
|
|
|
// Region
|
|
private String sRegionName; // (RegionInfo)
|
|
// private int iCountry=undef ; // (user can override) ---- code (RegionInfo)
|
|
private int iGeoId = undef; // GeoId
|
|
private String sLocalizedCountry; // localized country name
|
|
private String sEnglishCountry; // english country name (RegionInfo)
|
|
private String sNativeCountry; // native country name
|
|
private String sISO3166CountryName; // ISO 3166 (RegionInfo), ie: US
|
|
|
|
// Numbers
|
|
private String sPositiveSign; // (user can override) positive sign
|
|
private String sNegativeSign; // (user can override) negative sign
|
|
private String[] saNativeDigits; // (user can override) native characters for digits 0-9
|
|
// (nfi populates these 5, don't have to be = undef)
|
|
private int32 iDigitSubstitution; // (user can override) Digit substitution 0=context, 1=none/arabic, 2=Native/national (2 seems to be unused)
|
|
private int32 iLeadingZeros; // (user can override) leading zeros 0 = no leading zeros, 1 = leading zeros
|
|
private int32 iDigits; // (user can override) number of fractional digits
|
|
private int32 iNegativeNumber; // (user can override) negative number format
|
|
private int32[] waGrouping; // (user can override) grouping of digits
|
|
private String sDecimalSeparator; // (user can override) decimal separator
|
|
private String sThousandSeparator; // (user can override) thousands separator
|
|
private String sNaN; // Not a Number
|
|
private String sPositiveInfinity; // + Infinity
|
|
private String sNegativeInfinity; // - Infinity
|
|
|
|
// Percent
|
|
private int iNegativePercent = undef; // Negative Percent (0-3)
|
|
private int iPositivePercent = undef; // Positive Percent (0-11)
|
|
private String sPercent; // Percent (%) symbol
|
|
private String sPerMille; // PerMille (‰) symbol
|
|
|
|
// Currency
|
|
private String sCurrency; // (user can override) local monetary symbol
|
|
private String sIntlMonetarySymbol; // international monetary symbol (RegionInfo)
|
|
private String sEnglishCurrency; // English name for this currency
|
|
private String sNativeCurrency; // Native name for this currency
|
|
// (nfi populates these 4, don't have to be = undef)
|
|
private int iCurrencyDigits; // (user can override) # local monetary fractional digits
|
|
private int iCurrency; // (user can override) positive currency format
|
|
private int iNegativeCurrency; // (user can override) negative currency format
|
|
private int[] waMonetaryGrouping; // (user can override) monetary grouping of digits
|
|
private String sMonetaryDecimal; // (user can override) monetary decimal separator
|
|
private String sMonetaryThousand; // (user can override) monetary thousands separator
|
|
|
|
// Misc
|
|
private int32 iMeasure = undef; // (user can override) system of measurement 0=metric, 1=US (RegionInfo)
|
|
private String sListSeparator; // (user can override) list separator
|
|
// private int iPaperSize ; // default paper size (RegionInfo)
|
|
|
|
// Time
|
|
private String sAM1159; // (user can override) AM designator
|
|
private String sPM2359; // (user can override) PM designator
|
|
private String sTimeSeparator;
|
|
private volatile String[] saLongTimes; // (user can override) time format
|
|
private volatile String[] saShortTimes; // short time format
|
|
private volatile String[] saDurationFormats; // time duration format
|
|
|
|
// Calendar specific data
|
|
private int iFirstDayOfWeek = undef; // (user can override) first day of week (gregorian really)
|
|
private int iFirstWeekOfYear = undef; // (user can override) first week of year (gregorian really)
|
|
private volatile int[] waCalendars; // all available calendar type(s). The first one is the default calendar
|
|
|
|
// Store for specific data about each calendar
|
|
private CalendarData[] calendars; // Store for specific calendar data
|
|
|
|
// Text information
|
|
private int iReadingLayout = undef; // Reading layout data
|
|
// 0 - Left to right (eg en-US)
|
|
// 1 - Right to left (eg arabic locales)
|
|
// 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
|
|
// 3 - Vertical top to bottom with columns proceeding to the right
|
|
|
|
private String sTextInfo; // Text info name to use for custom
|
|
private String sCompareInfo; // Compare info name (including sorting key) to use if custom
|
|
private String sScripts; // Typical Scripts for this locale (latn;cyrl; etc)
|
|
|
|
// The bools all need to be in one spot
|
|
private bool bUseOverrides; // use user overrides?
|
|
private bool bNeutral; // Flags for the culture (ie: neutral or not right now)
|
|
|
|
List<Object> ownedObjects = new .() ~ DeleteContainerAndItems!(_);
|
|
|
|
public this()
|
|
{
|
|
|
|
}
|
|
|
|
internal void GetNFIValues(NumberFormatInfo nfi)
|
|
{
|
|
|
|
}
|
|
|
|
public bool IsInvariantCulture
|
|
{
|
|
get
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
internal Calendar DefaultCalendar
|
|
{
|
|
get
|
|
{
|
|
/*int defaultCalId = DoGetLocaleInfoInt(LOCALE_ICALENDARTYPE);
|
|
if (defaultCalId == 0)
|
|
{
|
|
defaultCalId = this.CalendarIds[0];
|
|
}
|
|
|
|
return CultureInfo.GetCalendarInstance(defaultCalId);*/
|
|
//Runtime.NotImplemented();
|
|
// NotImplemented
|
|
return CultureInfo.GetCalendarInstance(Calendar.CAL_GREGORIAN);
|
|
}
|
|
}
|
|
|
|
internal StringView CultureName
|
|
{
|
|
get
|
|
{
|
|
switch (this.sName)
|
|
{
|
|
case "zh-CHS":
|
|
case "zh-CHT":
|
|
return this.sName;
|
|
}
|
|
return this.sRealName;
|
|
}
|
|
}
|
|
|
|
internal String[] LongTimes
|
|
{
|
|
get
|
|
{
|
|
if (this.saLongTimes == null
|
|
#if !FEATURE_CORECLR
|
|
|| UseUserOverride
|
|
#endif
|
|
)
|
|
{
|
|
/*String[] longTimes = DoEnumTimeFormats();
|
|
if (longTimes == null || longTimes.Length == 0)
|
|
{
|
|
this.saLongTimes = Invariant.saLongTimes;
|
|
}
|
|
else
|
|
{
|
|
this.saLongTimes = longTimes;
|
|
}*/
|
|
//NotImpl
|
|
saLongTimes = new String[](new .("h:mm:ss tt"));
|
|
ownedObjects.Add(saLongTimes);
|
|
ownedObjects.Add(saLongTimes[0]);
|
|
|
|
}
|
|
return this.saLongTimes;
|
|
}
|
|
}
|
|
|
|
internal String[] ShortTimes
|
|
{
|
|
get
|
|
{
|
|
if (this.saShortTimes == null
|
|
#if !FEATURE_CORECLR
|
|
|| UseUserOverride
|
|
#endif
|
|
)
|
|
{
|
|
// Try to get the short times from the OS/culture.dll
|
|
/*String[] shortTimes = DoEnumShortTimeFormats();
|
|
|
|
if (shortTimes == null || shortTimes.Length == 0)
|
|
{
|
|
//
|
|
// If we couldn't find short times, then compute them from long times
|
|
// (eg: CORECLR on < Win7 OS & fallback for missing culture.dll)
|
|
//
|
|
shortTimes = DeriveShortTimesFromLong();
|
|
}
|
|
|
|
// Found short times, use them
|
|
this.saShortTimes = shortTimes;*/
|
|
saShortTimes = new String[](new .("h:mm tt"));
|
|
ownedObjects.Add(saShortTimes);
|
|
ownedObjects.Add(saShortTimes[0]);
|
|
}
|
|
return this.saShortTimes;
|
|
}
|
|
}
|
|
|
|
internal static CultureData GetCultureData(StringView cultureName, bool useUserOverride)
|
|
{
|
|
CultureData culture = CreateCultureData(cultureName, useUserOverride);
|
|
return culture;
|
|
|
|
}
|
|
|
|
private static CultureData CreateCultureData(StringView cultureName, bool useUserOverride)
|
|
{
|
|
CultureData culture = new CultureData();
|
|
//culture.bUseOverrides = useUserOverride;
|
|
//culture.sRealName = cultureName;
|
|
|
|
// Ask native code if that one's real
|
|
if (culture.InitCultureData() == false)
|
|
{
|
|
/*#if !FEATURE_CORECLR
|
|
if (culture.InitCompatibilityCultureData() == false
|
|
&& culture.InitLegacyAlternateSortData() == false)
|
|
#endif*/
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return culture;
|
|
}
|
|
|
|
private bool InitCultureData()
|
|
{
|
|
sWindowsName = new String("en-US");
|
|
sRealName = new String("en-US");
|
|
sSpecificCulture = new String("en-US");
|
|
sName = new String("en-US");
|
|
|
|
//NotImplemented
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Reescape a Win32 style quote string as a NLS+ style quoted string
|
|
//
|
|
// This is also the escaping style used by custom culture data files
|
|
//
|
|
// NLS+ uses \ to escape the next character, whether in a quoted string or
|
|
// not, so we always have to change \ to \\.
|
|
//
|
|
// NLS+ uses \' to escape a quote inside a quoted string so we have to change
|
|
// '' to \' (if inside a quoted string)
|
|
//
|
|
// We don't build the stringbuilder unless we find something to change
|
|
////////////////////////////////////////////////////////////////////////////
|
|
static internal void ReescapeWin32String(String inStr)
|
|
{
|
|
// If we don't have data, then don't try anything
|
|
if (inStr == null)
|
|
return;
|
|
|
|
var inStr;
|
|
|
|
|
|
bool inQuote = false;
|
|
for (int i = 0; i < inStr.Length; i++)
|
|
{
|
|
// Look for quote
|
|
if (inStr[i] == '\'')
|
|
{
|
|
// Already in quote?
|
|
if (inQuote)
|
|
{
|
|
// See another single quote. Is this '' of 'fred''s' or '''', or is it an ending quote?
|
|
if (i + 1 < inStr.Length && inStr[i + 1] == '\'')
|
|
{
|
|
// Found another ', so we have ''. Need to add \' instead.
|
|
// 1st make sure we have our stringbuilder
|
|
|
|
// Append a \' and keep going (so we don't turn off quote mode)
|
|
inStr.Insert(i, "\\");
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
// Turning off quote mode, fall through to add it
|
|
inQuote = false;
|
|
}
|
|
else
|
|
{
|
|
// Found beginning quote, fall through to add it
|
|
inQuote = true;
|
|
}
|
|
}
|
|
// Is there a single \ character?
|
|
else if (inStr[i] == '\\')
|
|
{
|
|
// Found a \, need to change it to \\
|
|
// 1st make sure we have our stringbuilder
|
|
|
|
// Append our \\ to the string & continue
|
|
inStr.Insert(i, "\\\\");
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
static internal void ReescapeWin32Strings(String[] inArray)
|
|
{
|
|
if (inArray != null)
|
|
{
|
|
for (int i = 0; i < inArray.Count; i++)
|
|
{
|
|
ReescapeWin32String(inArray[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal bool UseUserOverride
|
|
{
|
|
get
|
|
{
|
|
return this.bUseOverrides;
|
|
}
|
|
}
|
|
|
|
internal bool IsSupplementalCustomCulture
|
|
{
|
|
get
|
|
{
|
|
//return IsCustomCultureId(this.ILANGUAGE);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static private bool IsOsWin7OrPrior()
|
|
{
|
|
return Environment.OSVersion.Platform == PlatformID.Win32NT &&
|
|
Environment.OSVersion.Version < Version(6, 2); // Win7 is 6.1.Build.Revision so we have to check for anything less than 6.2
|
|
}
|
|
|
|
internal CalendarData GetCalendar(int calendarId)
|
|
{
|
|
Debug.Assert(calendarId > 0 && calendarId <= CalendarData.MAX_CALENDARS,
|
|
"[CultureData.GetCalendar] Expect calendarId to be in a valid range");
|
|
|
|
// arrays are 0 based, calendarIds are 1 based
|
|
int calendarIndex = calendarId - 1;
|
|
|
|
// Have to have calendars
|
|
if (calendars == null)
|
|
{
|
|
calendars = new CalendarData[CalendarData.MAX_CALENDARS];
|
|
}
|
|
|
|
// we need the following local variable to avoid returning null
|
|
// when another thread creates a new array of CalendarData (above)
|
|
// right after we insert the newly created CalendarData (below)
|
|
CalendarData calendarData = calendars[calendarIndex];
|
|
// Make sure that calendar has data
|
|
if (calendarData == null
|
|
#if !FEATURE_CORECLR
|
|
|| UseUserOverride
|
|
#endif
|
|
)
|
|
{
|
|
//Contract.Assert(this.sWindowsName != null, "[CultureData.GetCalendar] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
|
|
calendarData = new CalendarData(this.sWindowsName, calendarId, this.UseUserOverride);
|
|
/*#if !FEATURE_CORECLR
|
|
//Work around issue where Win7 data for MonthDay contains invalid two sets of data separated by semicolon
|
|
//even though MonthDay is not enumerated
|
|
if (IsOsWin7OrPrior() && !IsSupplementalCustomCulture && !IsReplacementCulture)
|
|
{
|
|
calendarData.FixupWin7MonthDaySemicolonBug();
|
|
}
|
|
#endif*/
|
|
calendars[calendarIndex] = calendarData;
|
|
}
|
|
|
|
return calendarData;
|
|
}
|
|
|
|
internal String[] ShortDates(int calendarId)
|
|
{
|
|
return GetCalendar(calendarId).saShortDates;
|
|
}
|
|
|
|
internal String[] LongDates(int calendarId)
|
|
{
|
|
return GetCalendar(calendarId).saLongDates;
|
|
}
|
|
|
|
// (user can override) date year/month format.
|
|
internal String[] YearMonths(int calendarId)
|
|
{
|
|
return GetCalendar(calendarId).saYearMonths;
|
|
}
|
|
|
|
// day names
|
|
internal String[] DayNames(int calendarId)
|
|
{
|
|
return GetCalendar(calendarId).saDayNames;
|
|
}
|
|
|
|
// abbreviated day names
|
|
internal String[] AbbreviatedDayNames(int calendarId)
|
|
{
|
|
// Get abbreviated day names for this calendar from the OS if necessary
|
|
return GetCalendar(calendarId).saAbbrevDayNames;
|
|
}
|
|
|
|
// The super short day names
|
|
internal String[] SuperShortDayNames(int calendarId)
|
|
{
|
|
return GetCalendar(calendarId).saSuperShortDayNames;
|
|
}
|
|
|
|
// month names
|
|
internal String[] MonthNames(int calendarId)
|
|
{
|
|
return GetCalendar(calendarId).saMonthNames;
|
|
}
|
|
|
|
// Genitive month names
|
|
internal String[] GenitiveMonthNames(int calendarId)
|
|
{
|
|
return GetCalendar(calendarId).saMonthGenitiveNames;
|
|
}
|
|
|
|
// month names
|
|
internal String[] AbbreviatedMonthNames(int calendarId)
|
|
{
|
|
return GetCalendar(calendarId).saAbbrevMonthNames;
|
|
}
|
|
|
|
// Genitive month names
|
|
internal String[] AbbreviatedGenitiveMonthNames(int calendarId)
|
|
{
|
|
return GetCalendar(calendarId).saAbbrevMonthGenitiveNames;
|
|
}
|
|
|
|
// Leap year month names
|
|
// Note: This only applies to Hebrew, and it basically adds a "1" to the 6th month name
|
|
// the non-leap names skip the 7th name in the normal month name array
|
|
internal String[] LeapYearMonthNames(int calendarId)
|
|
{
|
|
return GetCalendar(calendarId).saLeapYearMonthNames;
|
|
}
|
|
|
|
// month/day format (single string, no override)
|
|
internal String MonthDay(int calendarId)
|
|
{
|
|
return GetCalendar(calendarId).sMonthDay;
|
|
}
|
|
|
|
void DoGetLocaleInfo(uint lctype, String outStr)
|
|
{
|
|
DoGetLocaleInfo(this.sWindowsName, lctype, outStr);
|
|
}
|
|
|
|
void DoGetLocaleInfo(StringView localeName, uint lctype, String outStr)
|
|
{
|
|
var lctype;
|
|
|
|
// Fix lctype if we don't want overrides
|
|
if (!UseUserOverride)
|
|
{
|
|
lctype |= LOCALE_NOUSEROVERRIDE;
|
|
}
|
|
|
|
// Ask OS for data
|
|
/*String result = CultureInfo.nativeGetLocaleInfoEx(localeName, lctype);
|
|
if (result == null)
|
|
{
|
|
// Failed, just use empty string
|
|
result = String.Empty;
|
|
}
|
|
|
|
return result;*/
|
|
}
|
|
|
|
private static void GetSeparator(StringView format, StringView timeParts, String outStr)
|
|
{
|
|
/*int index = IndexOfTimePart(format, 0, timeParts);
|
|
|
|
if (index != -1)
|
|
{
|
|
// Found a time part, find out when it changes
|
|
char8 cTimePart = format[index];
|
|
|
|
do
|
|
{
|
|
index++;
|
|
} while (index < format.Length && format[index] == cTimePart);
|
|
|
|
int separatorStart = index;
|
|
|
|
// Now we need to find the end of the separator
|
|
if (separatorStart < format.Length)
|
|
{
|
|
int separatorEnd = IndexOfTimePart(format, separatorStart, timeParts);
|
|
if (separatorEnd != -1)
|
|
{
|
|
// From [separatorStart, count) is our string, except we need to unescape
|
|
return UnescapeNlsString(format, separatorStart, separatorEnd - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
return String.Empty;*/
|
|
}
|
|
|
|
static private void GetTimeSeparator(StringView format, String outStr)
|
|
{
|
|
// Time format separator (ie: : in 12:39:00)
|
|
//
|
|
// We calculate this from the provided time format
|
|
//
|
|
|
|
//
|
|
// Find the time separator so that we can pretend we know STIME.
|
|
//
|
|
GetSeparator(format, "Hhms", outStr);
|
|
}
|
|
|
|
internal String TimeSeparator
|
|
{
|
|
get
|
|
{
|
|
if (sTimeSeparator == null
|
|
#if !FEATURE_CORECLR
|
|
|| UseUserOverride
|
|
#endif
|
|
)
|
|
{
|
|
String longTimeFormat = scope String();
|
|
DoGetLocaleInfo(LOCALE_STIMEFORMAT, longTimeFormat);
|
|
ReescapeWin32String(longTimeFormat);
|
|
if (String.IsNullOrEmpty(longTimeFormat))
|
|
{
|
|
longTimeFormat = LongTimes[0];
|
|
}
|
|
|
|
// Compute STIME from time format
|
|
sTimeSeparator = new String();
|
|
GetTimeSeparator(longTimeFormat, sTimeSeparator);
|
|
}
|
|
return sTimeSeparator;
|
|
}
|
|
}
|
|
}
|
|
}
|