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 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; } } } }