2 // System.Globalization.CultureInfo.cs
4 // Miguel de Icaza (miguel@ximian.com)
5 // Dick Porter (dick@ximian.com)
7 // (C) 2001, 2002, 2003 Ximian, Inc. (http://www.ximian.com)
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Collections;
34 using System.Threading;
35 using System.Runtime.CompilerServices;
36 using System.Runtime.InteropServices;
38 namespace System.Globalization
40 [System.Runtime.InteropServices.ComVisible (true)]
42 public class CultureInfo : ICloneable, IFormatProvider
44 static volatile CultureInfo invariant_culture_info;
45 static object shared_table_lock = new object ();
46 internal static int BootstrapCultureID;
48 const int NumOptionalCalendars = 5;
49 const int GregorianTypeMask = 0x00FFFFFF;
50 const int CalendarTypeBits = 24;
52 #pragma warning disable 169, 649
63 bool m_useUserOverride;
65 volatile NumberFormatInfo numInfo;
66 volatile DateTimeFormatInfo dateTimeInfo;
67 volatile TextInfo textInfo;
68 private string m_name;
71 private string displayname;
73 private string englishname;
75 private string nativename;
77 private string iso3lang;
79 private string iso2lang;
81 private string icu_name;
83 private string win3lang;
85 private string territory;
86 volatile CompareInfo compareInfo;
88 private unsafe readonly int *calendar_data;
90 private unsafe readonly void *textinfo_data;
92 private Calendar [] optional_calendars;
94 CultureInfo parent_culture;
96 int m_dataItem; // MS.NET serializes this.
97 Calendar calendar; // MS.NET serializes this.
98 #pragma warning restore 169, 649
100 // Deserialized instances will set this to false
105 // Used by Thread.set_CurrentCulture
106 internal byte[] cached_serialized_form;
108 const int InvariantCultureId = 0x7F;
110 private static readonly string MSG_READONLY = "This instance is read only";
112 static public CultureInfo InvariantCulture {
114 return invariant_culture_info;
118 static CultureInfo ()
120 invariant_culture_info = new CultureInfo (InvariantCultureId, false, true);
123 public static CultureInfo CreateSpecificCulture (string name)
126 throw new ArgumentNullException ("name");
129 if (name == String.Empty)
130 return InvariantCulture;
132 CultureInfo ci = new CultureInfo ();
133 if (!ConstructInternalLocaleFromSpecificName (ci, name.ToLowerInvariant ()))
134 throw new ArgumentException ("Culture name " + name +
135 " is not supported.", name);
140 public static CultureInfo CurrentCulture
143 return Thread.CurrentThread.CurrentCulture;
147 public static CultureInfo CurrentUICulture
150 return Thread.CurrentThread.CurrentUICulture;
154 internal static CultureInfo ConstructCurrentCulture ()
156 CultureInfo ci = new CultureInfo ();
157 if (!ConstructInternalLocaleFromCurrentLocale (ci))
158 ci = InvariantCulture;
159 BootstrapCultureID = ci.cultureID;
163 internal static CultureInfo ConstructCurrentUICulture ()
165 return ConstructCurrentCulture ();
168 // it is used for RegionInfo.
169 internal string Territory {
170 get { return territory; }
174 // FIXME: It is implemented, but would be hell slow.
176 public CultureTypes CultureTypes {
178 CultureTypes ret = (CultureTypes) 0;
179 foreach (CultureTypes v in Enum.GetValues (typeof (CultureTypes)))
180 if (Array.IndexOf (GetCultures (v), this) >= 0)
187 public CultureInfo GetConsoleFallbackUICulture ()
189 // as documented in MSDN ...
191 case "ar": case "ar-BH": case "ar-EG": case "ar-IQ":
192 case "ar-JO": case "ar-KW": case "ar-LB": case "ar-LY":
193 case "ar-QA": case "ar-SA": case "ar-SY": case "ar-AE":
195 case "dv": case "dv-MV":
196 case "fa": case "fa-IR":
197 case "gu": case "gu-IN":
198 case "he": case "he-IL":
199 case "hi": case "hi-IN":
200 case "kn": case "kn-IN":
201 case "kok": case "kok-IN":
202 case "mr": case "mr-IN":
203 case "pa": case "pa-IN":
204 case "sa": case "sa-IN":
205 case "syr": case "syr-SY":
206 case "ta": case "ta-IN":
207 case "te": case "te-IN":
208 case "th": case "th-TH":
209 case "ur": case "ur-PK":
210 case "vi": case "vi-VN":
211 return GetCultureInfo ("en");
212 case "ar-DZ": case "ar-MA": case "ar-TN":
213 return GetCultureInfo ("fr");
215 return (CultureTypes & CultureTypes.WindowsOnlyCultures) != 0 ? CultureInfo.InvariantCulture : this;
219 public string IetfLanguageTag {
220 // There could be more consistent way to implement
221 // it, but right now it works just fine with this...
234 // For specific cultures it basically returns LCID.
235 // For neutral cultures it is mapped to the default(?) specific
236 // culture, where the LCID of the specific culture seems to be
237 // n + 1024 by default. zh-CHS is the only exception which is
238 // mapped to 2052, not 1028 (zh-CHT is mapped to 1028 instead).
239 // There are very few exceptions, here I simply list them here.
240 // It is Windows-specific property anyways, so no worthy of
241 // trying to do some complex things with locale-builder.
243 public virtual int KeyboardLayoutId {
246 case 4: // zh-CHS (neutral)
248 case 1034: // es-ES Spanish 2
250 case 31748: // zh-CHT (neutral)
252 case 31770: // sr (neutral)
255 return LCID < 1024 ? LCID + 1024 : LCID;
261 public virtual int LCID {
267 public virtual string Name {
270 if (m_name == "zh-CHS")
272 if (m_name == "zh-CHT")
279 public virtual string NativeName
282 if (!constructed) Construct ();
287 public virtual Calendar Calendar
289 get { return DateTimeFormat.Calendar; }
292 public virtual Calendar[] OptionalCalendars
295 if (optional_calendars == null) {
297 if (optional_calendars == null)
298 ConstructCalendars ();
301 return optional_calendars;
305 public virtual CultureInfo Parent
308 if (parent_culture == null) {
311 if (parent_lcid == cultureID)
314 if (parent_lcid == InvariantCultureId)
315 parent_culture = InvariantCulture;
316 else if (cultureID == InvariantCultureId)
317 parent_culture = this;
319 parent_culture = new CultureInfo (parent_lcid);
321 return parent_culture;
325 public virtual TextInfo TextInfo
328 if (textInfo == null) {
329 if (!constructed) Construct ();
331 if(textInfo == null) {
332 textInfo = CreateTextInfo (m_isReadOnly);
341 public virtual string ThreeLetterISOLanguageName
344 if (!constructed) Construct ();
349 public virtual string ThreeLetterWindowsLanguageName
352 if (!constructed) Construct ();
357 public virtual string TwoLetterISOLanguageName
360 if (!constructed) Construct ();
365 public bool UseUserOverride
368 return m_useUserOverride;
372 internal string IcuName {
374 if (!constructed) Construct ();
379 public void ClearCachedData()
381 Thread.CurrentThread.CurrentCulture = null;
382 Thread.CurrentThread.CurrentUICulture = null;
385 public virtual object Clone()
387 if (!constructed) Construct ();
388 CultureInfo ci=(CultureInfo)MemberwiseClone ();
389 ci.m_isReadOnly=false;
390 ci.cached_serialized_form=null;
391 if (!IsNeutralCulture) {
392 ci.NumberFormat = (NumberFormatInfo)NumberFormat.Clone ();
393 ci.DateTimeFormat = (DateTimeFormatInfo)DateTimeFormat.Clone ();
398 public override bool Equals (object value)
400 CultureInfo b = value as CultureInfo;
403 return b.cultureID == cultureID;
408 public static CultureInfo[] GetCultures(CultureTypes types)
410 bool neutral=((types & CultureTypes.NeutralCultures)!=0);
411 bool specific=((types & CultureTypes.SpecificCultures)!=0);
412 bool installed=((types & CultureTypes.InstalledWin32Cultures)!=0); // TODO
414 CultureInfo [] infos = internal_get_cultures (neutral, specific, installed);
415 // The runtime returns a NULL in the first position of the array when
416 // 'neutral' is true. We fill it in with a clone of InvariantCulture
417 // since it must not be read-only
418 if (neutral && infos.Length > 0 && infos [0] == null) {
419 infos [0] = (CultureInfo) InvariantCulture.Clone ();
426 public override int GetHashCode()
431 public static CultureInfo ReadOnly(CultureInfo ci)
434 throw new ArgumentNullException("ci");
437 if(ci.m_isReadOnly) {
440 CultureInfo new_ci=(CultureInfo)ci.Clone ();
441 new_ci.m_isReadOnly=true;
442 if (new_ci.numInfo != null)
443 new_ci.numInfo = NumberFormatInfo.ReadOnly (new_ci.numInfo);
444 if (new_ci.dateTimeInfo != null)
445 new_ci.dateTimeInfo = DateTimeFormatInfo.ReadOnly (new_ci.dateTimeInfo);
446 // TextInfo doesn't have a ReadOnly method in 1.1...
447 if (new_ci.textInfo != null)
448 new_ci.textInfo = TextInfo.ReadOnly (new_ci.textInfo);
453 public override string ToString()
458 public virtual CompareInfo CompareInfo
461 if(compareInfo==null) {
466 if(compareInfo==null) {
467 compareInfo=new CompareInfo (this);
476 internal static bool IsIDNeutralCulture (int lcid)
479 if (!internal_is_lcid_neutral (lcid, out ret))
480 throw new ArgumentException (String.Format ("Culture id 0x{:x4} is not supported.", lcid));
485 public virtual bool IsNeutralCulture {
487 if (!constructed) Construct ();
488 if (cultureID == InvariantCultureId)
491 return ((cultureID & 0xff00) == 0 || specific_lcid == 0);
495 internal void CheckNeutral ()
498 if (IsNeutralCulture) {
499 throw new NotSupportedException ("Culture \"" + m_name + "\" is " +
500 "a neutral culture. It can not be used in formatting " +
501 "and parsing and therefore cannot be set as the thread's " +
507 public virtual NumberFormatInfo NumberFormat {
509 if (!constructed) Construct ();
511 if (numInfo == null){
513 if (numInfo == null) {
514 numInfo = new NumberFormatInfo (m_isReadOnly);
515 construct_number_format ();
524 if (!constructed) Construct ();
525 if (m_isReadOnly) throw new InvalidOperationException(MSG_READONLY);
528 throw new ArgumentNullException ("NumberFormat");
534 public virtual DateTimeFormatInfo DateTimeFormat
538 if (!constructed) Construct ();
540 if (dateTimeInfo == null)
544 if (dateTimeInfo == null) {
545 dateTimeInfo = new DateTimeFormatInfo(m_isReadOnly);
546 construct_datetime_format ();
547 if (optional_calendars != null)
548 dateTimeInfo.Calendar = optional_calendars [0];
558 if (!constructed) Construct ();
559 if (m_isReadOnly) throw new InvalidOperationException(MSG_READONLY);
562 throw new ArgumentNullException ("DateTimeFormat");
564 dateTimeInfo = value;
568 public virtual string DisplayName
571 if (!constructed) Construct ();
576 public virtual string EnglishName
579 if (!constructed) Construct ();
584 public static CultureInfo InstalledUICulture
586 get { return GetCultureInfo (BootstrapCultureID); }
588 public bool IsReadOnly
591 return(m_isReadOnly);
597 // IFormatProvider implementation
599 public virtual object GetFormat( Type formatType )
601 object format = null;
603 if ( formatType == typeof(NumberFormatInfo) )
604 format = NumberFormat;
605 else if ( formatType == typeof(DateTimeFormatInfo) )
606 format = DateTimeFormat;
613 construct_internal_locale_from_lcid (cultureID);
617 bool ConstructInternalLocaleFromName (string locale)
619 // It is sort of hack to get those new pseudo-alias
620 // culture names that are not supported in good old
623 if (locale == "zh-chs" || locale == "zh-cht")
635 if (!construct_internal_locale_from_name (locale))
640 bool ConstructInternalLocaleFromLcid (int lcid)
642 if (!construct_internal_locale_from_lcid (lcid))
647 static bool ConstructInternalLocaleFromSpecificName (CultureInfo ci, string name)
649 if (!construct_internal_locale_from_specific_name (ci, name))
654 static bool ConstructInternalLocaleFromCurrentLocale (CultureInfo ci)
656 if (!construct_internal_locale_from_current_locale (ci))
661 [MethodImplAttribute (MethodImplOptions.InternalCall)]
662 private extern bool construct_internal_locale_from_lcid (int lcid);
664 [MethodImplAttribute (MethodImplOptions.InternalCall)]
665 private extern bool construct_internal_locale_from_name (string name);
667 [MethodImplAttribute (MethodImplOptions.InternalCall)]
668 private extern static bool construct_internal_locale_from_specific_name (CultureInfo ci,
671 [MethodImplAttribute (MethodImplOptions.InternalCall)]
672 private extern static bool construct_internal_locale_from_current_locale (CultureInfo ci);
674 [MethodImplAttribute (MethodImplOptions.InternalCall)]
675 private extern static CultureInfo [] internal_get_cultures (bool neutral, bool specific, bool installed);
677 [MethodImplAttribute (MethodImplOptions.InternalCall)]
678 private extern void construct_datetime_format ();
680 [MethodImplAttribute (MethodImplOptions.InternalCall)]
681 private extern void construct_number_format ();
683 // Returns false if the culture can not be found, sets is_neutral if it is
684 [MethodImplAttribute (MethodImplOptions.InternalCall)]
685 private extern static bool internal_is_lcid_neutral (int lcid, out bool is_neutral);
687 private void ConstructInvariant (bool read_only)
689 cultureID = InvariantCultureId;
691 /* NumberFormatInfo defaults to the invariant data */
692 numInfo=NumberFormatInfo.InvariantInfo;
693 /* DateTimeFormatInfo defaults to the invariant data */
694 dateTimeInfo=DateTimeFormatInfo.InvariantInfo;
697 numInfo = (NumberFormatInfo) numInfo.Clone ();
698 dateTimeInfo = (DateTimeFormatInfo) dateTimeInfo.Clone ();
701 textInfo = CreateTextInfo (read_only);
706 nativename="Invariant Language (Invariant Country)";
709 icu_name="en_US_POSIX";
713 private unsafe TextInfo CreateTextInfo (bool readOnly)
715 return new TextInfo (this, cultureID, this.textinfo_data, readOnly);
718 public CultureInfo (int culture) : this (culture, true) {}
720 public CultureInfo (int culture, bool useUserOverride) :
721 this (culture, useUserOverride, false) {}
723 private CultureInfo (int culture, bool useUserOverride, bool read_only)
726 throw new ArgumentOutOfRangeException ("culture", "Positive "
727 + "number required.");
730 m_isReadOnly = read_only;
731 m_useUserOverride = useUserOverride;
733 if (culture == InvariantCultureId) {
734 /* Short circuit the invariant culture */
735 ConstructInvariant (read_only);
739 if (!ConstructInternalLocaleFromLcid (culture))
740 throw new ArgumentException (
741 String.Format ("Culture ID {0} (0x{0:X4}) is not a " +
742 "supported culture.", culture), "culture");
745 public CultureInfo (string name) : this (name, true) {}
747 public CultureInfo (string name, bool useUserOverride) :
748 this (name, useUserOverride, false) {}
750 private CultureInfo (string name, bool useUserOverride, bool read_only)
753 throw new ArgumentNullException ("name");
756 m_isReadOnly = read_only;
757 m_useUserOverride = useUserOverride;
759 if (name.Length == 0) {
760 /* Short circuit the invariant culture */
761 ConstructInvariant (read_only);
765 if (!ConstructInternalLocaleFromName (name.ToLowerInvariant ()))
766 throw new ArgumentException ("Culture name " + name +
767 " is not supported.", "name");
770 // This is used when creating by specific name and creating by
771 // current locale so we can initialize the object without
772 // doing any member initialization
773 private CultureInfo () { constructed = true; }
774 static Hashtable shared_by_number, shared_by_name;
776 static void insert_into_shared_tables (CultureInfo c)
778 if (shared_by_number == null){
779 shared_by_number = new Hashtable ();
780 shared_by_name = new Hashtable ();
782 shared_by_number [c.cultureID] = c;
783 shared_by_name [c.m_name] = c;
786 public static CultureInfo GetCultureInfo (int culture)
790 lock (shared_table_lock){
791 if (shared_by_number != null){
792 c = shared_by_number [culture] as CultureInfo;
795 return (CultureInfo) c;
797 c = new CultureInfo (culture, false, true);
798 insert_into_shared_tables (c);
803 public static CultureInfo GetCultureInfo (string name)
806 throw new ArgumentNullException ("name");
809 lock (shared_table_lock){
810 if (shared_by_name != null){
811 c = shared_by_name [name] as CultureInfo;
814 return (CultureInfo) c;
816 c = new CultureInfo (name, false, true);
817 insert_into_shared_tables (c);
822 [MonoTODO ("Currently it ignores the altName parameter")]
823 public static CultureInfo GetCultureInfo (string name, string altName) {
825 throw new ArgumentNullException ("null");
827 throw new ArgumentNullException ("null");
829 return GetCultureInfo (name);
832 public static CultureInfo GetCultureInfoByIetfLanguageTag (string name)
834 // There could be more consistent way to implement
835 // it, but right now it works just fine with this...
838 return GetCultureInfo ("zh-CHS");
840 return GetCultureInfo ("zh-CHT");
842 return GetCultureInfo (name);
846 // used in runtime (icall.c) to construct CultureInfo for
847 // AssemblyName of assemblies
848 internal static CultureInfo CreateCulture (string name, bool reference)
851 bool use_user_override;
853 bool invariant = name.Length == 0;
855 use_user_override = invariant ? false : true;
859 use_user_override = invariant ? false : true;
862 return new CultureInfo (name, use_user_override, read_only);
865 unsafe internal void ConstructCalendars ()
867 if (calendar_data == null) {
868 optional_calendars = new Calendar [] {new GregorianCalendar (GregorianCalendarTypes.Localized)};
872 optional_calendars = new Calendar [NumOptionalCalendars];
874 for (int i=0; i<NumOptionalCalendars; i++) {
876 int caldata = *(calendar_data + i);
877 int caltype = (caldata >> CalendarTypeBits);
880 GregorianCalendarTypes greg_type;
881 greg_type = (GregorianCalendarTypes) (caldata & GregorianTypeMask);
882 cal = new GregorianCalendar (greg_type);
885 cal = new HijriCalendar ();
888 cal = new ThaiBuddhistCalendar ();
891 throw new Exception ("invalid calendar type: " + caldata);
893 optional_calendars [i] = cal;