2009-01-22 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / tools / locale-builder / Driver.cs
index 9e1ad27bcb5a9d9fcd5a96101700d5ac2e3e30be..52586043f241713ffeb53948ee225b67cc290485 100644 (file)
@@ -3,8 +3,9 @@
 //
 // Author(s):
 //  Jackson Harper (jackson@ximian.com)
+//  Atsushi Enomoto (atsushi@ximian.com)
 //
-// (C) 2004 Novell, Inc (http://www.novell.com)
+// (C) 2004-2005 Novell, Inc (http://www.novell.com)
 //
 
 
@@ -46,7 +47,10 @@ namespace Mono.Tools.LocaleBuilder {
                 private ArrayList cultures;
                 private Hashtable langs;
                 private Hashtable currency_types;
-                
+                private Hashtable regions;
+
+               private XPathDocument lcids_doc;
+
                // The lang is the language that display names will be displayed in
                public string Lang {
                        get {
@@ -73,35 +77,96 @@ namespace Mono.Tools.LocaleBuilder {
 
                public void Run ()
                {
+                       lcids_doc = GetXPathDocument ("lcids.xml");
+
                        Regex locales_regex = null;
                        if (Locales != null)
                                locales_regex = new Regex (Locales);
 
                         langs = new Hashtable ();
                         cultures = new ArrayList ();
+                        regions = new Hashtable ();
+
+                       LookupRegions ();
 
                         LookupCurrencyTypes ();
 
                        foreach (string file in Directory.GetFiles ("locales", "*.xml")) {
                                string fn = Path.GetFileNameWithoutExtension (file);
+                               if (fn == "hy_AM")
+                                       continue; // see bug #75499
                                if (locales_regex == null || locales_regex.IsMatch (fn)) {
                                        ParseLocale (fn);
                                 }
                        }
 
+                       /* FIXME: This is hacky.
+                        * Since there is only langs/zh.xml while there are
+                        * two "zh" languages (CHS and CHT), there should be
+                        * different language profiles and we are not likely
+                        * to add lang/* files. So here I just clone zh-CHS
+                        * as zh-CHT
+                        */
+                        foreach (CultureInfoEntry e in cultures) {
+                               if (e.Name == "zh-CHS") {
+                                       CultureInfoEntry t =
+                                               CultureInfoEntry.ShallowCopy (e);
+                                       t.Language = "zh-CHT";
+                                       LookupLcids (t, true);
+                                       cultures.Add (t);
+                                       break;
+                               }
+                        }
+
+                       ArrayList regionList = new ArrayList (regions.Values);
+                       regionList.Sort (RegionComparer.Instance);
+                       int number = 0;
+                       foreach (RegionInfoEntry r in regionList)
+                               r.RegionId = number++;
+
+                       foreach (CultureInfoEntry e in cultures) {
+                               int lcid = int.Parse (e.Lcid.Substring (2),
+                                       NumberStyles.HexNumber);
+                               int idx;
+                               int start = e.Name.IndexOf ('-') + 1;
+                               if (start == 0)
+                                       continue;
+                               for (idx = start; idx < e.Name.Length; idx++)
+                                       if (!Char.IsLetter (e.Name [idx]))
+                                               break;
+                               if (start == idx) {
+                                       Console.Error.WriteLine ("Culture {0} {1} is not mappable to Region.", e.Lcid, e.Name);
+                                       continue;
+                               }
+                               string name = e.Name.Substring (start, idx - start);
+                               RegionInfoEntry rm = null;
+                               foreach (RegionInfoEntry r in regions.Values)
+                                       if (r.ISO2Name == name) {
+                                               rm = r;
+                                               break;
+                                       }
+                               if (rm == null) {
+                                       Console.Error.WriteLine ("No definition for region {0}", name);
+                                       continue;
+                               }
+                               e.RegionId = rm.RegionId;
+                       }
+
                         /**
                          * Dump each table individually. Using StringBuilders
                          * because it is easier to debug, should switch to just
                          * writing to streams eventually.
                          */
                         using (StreamWriter writer = new StreamWriter (HeaderFileName, false, new UTF8Encoding (false, true))) {
-
+                                writer.NewLine = "\n";
                                 writer.WriteLine ();
+                                writer.WriteLine ("/* This is a generated file. Do not edit. See tools/locale-builder. */");
                                 writer.WriteLine ("#ifndef MONO_METADATA_CULTURE_INFO_TABLES");
                                 writer.WriteLine ("#define MONO_METADATA_CULTURE_INFO_TABLES 1");
                                 writer.WriteLine ("\n");
 
                                 writer.WriteLine ("#define NUM_CULTURE_ENTRIES " + cultures.Count);
+                                writer.WriteLine ("#define NUM_REGION_ENTRIES " + regionList.Count);
                                 writer.WriteLine ("\n");
 
                                 // Sort the cultures by lcid
@@ -161,7 +226,7 @@ namespace Mono.Tools.LocaleBuilder {
                                 builder = new StringBuilder ();
                                 for (int i = 0; i < count; i++) {
                                         CultureInfoEntry ci = (CultureInfoEntry) cultures [i];
-                                        builder.Append ("\t{\"" + ci.Name.ToLower () + "\", ");
+                                        builder.Append ("\t{" + Entry.EncodeStringIdx (ci.Name.ToLower ()) + ", ");
                                         builder.Append (ci.Row + "}");
                                         if (i + 1 < count)
                                                 builder.Append (',');
@@ -172,13 +237,61 @@ namespace Mono.Tools.LocaleBuilder {
                                 writer.Write (builder);
                                 writer.WriteLine ("};\n\n");
 
+                               builder = new StringBuilder ();
+                               int rcount = 0;
+                               foreach (RegionInfoEntry r in regionList) {
+                                       r.AppendTableRow (builder);
+                                       if (++rcount != regionList.Count)
+                                               builder.Append (',');
+                                       builder.Append ('\n');
+                               }
+                               writer.WriteLine ("static const RegionInfoEntry region_entries [] = {");
+                               writer.Write (builder);
+                               writer.WriteLine ("};\n\n");
+
+                                builder = new StringBuilder ();
+                               rcount = 0;
+                               foreach (RegionInfoEntry ri in regionList) {
+                                        builder.Append ("\t{" + Entry.EncodeStringIdx (ri.ISO2Name) + ", ");
+                                        builder.Append (ri.RegionId + "}");
+                                        if (++rcount < regionList.Count)
+                                                builder.Append (',');
+                                        builder.Append ('\n');
+                                }
+
+                                writer.WriteLine ("static const RegionInfoNameEntry region_name_entries [] = {");
+                                writer.Write (builder);
+                                writer.WriteLine ("};\n\n");
+
+                                writer.WriteLine ("static const char locale_strings [] = {");
+                                writer.Write (Entry.GetStrings ());
+                                writer.WriteLine ("};\n\n");
+
                                 writer.WriteLine ("#endif\n");
                         }
                }
 
+               private XPathDocument GetXPathDocument (string path)
+               {
+                       XmlTextReader xtr = null;
+                       try {
+                               xtr = new XmlTextReader (path);
+                               xtr.XmlResolver = null;
+                               return new XPathDocument (xtr);
+                       } finally {
+                               if (xtr != null)
+                                       xtr.Close ();
+                       }
+               }
+
+               private string GetShortName (string lang)
+               {
+                       return lang == "zh-CHS" ? "zh" : lang;
+               }
+
                 private bool ParseLang (string lang)
                {
-                        XPathDocument doc = new XPathDocument (Path.Combine ("langs", lang + ".xml"));
+                        XPathDocument doc = GetXPathDocument (Path.Combine ("langs", GetShortName (lang) + ".xml"));
                        XPathNavigator nav = doc.CreateNavigator ();
                         CultureInfoEntry ci = new CultureInfoEntry ();
                         string lang_type, terr_type;
@@ -191,17 +304,17 @@ namespace Mono.Tools.LocaleBuilder {
                         ci.Language = (lang_type == String.Empty ? null : lang_type);
                         ci.Territory = (terr_type == String.Empty ? null : terr_type);
 
-                       if (!LookupLcids (ci))
+                       if (!LookupLcids (ci, true))
                                 return false;
 
-                        doc = new XPathDocument (Path.Combine ("langs", Lang + ".xml"));
+                        doc = GetXPathDocument (Path.Combine ("langs", GetShortName (Lang) + ".xml"));
                        nav = doc.CreateNavigator ();
                        ci.DisplayName = LookupFullName (ci, nav);
                         
                        if (Lang == "en") {
                                ci.EnglishName = ci.DisplayName;
                        } else {
-                               doc = new XPathDocument (Path.Combine ("langs", Lang + ".xml"));
+                               doc = GetXPathDocument (Path.Combine ("langs", GetShortName (lang) + ".xml"));
                                nav = doc.CreateNavigator ();
                                ci.EnglishName = LookupFullName (ci, nav);
                        }
@@ -209,7 +322,7 @@ namespace Mono.Tools.LocaleBuilder {
                        if (ci.Language == Lang) {
                                ci.NativeName = ci.DisplayName;
                        } else {
-                               doc = new XPathDocument (Path.Combine ("langs", lang + ".xml"));
+                               doc = GetXPathDocument (Path.Combine ("langs", GetShortName (lang) + ".xml"));
                                nav = doc.CreateNavigator ();
                                ci.NativeName = LookupFullName (ci, nav);
                        }
@@ -233,8 +346,8 @@ namespace Mono.Tools.LocaleBuilder {
                         if (ci == null)
                                 return;
 
-                        if (langs [ci.Language] == null) {
-                                if (!ParseLang (ci.Language)) // If we can't parse the lang we cant have the locale
+                        if (langs [GetLanguageFixed (ci)] == null) {
+                                if (!ParseLang (GetLanguageFixed (ci))) // If we can't parse the lang we cant have the locale
                                         return;
                         }
 
@@ -243,7 +356,10 @@ namespace Mono.Tools.LocaleBuilder {
 
                private CultureInfoEntry LookupCulture (string locale)
                {
-                        XPathDocument doc = new XPathDocument (Path.Combine ("locales", locale + ".xml"));
+                       string path = Path.Combine ("locales", locale + ".xml");
+                       if (!File.Exists (path))
+                               return null;
+                        XPathDocument doc = GetXPathDocument (path);
                         XPathNavigator nav = doc.CreateNavigator ();
                        CultureInfoEntry ci = new CultureInfoEntry ();
                        string supp;
@@ -254,7 +370,7 @@ namespace Mono.Tools.LocaleBuilder {
                        ci.Language = nav.Evaluate ("string (ldml/identity/language/@type)").ToString ();
                        ci.Territory = nav.Evaluate ("string (ldml/identity/territory/@type)").ToString ();
 
-                        if (!LookupLcids (ci))
+                        if (!LookupLcids (ci, false))
                                 return null;
                        LookupNames (ci);
 
@@ -267,32 +383,32 @@ namespace Mono.Tools.LocaleBuilder {
                         * locale file. Values in each descending file can
                         * overwrite previous values.
                         */
-                        doc = new XPathDocument (Path.Combine ("langs", "root.xml"));
+                        doc = GetXPathDocument (Path.Combine ("langs", "root.xml"));
                         nav = doc.CreateNavigator ();
                         Lookup (nav, ci);
 
-                        doc = new XPathDocument (Path.Combine ("supp", "root.xml"));
+                        doc = GetXPathDocument (Path.Combine ("supp", "root.xml"));
                         nav = doc.CreateNavigator ();
                         Lookup (nav, ci);
 
-                       doc = new XPathDocument (Path.Combine ("langs", ci.Language + ".xml"));
+                       doc = GetXPathDocument (Path.Combine ("langs", GetShortName (GetLanguageFixed (ci)) + ".xml"));
                        nav = doc.CreateNavigator ();
                        Lookup (nav, ci);
 
-                       supp = Path.Combine ("supp", ci.Language + ".xml");
+                       supp = Path.Combine ("supp", GetLanguageFixed (ci) + ".xml");
                        if (File.Exists (supp)) {
-                               doc = new XPathDocument (supp);
+                               doc = GetXPathDocument (supp);
                                nav = doc.CreateNavigator ();
                                Lookup (nav, ci);
                        }
                        
-                       doc = new XPathDocument (Path.Combine ("locales", locale + ".xml"));
+                       doc = GetXPathDocument (Path.Combine ("locales", locale + ".xml"));
                        nav = doc.CreateNavigator ();
                        Lookup (nav, ci);
 
                        supp = Path.Combine ("supp", locale + ".xml");
                        if (File.Exists (supp)) {
-                               doc = new XPathDocument (supp);
+                               doc = GetXPathDocument (supp);
                                nav = doc.CreateNavigator ();
                                Lookup (nav, ci);
                        }
@@ -306,9 +422,22 @@ namespace Mono.Tools.LocaleBuilder {
                        LookupNumberInfo (nav, ci);
                }
 
+               private string GetLanguageFixed (CultureInfoEntry ci)
+               {
+                       // This is a hack, but without it nb-NO and nn-NO won't work.
+                       if (ci.Territory == "NO") {
+                               switch (ci.Language) {
+                               case "nb":
+                               case "nn":
+                                       return "no";
+                               }
+                       }
+                       return ci.Language;
+               }
+
                private void LookupNames (CultureInfoEntry ci)
                {
-                       XPathDocument doc = new XPathDocument (Path.Combine ("langs", Lang + ".xml"));
+                       XPathDocument doc = GetXPathDocument (Path.Combine ("langs", GetShortName (Lang) + ".xml"));
                        XPathNavigator nav = doc.CreateNavigator ();
 
                        ci.DisplayName = LookupFullName (ci, nav);
@@ -316,7 +445,7 @@ namespace Mono.Tools.LocaleBuilder {
                        if (Lang == "en") {
                                ci.EnglishName = ci.DisplayName;
                        } else {
-                               doc = new XPathDocument (Path.Combine ("langs", "en.xml"));
+                               doc = GetXPathDocument (Path.Combine ("langs", "en.xml"));
                                nav = doc.CreateNavigator ();
                                ci.EnglishName = LookupFullName (ci, nav);
                        }
@@ -324,12 +453,22 @@ namespace Mono.Tools.LocaleBuilder {
                        if (ci.Language == Lang) {
                                ci.NativeName = ci.DisplayName;
                        } else {
-                               doc = new XPathDocument (Path.Combine ("langs", ci.Language + ".xml"));
+                               // FIXME: We use ci.Language here.
+                               // This is nothing more than hack for nb-NO and nn-NO
+                               // where Parent of them is nn (not nb or nn).
+                               string lang = ci.Language;
+                               doc = GetXPathDocument (Path.Combine ("langs", GetShortName (lang) + ".xml"));
                                nav = doc.CreateNavigator ();
                                ci.NativeName = LookupFullName (ci, nav);
                        }
                }
 
+               private void AddPattern (ArrayList al, string pattern)
+               {
+                       if (!al.Contains (pattern))
+                               al.Add (pattern);
+               }
+
                private void LookupDateTimeInfo (XPathNavigator nav, CultureInfoEntry ci)
                {
                        /**
@@ -384,6 +523,8 @@ namespace Mono.Tools.LocaleBuilder {
                                                df.MonthNames.Clear ();
                                        df.MonthNames.Add (ni2.Current.Value);
                                }
+                               if (df.MonthNames.Count == 12)
+                                       df.MonthNames.Add (String.Empty);
 
                                ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dayNames/day");
                                while (ni2.MoveNext ()) {
@@ -405,46 +546,105 @@ namespace Mono.Tools.LocaleBuilder {
                                                df.AbbreviatedMonthNames.Clear ();
                                        df.AbbreviatedMonthNames.Add (ni2.Current.Value);
                                }
+                               if (df.AbbreviatedMonthNames.Count == 12)
+                                       df.AbbreviatedMonthNames.Add (String.Empty);
 
                                ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dateFormats/dateFormatLength");
                                while (ni2.MoveNext ()) {
                                        XPathNavigator df_nav = ni2.Current;
+                                       XPathNodeIterator p = df_nav.Select ("dateFormat/pattern");
+                                       string value = null;
+                                       if (p.MoveNext ())
+                                               value = p.Current.Value;
+                                       XPathNodeIterator ext = null;
                                        switch (df_nav.GetAttribute ("type", String.Empty)) {
-                                        case "full":
-                                                ParseFullDateFormat (df, df_nav.Value);
-                                                break;
+                                       case "full":
+                                               if (value != null)
+                                                       ParseFullDateFormat (df, value);
+                                               break;
                                        case "long":
-                                               df.LongDatePattern = df_nav.Value;
+                                               if (value != null)
+                                                       df.LongDatePattern = value;
+                                               ext = df_nav.Select ("extraPatterns/pattern");
+                                               if (ext.MoveNext ()) {
+                                                       df.LongDatePatterns.Clear ();
+                                                       AddPattern (df.LongDatePatterns, df.LongDatePattern);
+                                                       do {
+                                                               df.LongDatePatterns.Add (ext.Current.Value);
+                                                       } while (ext.MoveNext ());
+                                               }
+                                               else
+                                                       AddPattern (df.LongDatePatterns, df.LongDatePattern);
                                                break;
                                        case "short":
-                                               df.ShortDatePattern = df_nav.Value;
+                                               if (value != null)
+                                                       df.ShortDatePattern = value;
+                                               ext = df_nav.Select ("extraPatterns/pattern");
+                                               if (ext.MoveNext ()) {
+                                                       df.ShortDatePatterns.Clear ();
+                                                       AddPattern (df.ShortDatePatterns, df.ShortDatePattern);
+                                                       do {
+                                                               df.ShortDatePatterns.Add (ext.Current.Value);
+                                                       } while (ext.MoveNext ());
+                                               }
+                                               else
+                                                       AddPattern (df.ShortDatePatterns, df.ShortDatePattern);
+                                               break;
+                                       case "year_month":
+                                               if (value != null)
+                                                       df.YearMonthPattern = value;
+                                               break;
+                                       case "month_day":
+                                               if (value != null)
+                                                       df.MonthDayPattern = value;
                                                break;
-                                        case "year_month":
-                                                df.YearMonthPattern = df_nav.Value;
-                                                break;
-                                        case "month_day":
-                                                df.MonthDayPattern = df_nav.Value;
-                                                break;
                                        }
                                }
 
                                ni2 = (XPathNodeIterator) ni.Current.Evaluate ("timeFormats/timeFormatLength");
                                while (ni2.MoveNext ()) {
                                        XPathNavigator df_nav = ni2.Current;
+                                       XPathNodeIterator p = df_nav.Select ("timeFormat/pattern");
+                                       string value = null;
+                                       if (p.MoveNext ())
+                                               value = p.Current.Value;
+                                       XPathNodeIterator ext = null;
                                        switch (df_nav.GetAttribute ("type", String.Empty)) {
                                        case "long":
-                                               df.LongTimePattern = df_nav.Value.Replace ('a', 't');
+                                               if (value != null)
+                                                       df.LongTimePattern = value.Replace ('a', 't');
+                                               ext = df_nav.Select ("extraPatterns/pattern");
+                                               if (ext.MoveNext ()) {
+                                                       df.LongTimePatterns.Clear ();
+                                                       AddPattern (df.LongTimePatterns, df.LongTimePattern);
+                                                       do {
+                                                               df.LongTimePatterns.Add (ext.Current.Value);
+                                                       } while (ext.MoveNext ());
+                                               }
+                                               else
+                                                       AddPattern (df.LongTimePatterns, df.LongTimePattern);
                                                break;
                                        case "short":
-                                               df.ShortTimePattern = df_nav.Value.Replace ('a', 't');
+                                               if (value != null)
+                                                       df.ShortTimePattern = value.Replace ('a', 't');
+                                               ext = df_nav.Select ("extraPatterns/pattern");
+                                               if (ext.MoveNext ()) {
+                                                       df.ShortTimePatterns.Clear ();
+                                                       AddPattern (df.ShortTimePatterns, df.ShortTimePattern);
+                                                       do {
+                                                               df.ShortTimePatterns.Add (ext.Current.Value);
+                                                       } while (ext.MoveNext ());
+                                               }
+                                               else
+                                                       AddPattern (df.ShortTimePatterns, df.ShortTimePattern);
                                                break;
                                        }
                                }
 
                                ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dateTimeFormats/dateTimeFormatLength/dateTimeFormat/pattern");
                                if (ni2.MoveNext ())
-                                       df.FullDateTimePattern = String.Format (ni2.Current.ToString (),
-                                                       df.LongTimePattern, df.LongDatePattern);
+                                       df.RawFullDateTimePattern = ni2.Current.ToString ();/*String.Format (ni2.Current.ToString (),
+                                                       df.LongTimePattern, df.LongDatePattern);*/
 
                                XPathNodeIterator am = ni.Current.SelectChildren ("am", "");
                                if (am.MoveNext ())
@@ -495,11 +695,17 @@ namespace Mono.Tools.LocaleBuilder {
 
                        string [] part_one, part_two;
                        string [] pos_neg = format.Split (new char [1] {';'}, 2);
-                       
+
+                       // Most of the patterns are common in positive and negative
+                       if (pos_neg.Length == 1)
+                               pos_neg = new string [] {pos_neg [0], pos_neg [0]};
+
                        if (pos_neg.Length == 2) {
                                
                                part_one = pos_neg [0].Split (new char [1] {'.'}, 2);
-                                                                                       
+                               if (part_one.Length == 1)
+                                       part_one = new string [] {part_one [0], String.Empty};
+
                                if (part_one.Length == 2) {
                                        // assumed same for both positive and negative
                                        // decimal digit side
@@ -508,8 +714,10 @@ namespace Mono.Tools.LocaleBuilder {
                                                if (part_one [1][i] == '#') {
                                                        ci.NumberFormatEntry.NumberDecimalDigits ++;
                                                } else
-                                                       break;                                          
-                                       }
+                                                       break;                                                          }
+                                       // FIXME: This should be actually done by modifying culture xml files, but too many files to be modified.
+                                       if (ci.NumberFormatEntry.NumberDecimalDigits > 0)
+                                               ci.NumberFormatEntry.NumberDecimalDigits --;
 
                                        // decimal grouping side
                                        part_two = part_one [0].Split (',');
@@ -521,7 +729,7 @@ namespace Mono.Tools.LocaleBuilder {
                                                        ci.NumberFormatEntry.NumberGroupSizes [i] = pat.Length;
                                                }
                                        } else {
-                                               ci.NumberFormatEntry.NumberGroupSizes = new int [1] { 0 };
+                                               ci.NumberFormatEntry.NumberGroupSizes = new int [1] { 3 };
                                        }
 
                                        if (pos_neg [1].StartsWith ("(") && pos_neg [1].EndsWith (")")) {
@@ -556,19 +764,21 @@ namespace Mono.Tools.LocaleBuilder {
                        if (format.StartsWith ("%")) {
                                ci.NumberFormatEntry.PercentPositivePattern = 2;
                                ci.NumberFormatEntry.PercentNegativePattern = 2;
+                               format = format.Substring (1);
                        } else if (format.EndsWith (" %")) {
                                ci.NumberFormatEntry.PercentPositivePattern = 0;
                                ci.NumberFormatEntry.PercentNegativePattern = 0;
+                               format = format.Substring (0, format.Length - 2);
                        } else if (format.EndsWith ("%")) {
                                ci.NumberFormatEntry.PercentPositivePattern = 1;
                                ci.NumberFormatEntry.PercentNegativePattern = 1;
+                               format = format.Substring (0, format.Length - 1);
                        } else {
                                ci.NumberFormatEntry.PercentPositivePattern = 0;
                                ci.NumberFormatEntry.PercentNegativePattern = 0;
                        }
 
                        part_one = format.Split (new char [1] {'.'}, 2);
-                       
                        if (part_one.Length == 2) {
                                // assumed same for both positive and negative
                                // decimal digit side
@@ -579,7 +789,9 @@ namespace Mono.Tools.LocaleBuilder {
                                        else
                                                break;
                                }
+                       }
 
+                       if (part_one.Length > 0) {
                                // percent grouping side
                                part_two = part_one [0].Split (',');
                                if (part_two.Length > 1) {
@@ -587,10 +799,13 @@ namespace Mono.Tools.LocaleBuilder {
                                        ci.NumberFormatEntry.PercentGroupSizes = new int [len];
                                        for (int i = 0; i < len; i++) {
                                                string pat = part_two [i + 1];
+                                               if (pat [pat.Length -1] == '0')
+                                                       ci.NumberFormatEntry.PercentDecimalDigits = pat.Length - 1;
                                                ci.NumberFormatEntry.PercentGroupSizes [i] = pat.Length;
                                        }
                                } else {
-                                       ci.NumberFormatEntry.PercentGroupSizes = new int [1] { 0 };
+                                       ci.NumberFormatEntry.PercentGroupSizes = new int [1] { 3 };
+                                       ci.NumberFormatEntry.PercentDecimalDigits = 2;
                                }
                        }
                }
@@ -605,11 +820,15 @@ namespace Mono.Tools.LocaleBuilder {
 
                        string [] part_one, part_two;
                        string [] pos_neg = format.Split (new char [1] {';'}, 2);
-                       
-                       pos_neg = format.Split (new char [1] {';'}, 2);                 
+       
+                       // Most of the patterns are common in positive and negative
+                       if (pos_neg.Length == 1)
+                               pos_neg = new string [] {pos_neg [0], pos_neg [0]};
+
                        if (pos_neg.Length == 2) {
                                part_one = pos_neg [0].Split (new char [1] {'.'}, 2);
-                               
+                               if (part_one.Length == 1)
+                                       part_one = new string [] {part_one [0], String.Empty};
                                if (part_one.Length == 2) {
                                        // assumed same for both positive and negative
                                        // decimal digit side
@@ -631,7 +850,7 @@ namespace Mono.Tools.LocaleBuilder {
                                                        ci.NumberFormatEntry.CurrencyGroupSizes [i] = pat.Length;
                                                }
                                        } else {
-                                               ci.NumberFormatEntry.CurrencyGroupSizes = new int [1] { 0 };
+                                               ci.NumberFormatEntry.CurrencyGroupSizes = new int [1] { 3 };
                                        }
 
                                        if (pos_neg [1].StartsWith ("(\u00a4 ") && pos_neg [1].EndsWith (")")) {
@@ -738,25 +957,29 @@ namespace Mono.Tools.LocaleBuilder {
                                 ci.NumberFormatEntry.CurrencySymbol = cur;
                 }
 
-               private bool LookupLcids (CultureInfoEntry ci)
+               private bool LookupLcids (CultureInfoEntry ci, bool lang)
                {
-                       XPathDocument doc = new XPathDocument ("lcids.xml");
-                       XPathNavigator nav = doc.CreateNavigator ();
-                       string name = ci.Language;
+                       XPathNavigator nav = lcids_doc.CreateNavigator ();
+                       string name = ci.Name;
+                       // Language name does not always consist of locale name.
+                       // (for zh-* it must be either zh-CHS or zh-CHT)
+                       string langName = GetLanguageFixed (ci);
 
-                        if (ci.Territory != null)
-                                name += "-" + ci.Territory;
+//                        if (ci.Territory != null)
+//                                name += "-" + ci.Territory;
 
                        XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("lcids/lcid[@name='"
-                                       + name + "']");
+                                       + (lang ? langName : name) + "']");
                        if (!ni.MoveNext ()) {
+                               Console.WriteLine ("no lcid found for: {0} ({1}/{2})", name, ci.Language, ci.Territory);
                                 string file;
+
                                 if (ci.Territory != null) {
                                         file = Path.Combine ("locales", ci.Language + "_" + ci.Territory + ".xml");
-                                        File.Delete (file);
                                         Console.WriteLine ("deleting file:  " + file);
+                                        File.Delete (file);
                                 }
-                               Console.WriteLine ("no lcid found for:  " + name);
+
                                return false;
                        }
 
@@ -776,6 +999,8 @@ namespace Mono.Tools.LocaleBuilder {
                         ci.ISO3Lang = iso3;
                         ci.Win3Lang = win;
                         ci.IcuName = icu;
+                       
+                       ci.TextInfoEntry = new TextInfoEntry (int.Parse (id.Substring (2), NumberStyles.HexNumber), GetXPathDocument ("textinfo.xml"));
 
                         return true;
                }
@@ -785,8 +1010,11 @@ namespace Mono.Tools.LocaleBuilder {
                        string pre = "ldml/localeDisplayNames/";
                        string ret;
 
+                       // FIXME: We use ci.Language here.
+                       // This is nothing more than hack for nb-NO or nn-NO
+                       // where Parent of them is nn (not nb or nn).
                        ret = (string) nav.Evaluate ("string("+
-                                       pre + "languages/language[@type='" + ci.Language + "'])");
+                                       pre + "languages/language[@type='" + GetShortName (ci.Language) + "'])");
 
                        if (ci.Territory == null)
                                return ret;
@@ -796,9 +1024,45 @@ namespace Mono.Tools.LocaleBuilder {
                        return ret;
                }
 
+               private void LookupRegions ()
+               {
+                        XPathDocument doc = GetXPathDocument ("supplementalData.xml");
+                       XPathNavigator nav = doc.CreateNavigator ();
+                       XPathNodeIterator ni = nav.Select ("supplementalData/currencyData/region");
+                       while (ni.MoveNext ()) {
+                               string territory = (string) ni.Current.GetAttribute ("iso3166", String.Empty);
+                                string currency = (string) ni.Current.Evaluate ("string(currency/@iso4217)");
+                               RegionInfoEntry region = new RegionInfoEntry ();
+                               region.ISO2Name = territory.ToUpper ();
+                               region.ISOCurrencySymbol = currency;
+                               regions [territory] = region;
+                       }
+
+                        doc = GetXPathDocument ("langs/en.xml");
+                       nav = doc.CreateNavigator ();
+                       ni = nav.Select ("/ldml/localeDisplayNames/territories/territory");
+                       while (ni.MoveNext ()) {
+                               RegionInfoEntry r = (RegionInfoEntry)
+                                       regions [ni.Current.GetAttribute ("type", "")];
+                               if (r == null)
+                                       continue;
+                               r.EnglishName = ni.Current.Value;
+                       }
+
+                       Hashtable curNames = new Hashtable ();
+                       ni = nav.Select ("/ldml/numbers/currencies/currency");
+                       while (ni.MoveNext ())
+                               curNames [ni.Current.GetAttribute ("type", "")] =
+                                       ni.Current.Evaluate ("string (displayName)");
+
+                       foreach (RegionInfoEntry r in regions.Values)
+                               r.CurrencyEnglishName =
+                                       (string) curNames [r.ISOCurrencySymbol];
+               }
+
                 private void LookupCurrencyTypes ()
                 {
-                        XPathDocument doc = new XPathDocument ("supplementalData.xml");
+                        XPathDocument doc = GetXPathDocument ("supplementalData.xml");
                        XPathNavigator nav = doc.CreateNavigator ();
 
                         currency_types = new Hashtable ();
@@ -811,7 +1075,7 @@ namespace Mono.Tools.LocaleBuilder {
                        }
                 }
 
-                static string control_chars = "ghmsftz";
+                static string control_chars = "eghmsftz";
 
                 // HACK: We are trying to build year_month and month_day patterns from the full pattern.
                 private void ParseFullDateFormat (DateTimeFormatEntry df, string full)
@@ -821,29 +1085,37 @@ namespace Mono.Tools.LocaleBuilder {
                         string year_month = String.Empty;
                         bool in_month_data = false;
                         bool in_year_data = false;
-                        int month_end = 0;
-                        int year_end = 0;
+                       int day_start = 0, day_end = 0;
+                        int month_start = 0, month_end = 0;
+                        int year_start = 0, year_end = 0;
+                       bool inquote = false;
                         
                         for (int i = 0; i < full.Length; i++) {
                                 char c = full [i];
-                                if (c == 'M') {
+                               if (!inquote && c == 'M') {
                                         month_day += c;
                                         year_month += c;
                                         in_year_data = true;
                                         in_month_data = true;
-                                        month_end = month_day.Length;
+                                       if (month_start == 0)
+                                               month_start = i;
+                                        month_end = i;
                                         year_end = year_month.Length;
-                                } else if (Char.ToLower (c) == 'd') {
+                                } else if (!inquote && Char.ToLower (c) == 'd') {
                                         month_day += c;
                                         in_month_data = true;
                                         in_year_data = false;
-                                        month_end = month_day.Length;
-                                } else if (Char.ToLower (c) == 'y') {
+                                       if (day_start == 0)
+                                               day_start = i;
+                                        day_end = i;
+                                } else if (!inquote && Char.ToLower (c) == 'y') {
                                         year_month += c;
                                         in_year_data = true;
                                         in_month_data = false;
-                                        year_end = year_month.Length;
-                                } else if (control_chars.IndexOf (Char.ToLower (c)) >= 0) {
+                                       if (year_start == 0)
+                                               year_start = i;
+                                        year_end = i;
+                                } else if (!inquote && control_chars.IndexOf (Char.ToLower (c)) >= 0) {
                                         in_year_data = false;
                                         in_month_data = false;
                                 } else if (in_year_data || in_month_data) {
@@ -852,18 +1124,42 @@ namespace Mono.Tools.LocaleBuilder {
                                         if (in_year_data)
                                                 year_month += c;
                                 }
+
+                               if (c == '\'') {
+                                       inquote = !inquote;
+                                }
                         }
 
                         if (month_day != String.Empty) {
-                                month_day = month_day.Substring (0, month_end);
-                                df.MonthDayPattern = month_day;
+                                //month_day = month_day.Substring (0, month_end);
+                                df.MonthDayPattern = TrimPattern (month_day);
                         }
                         if (year_month != String.Empty) {
-                                year_month = year_month.Substring (0, year_end);
-                                df.YearMonthPattern = year_month;
+                                //year_month = year_month.Substring (0, year_end);
+                                df.YearMonthPattern = TrimPattern (year_month);
                         }
                 }
 
+               string TrimPattern (string p)
+               {
+                       int idx = 0;
+                       p = p.Trim ().TrimEnd (',');
+                       idx = p.LastIndexOf ("' de '"); // spanish dates
+                       if (idx > 0)
+                               p = p.Substring (0, idx);
+                       idx = p.LastIndexOf ("' ta '"); // finnish
+                       if (idx > 0)
+                               p = p.Substring (0, idx);
+                       idx = p.LastIndexOf ("'ren'"); // euskara
+                       if (idx > 0)
+                               p = p.Replace ("'ren'", "").Trim ();
+                       idx = p.LastIndexOf ("'a'"); // estonian
+                       if (idx > 0)
+                               p = p.Substring (0, idx);
+
+                       return p.Replace ("'ta '", "'ta'"); // finnish
+               }
+
                 private class LcidComparer : IComparer {
 
                         public int Compare (object a, object b)
@@ -882,9 +1178,34 @@ namespace Mono.Tools.LocaleBuilder {
                                 CultureInfoEntry aa = (CultureInfoEntry) a;
                                 CultureInfoEntry bb = (CultureInfoEntry) b;
 
-                                return aa.Name.ToLower ().CompareTo (bb.Name.ToLower ());
+                                return String.CompareOrdinal(aa.Name.ToLower (), bb.Name.ToLower ());
                         }
                 }
+
+               class RegionComparer : IComparer
+               {
+                       public static RegionComparer Instance = new RegionComparer ();
+                       
+                       public int Compare (object o1, object o2)
+                       {
+                               RegionInfoEntry r1 = (RegionInfoEntry) o1;
+                               RegionInfoEntry r2 = (RegionInfoEntry) o2;
+                               return String.CompareOrdinal (
+                                       r1.ISO2Name, r2.ISO2Name);
+                       }
+               }
+
+               class RegionLCIDMap
+               {
+                       public RegionLCIDMap (int lcid, int regionId)
+                       {
+                               LCID = lcid;
+                               RegionId = regionId;
+                       }
+
+                       public int LCID;
+                       public int RegionId;
+               }
         }
 }