2 // System.Globalization.CultureInfo.cs
5 // Miguel de Icaza (miguel@ximian.com)
6 // Dick Porter (dick@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
9 // (C) 2001, 2002, 2003 Ximian, Inc. (http://www.ximian.com)
10 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
11 // Copyright (C) 2012 Xamarin Inc (http://www.xamarin.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 [StructLayout (LayoutKind.Sequential)]
43 public class CultureInfo : ICloneable, IFormatProvider
45 static volatile CultureInfo invariant_culture_info = new CultureInfo (InvariantCultureId, false, true);
46 static object shared_table_lock = new object ();
47 internal static int BootstrapCultureID;
49 #pragma warning disable 169, 649
59 int default_calendar_type;
60 bool m_useUserOverride;
62 volatile NumberFormatInfo numInfo;
63 volatile DateTimeFormatInfo dateTimeInfo;
64 volatile TextInfo textInfo;
65 private string m_name;
68 private string englishname;
70 private string nativename;
72 private string iso3lang;
74 private string iso2lang;
76 private string win3lang;
78 private string territory;
80 string[] native_calendar_names;
82 volatile CompareInfo compareInfo;
84 private unsafe readonly void *textinfo_data;
86 int m_dataItem; // MS.NET serializes this.
87 #pragma warning restore 169, 649
92 CultureInfo parent_culture;
94 // Deserialized instances will set this to false
99 // Used by Thread.set_CurrentCulture
100 internal byte[] cached_serialized_form;
102 const int InvariantCultureId = 0x7F;
103 const int CalendarTypeBits = 8;
105 const string MSG_READONLY = "This instance is read only";
107 public static CultureInfo InvariantCulture {
109 return invariant_culture_info;
113 public static CultureInfo CreateSpecificCulture (string name)
116 throw new ArgumentNullException ("name");
119 if (name == String.Empty)
120 return InvariantCulture;
122 CultureInfo ci = new CultureInfo ();
123 if (!construct_internal_locale_from_specific_name (ci, name.ToLowerInvariant ()))
124 throw new ArgumentException ("Culture name " + name +
125 " is not supported.", name);
130 public static CultureInfo CurrentCulture
133 return Thread.CurrentThread.CurrentCulture;
137 public static CultureInfo CurrentUICulture
140 return Thread.CurrentThread.CurrentUICulture;
144 internal static CultureInfo ConstructCurrentCulture ()
146 CultureInfo ci = new CultureInfo ();
147 if (!ConstructInternalLocaleFromCurrentLocale (ci))
148 ci = InvariantCulture;
149 BootstrapCultureID = ci.cultureID;
153 internal static CultureInfo ConstructCurrentUICulture ()
155 return ConstructCurrentCulture ();
158 // it is used for RegionInfo.
159 internal string Territory {
160 get { return territory; }
164 // FIXME: It is implemented, but would be hell slow.
166 public CultureTypes CultureTypes {
168 CultureTypes ret = (CultureTypes) 0;
169 foreach (CultureTypes v in Enum.GetValues (typeof (CultureTypes)))
170 if (Array.IndexOf (GetCultures (v), this) >= 0)
177 public CultureInfo GetConsoleFallbackUICulture ()
179 // as documented in MSDN ...
181 case "ar": case "ar-BH": case "ar-EG": case "ar-IQ":
182 case "ar-JO": case "ar-KW": case "ar-LB": case "ar-LY":
183 case "ar-QA": case "ar-SA": case "ar-SY": case "ar-AE":
185 case "dv": case "dv-MV":
186 case "fa": case "fa-IR":
187 case "gu": case "gu-IN":
188 case "he": case "he-IL":
189 case "hi": case "hi-IN":
190 case "kn": case "kn-IN":
191 case "kok": case "kok-IN":
192 case "mr": case "mr-IN":
193 case "pa": case "pa-IN":
194 case "sa": case "sa-IN":
195 case "syr": case "syr-SY":
196 case "ta": case "ta-IN":
197 case "te": case "te-IN":
198 case "th": case "th-TH":
199 case "ur": case "ur-PK":
200 case "vi": case "vi-VN":
201 return GetCultureInfo ("en");
202 case "ar-DZ": case "ar-MA": case "ar-TN":
203 return GetCultureInfo ("fr");
205 return (CultureTypes & CultureTypes.WindowsOnlyCultures) != 0 ? CultureInfo.InvariantCulture : this;
209 public string IetfLanguageTag {
210 // There could be more consistent way to implement
211 // it, but right now it works just fine with this...
224 // For specific cultures it basically returns LCID.
225 // For neutral cultures it is mapped to the default(?) specific
226 // culture, where the LCID of the specific culture seems to be
227 // n + 1024 by default. zh-CHS is the only exception which is
228 // mapped to 2052, not 1028 (zh-CHT is mapped to 1028 instead).
229 // There are very few exceptions, here I simply list them here.
230 // It is Windows-specific property anyways, so no worthy of
231 // trying to do some complex things with locale-builder.
233 public virtual int KeyboardLayoutId {
236 case 4: // zh-CHS (neutral)
238 case 1034: // es-ES Spanish 2
240 case 31748: // zh-CHT (neutral)
242 case 31770: // sr (neutral)
245 return LCID < 1024 ? LCID + 1024 : LCID;
251 public virtual int LCID {
257 public virtual string Name {
260 if (m_name == "zh-CHS")
262 if (m_name == "zh-CHT")
269 public virtual string NativeName {
271 if (!constructed) Construct ();
276 internal string NativeCalendarName {
278 if (!constructed) Construct ();
279 return native_calendar_names[(default_calendar_type >> CalendarTypeBits) - 1];
283 public virtual Calendar Calendar {
285 if (calendar == null) {
286 if (!constructed) Construct ();
287 calendar = CreateCalendar (default_calendar_type);
294 [MonoLimitation ("Optional calendars are not supported only default calendar is returned")]
295 public virtual Calendar[] OptionalCalendars {
297 return new[] { Calendar };
301 public virtual CultureInfo Parent
304 if (parent_culture == null) {
307 if (parent_lcid == cultureID)
310 if (parent_lcid == InvariantCultureId)
311 parent_culture = InvariantCulture;
312 else if (cultureID == InvariantCultureId)
313 parent_culture = this;
315 parent_culture = new CultureInfo (parent_lcid);
317 return parent_culture;
321 public virtual TextInfo TextInfo
324 if (textInfo == null) {
325 if (!constructed) Construct ();
327 if(textInfo == null) {
328 textInfo = CreateTextInfo (m_isReadOnly);
337 public virtual string ThreeLetterISOLanguageName {
339 if (!constructed) Construct ();
344 public virtual string ThreeLetterWindowsLanguageName
347 if (!constructed) Construct ();
352 public virtual string TwoLetterISOLanguageName {
354 if (!constructed) Construct ();
359 public bool UseUserOverride
362 return m_useUserOverride;
366 public void ClearCachedData()
368 Thread.CurrentThread.CurrentCulture = null;
369 Thread.CurrentThread.CurrentUICulture = null;
372 public virtual object Clone()
374 if (!constructed) Construct ();
375 CultureInfo ci=(CultureInfo)MemberwiseClone ();
376 ci.m_isReadOnly=false;
377 ci.cached_serialized_form=null;
378 if (!IsNeutralCulture) {
379 ci.NumberFormat = (NumberFormatInfo)NumberFormat.Clone ();
380 ci.DateTimeFormat = (DateTimeFormatInfo)DateTimeFormat.Clone ();
385 public override bool Equals (object value)
387 CultureInfo b = value as CultureInfo;
390 return b.cultureID == cultureID;
395 public static CultureInfo[] GetCultures(CultureTypes types)
397 bool neutral=((types & CultureTypes.NeutralCultures)!=0);
398 bool specific=((types & CultureTypes.SpecificCultures)!=0);
399 bool installed=((types & CultureTypes.InstalledWin32Cultures)!=0); // TODO
401 CultureInfo [] infos = internal_get_cultures (neutral, specific, installed);
402 // The runtime returns a NULL in the first position of the array when
403 // 'neutral' is true. We fill it in with a clone of InvariantCulture
404 // since it must not be read-only
405 if (neutral && infos.Length > 0 && infos [0] == null) {
406 infos [0] = (CultureInfo) InvariantCulture.Clone ();
413 public override int GetHashCode ()
415 return cultureID.GetHashCode ();
418 public static CultureInfo ReadOnly(CultureInfo ci)
421 throw new ArgumentNullException("ci");
424 if(ci.m_isReadOnly) {
427 CultureInfo new_ci=(CultureInfo)ci.Clone ();
428 new_ci.m_isReadOnly=true;
429 if (new_ci.numInfo != null)
430 new_ci.numInfo = NumberFormatInfo.ReadOnly (new_ci.numInfo);
431 if (new_ci.dateTimeInfo != null)
432 new_ci.dateTimeInfo = DateTimeFormatInfo.ReadOnly (new_ci.dateTimeInfo);
433 if (new_ci.textInfo != null)
434 new_ci.textInfo = TextInfo.ReadOnly (new_ci.textInfo);
439 public override string ToString()
444 public virtual CompareInfo CompareInfo
447 if(compareInfo==null) {
452 if(compareInfo==null) {
453 compareInfo=new CompareInfo (this);
462 public virtual bool IsNeutralCulture {
464 if (cultureID == InvariantCultureId)
467 if (!constructed) Construct ();
468 return territory == null;
472 internal void CheckNeutral ()
474 #if !MOONLIGHT && !NET_4_0
475 if (IsNeutralCulture) {
476 throw new NotSupportedException ("Culture \"" + m_name + "\" is " +
477 "a neutral culture. It can not be used in formatting " +
478 "and parsing and therefore cannot be set as the thread's " +
484 public virtual NumberFormatInfo NumberFormat {
486 if (!constructed) Construct ();
488 if (numInfo == null){
490 if (numInfo == null) {
491 numInfo = new NumberFormatInfo (m_isReadOnly);
492 construct_number_format ();
501 if (!constructed) Construct ();
502 if (m_isReadOnly) throw new InvalidOperationException(MSG_READONLY);
505 throw new ArgumentNullException ("NumberFormat");
511 public virtual DateTimeFormatInfo DateTimeFormat {
513 if (dateTimeInfo != null)
516 if (!constructed) Construct ();
519 // TODO: Have to lock because construct_datetime_format is not atomic
521 if (cultureID == InvariantCultureId && m_isReadOnly)
522 dateTimeInfo = DateTimeFormatInfo.InvariantInfo;
523 else if (dateTimeInfo == null) {
524 dateTimeInfo = new DateTimeFormatInfo (this, m_isReadOnly);
525 if (cultureID != InvariantCultureId)
526 construct_datetime_format ();
534 if (!constructed) Construct ();
535 if (m_isReadOnly) throw new InvalidOperationException(MSG_READONLY);
538 throw new ArgumentNullException ("DateTimeFormat");
540 dateTimeInfo = value;
544 public virtual string DisplayName {
546 // Mono is not localized and will always return english name regardless of OS locale
551 public virtual string EnglishName {
553 if (!constructed) Construct ();
558 public static CultureInfo InstalledUICulture
560 get { return GetCultureInfo (BootstrapCultureID); }
563 public bool IsReadOnly {
571 // IFormatProvider implementation
573 public virtual object GetFormat( Type formatType )
575 object format = null;
577 if ( formatType == typeof(NumberFormatInfo) )
578 format = NumberFormat;
579 else if ( formatType == typeof(DateTimeFormatInfo) )
580 format = DateTimeFormat;
587 construct_internal_locale_from_lcid (cultureID);
591 static bool ConstructInternalLocaleFromCurrentLocale (CultureInfo ci)
593 if (!construct_internal_locale_from_current_locale (ci))
598 [MethodImplAttribute (MethodImplOptions.InternalCall)]
599 private extern bool construct_internal_locale_from_lcid (int lcid);
601 [MethodImplAttribute (MethodImplOptions.InternalCall)]
602 private extern bool construct_internal_locale_from_name (string name);
604 [MethodImplAttribute (MethodImplOptions.InternalCall)]
605 private extern static bool construct_internal_locale_from_specific_name (CultureInfo ci,
608 [MethodImplAttribute (MethodImplOptions.InternalCall)]
609 private extern static bool construct_internal_locale_from_current_locale (CultureInfo ci);
611 [MethodImplAttribute (MethodImplOptions.InternalCall)]
612 private extern static CultureInfo [] internal_get_cultures (bool neutral, bool specific, bool installed);
614 [MethodImplAttribute (MethodImplOptions.InternalCall)]
615 private extern void construct_datetime_format ();
617 [MethodImplAttribute (MethodImplOptions.InternalCall)]
618 private extern void construct_number_format ();
620 private void ConstructInvariant (bool read_only)
622 cultureID = InvariantCultureId;
624 /* NumberFormatInfo defaults to the invariant data */
625 numInfo=NumberFormatInfo.InvariantInfo;
628 numInfo = (NumberFormatInfo) numInfo.Clone ();
631 textInfo = CreateTextInfo (read_only);
635 nativename="Invariant Language (Invariant Country)";
639 default_calendar_type = 1 << CalendarTypeBits;
642 private unsafe TextInfo CreateTextInfo (bool readOnly)
644 return new TextInfo (this, cultureID, this.textinfo_data, readOnly);
647 public CultureInfo (int culture) : this (culture, true) {}
649 public CultureInfo (int culture, bool useUserOverride) :
650 this (culture, useUserOverride, false) {}
652 private CultureInfo (int culture, bool useUserOverride, bool read_only)
655 throw new ArgumentOutOfRangeException ("culture", "Positive "
656 + "number required.");
659 m_isReadOnly = read_only;
660 m_useUserOverride = useUserOverride;
662 if (culture == InvariantCultureId) {
663 /* Short circuit the invariant culture */
664 ConstructInvariant (read_only);
668 if (!construct_internal_locale_from_lcid (culture)) {
670 throw new CultureNotFoundException ("culture",
671 String.Format ("Culture ID {0} (0x{0:X4}) is not a " +
672 "supported culture.", culture));
674 throw new ArgumentException (
675 String.Format ("Culture ID {0} (0x{0:X4}) is not a " +
676 "supported culture.", culture), "culture");
681 public CultureInfo (string name) : this (name, true) {}
683 public CultureInfo (string name, bool useUserOverride) :
684 this (name, useUserOverride, false) {}
686 private CultureInfo (string name, bool useUserOverride, bool read_only)
689 throw new ArgumentNullException ("name");
692 m_isReadOnly = read_only;
693 m_useUserOverride = useUserOverride;
695 if (name.Length == 0) {
696 /* Short circuit the invariant culture */
697 ConstructInvariant (read_only);
701 if (!construct_internal_locale_from_name (name.ToLowerInvariant ())) {
703 throw new CultureNotFoundException ("name",
704 "Culture name " + name + " is not supported.");
706 throw new ArgumentException ("Culture name " + name +
707 " is not supported.", "name");
712 // This is used when creating by specific name and creating by
713 // current locale so we can initialize the object without
714 // doing any member initialization
715 private CultureInfo () { constructed = true; }
716 static Hashtable shared_by_number, shared_by_name;
718 static void insert_into_shared_tables (CultureInfo c)
720 if (shared_by_number == null){
721 shared_by_number = new Hashtable ();
722 shared_by_name = new Hashtable ();
724 shared_by_number [c.cultureID] = c;
725 shared_by_name [c.m_name] = c;
728 public static CultureInfo GetCultureInfo (int culture)
732 lock (shared_table_lock){
733 if (shared_by_number != null){
734 c = shared_by_number [culture] as CultureInfo;
737 return (CultureInfo) c;
739 c = new CultureInfo (culture, false, true);
740 insert_into_shared_tables (c);
745 public static CultureInfo GetCultureInfo (string name)
748 throw new ArgumentNullException ("name");
751 lock (shared_table_lock){
752 if (shared_by_name != null){
753 c = shared_by_name [name] as CultureInfo;
756 return (CultureInfo) c;
758 c = new CultureInfo (name, false, true);
759 insert_into_shared_tables (c);
764 [MonoTODO ("Currently it ignores the altName parameter")]
765 public static CultureInfo GetCultureInfo (string name, string altName) {
767 throw new ArgumentNullException ("null");
769 throw new ArgumentNullException ("null");
771 return GetCultureInfo (name);
774 public static CultureInfo GetCultureInfoByIetfLanguageTag (string name)
776 // There could be more consistent way to implement
777 // it, but right now it works just fine with this...
780 return GetCultureInfo ("zh-CHS");
782 return GetCultureInfo ("zh-CHT");
784 return GetCultureInfo (name);
788 // used in runtime (icall.c) to construct CultureInfo for
789 // AssemblyName of assemblies
790 internal static CultureInfo CreateCulture (string name, bool reference)
793 bool use_user_override;
795 bool invariant = name.Length == 0;
797 use_user_override = invariant ? false : true;
801 use_user_override = invariant ? false : true;
804 return new CultureInfo (name, use_user_override, read_only);
807 static Calendar CreateCalendar (int calendarType)
809 switch (calendarType >> CalendarTypeBits) {
811 GregorianCalendarTypes greg_type;
812 greg_type = (GregorianCalendarTypes) (calendarType & 0xFF);
813 return new GregorianCalendar (greg_type);
815 return new ThaiBuddhistCalendar ();
817 return new UmAlQuraCalendar ();
819 return new HijriCalendar ();
821 throw new NotImplementedException ("Unknown calendar type: " + calendarType);