New tests, updates
[mono.git] / tools / locale-builder / Driver.cs
1 //
2 // Mono.Tools.LocalBuilder.Driver
3 //
4 // Author(s):
5 //  Jackson Harper (jackson@ximian.com)
6 //  Atsushi Enomoto (atsushi@ximian.com)
7 //
8 // (C) 2004-2005 Novell, Inc (http://www.novell.com)
9 //
10
11
12 using System;
13 using System.IO;
14 using System.Text;
15 using System.Xml;
16 using System.Xml.XPath;
17 using System.Collections;
18 using System.Globalization;
19 using System.Text.RegularExpressions;
20
21 namespace Mono.Tools.LocaleBuilder {
22
23         public class Driver {
24
25                 public static void Main (string [] args)
26                 {
27                         Driver d = new Driver ();
28                         ParseArgs (args, d);
29                         d.Run ();
30                 }
31
32                 private static void ParseArgs (string [] args, Driver d)
33                 {
34                         for (int i = 0; i < args.Length; i++) {
35                                 if (args [i] == "--lang" && i+1 < args.Length)
36                                         d.Lang = args [++i];
37                                 else if (args [i] == "--locales" && i+1 < args.Length)
38                                         d.Locales = args [++i];
39                                 else if (args [i] == "--header" && i + 1 < args.Length)
40                                         d.HeaderFileName = args [++i];
41                         }
42                 }
43
44                 private string lang;
45                 private string locales;
46                 private string header_name;
47                 private ArrayList cultures;
48                 private Hashtable langs;
49                 private Hashtable currency_types;
50                 private Hashtable regions;
51
52                 private XPathDocument lcids_doc;
53
54                 // The lang is the language that display names will be displayed in
55                 public string Lang {
56                         get {
57                                 if (lang == null)
58                                         lang = "en";
59                                 return lang;
60                         }
61                         set { lang = value; }
62                 }
63
64                 public string Locales {
65                         get { return locales; }
66                         set { locales = value; }
67                 }
68
69                 public string HeaderFileName {
70                         get {
71                                 if (header_name == null)
72                                         return "culture-info-tables.h";
73                                 return header_name;
74                         }
75                         set { header_name = value; }
76                 }
77
78                 public void Run ()
79                 {
80                         lcids_doc = GetXPathDocument ("lcids.xml");
81
82                         Regex locales_regex = null;
83                         if (Locales != null)
84                                 locales_regex = new Regex (Locales);
85
86                         langs = new Hashtable ();
87                         cultures = new ArrayList ();
88                         regions = new Hashtable ();
89
90                         LookupRegions ();
91
92                         LookupCurrencyTypes ();
93
94                         foreach (string file in Directory.GetFiles ("locales", "*.xml")) {
95                                 string fn = Path.GetFileNameWithoutExtension (file);
96                                 if (fn == "hy_AM")
97                                         continue; // see bug #75499
98                                 if (locales_regex == null || locales_regex.IsMatch (fn)) {
99                                         ParseLocale (fn);
100                                 }
101                         }
102
103                         /* FIXME: This is hacky.
104                          * Since there is only langs/zh.xml while there are
105                          * two "zh" languages (CHS and CHT), there should be
106                          * different language profiles and we are not likely
107                          * to add lang/* files. So here I just clone zh-CHS
108                          * as zh-CHT
109                          */
110                          foreach (CultureInfoEntry e in cultures) {
111                                 if (e.Name == "zh-CHS") {
112                                         CultureInfoEntry t =
113                                                 CultureInfoEntry.ShallowCopy (e);
114                                         t.Language = "zh-CHT";
115                                         LookupLcids (t, true);
116                                         cultures.Add (t);
117                                         break;
118                                 }
119                          }
120
121                         ArrayList regionList = new ArrayList (regions.Values);
122                         regionList.Sort (RegionComparer.Instance);
123                         int number = 0;
124                         foreach (RegionInfoEntry r in regionList)
125                                 r.RegionId = number++;
126
127                         foreach (CultureInfoEntry e in cultures) {
128                                 int lcid = int.Parse (e.Lcid.Substring (2),
129                                         NumberStyles.HexNumber);
130                                 int idx;
131                                 int start = e.Name.IndexOf ('-') + 1;
132                                 if (start == 0)
133                                         continue;
134                                 for (idx = start; idx < e.Name.Length; idx++)
135                                         if (!Char.IsLetter (e.Name [idx]))
136                                                 break;
137                                 if (start == idx) {
138                                         Console.Error.WriteLine ("Culture {0} {1} is not mappable to Region.", e.Lcid, e.Name);
139                                         continue;
140                                 }
141                                 string name = e.Name.Substring (start, idx - start);
142                                 RegionInfoEntry rm = null;
143                                 foreach (RegionInfoEntry r in regions.Values)
144                                         if (r.ISO2Name == name) {
145                                                 rm = r;
146                                                 break;
147                                         }
148                                 if (rm == null) {
149                                         Console.Error.WriteLine ("No definition for region {0}", name);
150                                         continue;
151                                 }
152                                 e.RegionId = rm.RegionId;
153                         }
154
155                         /**
156                          * Dump each table individually. Using StringBuilders
157                          * because it is easier to debug, should switch to just
158                          * writing to streams eventually.
159                          */
160                         using (StreamWriter writer = new StreamWriter (HeaderFileName, false, new UTF8Encoding (false, true))) {
161                                 writer.NewLine = "\n";
162                                 writer.WriteLine ();
163                                 writer.WriteLine ("/* This is a generated file. Do not edit. See tools/locale-builder. */");
164                                 writer.WriteLine ("#ifndef MONO_METADATA_CULTURE_INFO_TABLES");
165                                 writer.WriteLine ("#define MONO_METADATA_CULTURE_INFO_TABLES 1");
166                                 writer.WriteLine ("\n");
167
168                                 writer.WriteLine ("#define NUM_CULTURE_ENTRIES " + cultures.Count);
169                                 writer.WriteLine ("#define NUM_REGION_ENTRIES " + regionList.Count);
170                                 writer.WriteLine ("\n");
171
172                                 // Sort the cultures by lcid
173                                 cultures.Sort (new LcidComparer ());
174
175                                 StringBuilder builder = new StringBuilder ();
176                                 int row = 0;
177                                 int count = cultures.Count;
178                                 for (int i = 0; i < count; i++) {
179                                         CultureInfoEntry ci = (CultureInfoEntry) cultures [i];
180                                         if (ci.DateTimeFormatEntry == null)
181                                                 continue;
182                                         ci.DateTimeFormatEntry.AppendTableRow (builder);
183                                         ci.DateTimeFormatEntry.Row = row++;
184                                         if (i + 1 < count)
185                                                 builder.Append (',');
186                                         builder.Append ('\n');
187                                 }
188
189                                 writer.WriteLine ("static const DateTimeFormatEntry datetime_format_entries [] = {");
190                                 writer.Write (builder);
191                                 writer.WriteLine ("};\n\n");
192                                 
193                                 builder = new StringBuilder ();
194                                 row = 0;
195                                 for (int i=0; i < count; i++) {
196                                         CultureInfoEntry ci = (CultureInfoEntry) cultures [i];
197                                         if (ci.NumberFormatEntry == null)
198                                                 continue;
199                                         ci.NumberFormatEntry.AppendTableRow (builder);
200                                         ci.NumberFormatEntry.Row = row++;
201                                         if (i + 1 < count)
202                                                 builder.Append (',');
203                                         builder.Append ('\n');
204                                 }
205
206                                 writer.WriteLine ("static const NumberFormatEntry number_format_entries [] = {");
207                                 writer.Write (builder);
208                                 writer.WriteLine ("};\n\n");
209                                 
210                                 builder = new StringBuilder ();
211                                 row = 0;
212                                 for (int i = 0; i < count; i++) {
213                                         CultureInfoEntry ci = (CultureInfoEntry) cultures [i];
214                                         ci.AppendTableRow (builder);
215                                         ci.Row = row++;
216                                         if (i + 1 < count)
217                                                 builder.Append (',');
218                                         builder.Append ('\n');
219                                 }
220                                 
221                                 writer.WriteLine ("static const CultureInfoEntry culture_entries [] = {");
222                                 writer.Write (builder);
223                                 writer.WriteLine ("};\n\n");
224
225                                 cultures.Sort (new NameComparer ()); // Sort based on name
226                                 builder = new StringBuilder ();
227                                 for (int i = 0; i < count; i++) {
228                                         CultureInfoEntry ci = (CultureInfoEntry) cultures [i];
229                                         builder.Append ("\t{" + Entry.EncodeStringIdx (ci.Name.ToLower ()) + ", ");
230                                         builder.Append (ci.Row + "}");
231                                         if (i + 1 < count)
232                                                 builder.Append (',');
233                                         builder.Append ('\n');
234                                 }
235
236                                 writer.WriteLine ("static const CultureInfoNameEntry culture_name_entries [] = {");
237                                 writer.Write (builder);
238                                 writer.WriteLine ("};\n\n");
239
240                                 builder = new StringBuilder ();
241                                 int rcount = 0;
242                                 foreach (RegionInfoEntry r in regionList) {
243                                         r.AppendTableRow (builder);
244                                         if (++rcount != regionList.Count)
245                                                 builder.Append (',');
246                                         builder.Append ('\n');
247                                 }
248                                 writer.WriteLine ("static const RegionInfoEntry region_entries [] = {");
249                                 writer.Write (builder);
250                                 writer.WriteLine ("};\n\n");
251
252                                 builder = new StringBuilder ();
253                                 rcount = 0;
254                                 foreach (RegionInfoEntry ri in regionList) {
255                                         builder.Append ("\t{" + Entry.EncodeStringIdx (ri.ISO2Name) + ", ");
256                                         builder.Append (ri.RegionId + "}");
257                                         if (++rcount < regionList.Count)
258                                                 builder.Append (',');
259                                         builder.Append ('\n');
260                                 }
261
262                                 writer.WriteLine ("static const RegionInfoNameEntry region_name_entries [] = {");
263                                 writer.Write (builder);
264                                 writer.WriteLine ("};\n\n");
265
266                                 writer.WriteLine ("static const char locale_strings [] = {");
267                                 writer.Write (Entry.GetStrings ());
268                                 writer.WriteLine ("};\n\n");
269
270                                 writer.WriteLine ("#endif\n");
271                         }
272                 }
273
274                 private XPathDocument GetXPathDocument (string path)
275                 {
276                         XmlTextReader xtr = null;
277                         try {
278                                 xtr = new XmlTextReader (path);
279                                 xtr.XmlResolver = null;
280                                 return new XPathDocument (xtr);
281                         } finally {
282                                 if (xtr != null)
283                                         xtr.Close ();
284                         }
285                 }
286
287                 private string GetShortName (string lang)
288                 {
289                         return lang == "zh-CHS" ? "zh" : lang;
290                 }
291
292                 private bool ParseLang (string lang)
293                 {
294                         XPathDocument doc = GetXPathDocument (Path.Combine ("langs", GetShortName (lang) + ".xml"));
295                         XPathNavigator nav = doc.CreateNavigator ();
296                         CultureInfoEntry ci = new CultureInfoEntry ();
297                         string lang_type, terr_type;
298
299 //                        ci.Name = lang; // TODO: might need to be mapped.
300
301                         lang_type = nav.Evaluate ("string (ldml/identity/language/@type)").ToString ();
302                         terr_type = nav.Evaluate ("string (ldml/identity/territory/@type)").ToString ();
303
304                         ci.Language = (lang_type == String.Empty ? null : lang_type);
305                         ci.Territory = (terr_type == String.Empty ? null : terr_type);
306
307                         if (!LookupLcids (ci, true))
308                                 return false;
309
310                         doc = GetXPathDocument (Path.Combine ("langs", GetShortName (Lang) + ".xml"));
311                         nav = doc.CreateNavigator ();
312                         ci.DisplayName = LookupFullName (ci, nav);
313                         
314                         if (Lang == "en") {
315                                 ci.EnglishName = ci.DisplayName;
316                         } else {
317                                 doc = GetXPathDocument (Path.Combine ("langs", GetShortName (lang) + ".xml"));
318                                 nav = doc.CreateNavigator ();
319                                 ci.EnglishName = LookupFullName (ci, nav);
320                         }
321
322                         if (ci.Language == Lang) {
323                                 ci.NativeName = ci.DisplayName;
324                         } else {
325                                 doc = GetXPathDocument (Path.Combine ("langs", GetShortName (lang) + ".xml"));
326                                 nav = doc.CreateNavigator ();
327                                 ci.NativeName = LookupFullName (ci, nav);
328                         }
329
330                         // Null these out because langs dont have them
331                         ci.DateTimeFormatEntry = null;
332                         ci.NumberFormatEntry = null;
333
334                         langs [lang] = ci;
335                         cultures.Add (ci);
336
337                         return true;
338                 }
339
340                 private void ParseLocale (string locale)
341                 {
342                         CultureInfoEntry ci;
343
344                         ci = LookupCulture (locale);
345
346                         if (ci == null)
347                                 return;
348
349                         if (langs [GetLanguageFixed (ci)] == null) {
350                                 if (!ParseLang (GetLanguageFixed (ci))) // If we can't parse the lang we cant have the locale
351                                         return;
352                         }
353
354                         cultures.Add (ci);
355                 }
356
357                 private CultureInfoEntry LookupCulture (string locale)
358                 {
359                         string path = Path.Combine ("locales", locale + ".xml");
360                         if (!File.Exists (path))
361                                 return null;
362                         XPathDocument doc = GetXPathDocument (path);
363                         XPathNavigator nav = doc.CreateNavigator ();
364                         CultureInfoEntry ci = new CultureInfoEntry ();
365                         string supp;
366
367 //                        ci.Name = locale; // TODO: Some of these need to be mapped.
368
369                         // First thing we do is get the lang-territory combo, lcid, and full names
370                         ci.Language = nav.Evaluate ("string (ldml/identity/language/@type)").ToString ();
371                         ci.Territory = nav.Evaluate ("string (ldml/identity/territory/@type)").ToString ();
372
373                         if (!LookupLcids (ci, false))
374                                 return null;
375                         LookupNames (ci);
376
377                         /**
378                          * Locale generation is done in six steps, first we
379                          * read the root file which is the base invariant data
380                          * then the supplemental root data, 
381                          * then the language file, the supplemental languages
382                          * file then the locale file, then the supplemental
383                          * locale file. Values in each descending file can
384                          * overwrite previous values.
385                          */
386                         doc = GetXPathDocument (Path.Combine ("langs", "root.xml"));
387                         nav = doc.CreateNavigator ();
388                         Lookup (nav, ci);
389
390                         doc = GetXPathDocument (Path.Combine ("supp", "root.xml"));
391                         nav = doc.CreateNavigator ();
392                         Lookup (nav, ci);
393
394                         doc = GetXPathDocument (Path.Combine ("langs", GetShortName (GetLanguageFixed (ci)) + ".xml"));
395                         nav = doc.CreateNavigator ();
396                         Lookup (nav, ci);
397
398                         supp = Path.Combine ("supp", GetLanguageFixed (ci) + ".xml");
399                         if (File.Exists (supp)) {
400                                 doc = GetXPathDocument (supp);
401                                 nav = doc.CreateNavigator ();
402                                 Lookup (nav, ci);
403                         }
404                         
405                         doc = GetXPathDocument (Path.Combine ("locales", locale + ".xml"));
406                         nav = doc.CreateNavigator ();
407                         Lookup (nav, ci);
408
409                         supp = Path.Combine ("supp", locale + ".xml");
410                         if (File.Exists (supp)) {
411                                 doc = GetXPathDocument (supp);
412                                 nav = doc.CreateNavigator ();
413                                 Lookup (nav, ci);
414                         }
415
416                         return ci;
417                 }
418
419                 private void Lookup (XPathNavigator nav, CultureInfoEntry ci)
420                 {
421                         LookupDateTimeInfo (nav, ci);
422                         LookupNumberInfo (nav, ci);
423                 }
424
425                 private string GetLanguageFixed (CultureInfoEntry ci)
426                 {
427                         // This is a hack, but without it nb-NO and nn-NO won't work.
428                         if (ci.Territory == "NO") {
429                                 switch (ci.Language) {
430                                 case "nb":
431                                 case "nn":
432                                         return "no";
433                                 }
434                         }
435                         return ci.Language;
436                 }
437
438                 private void LookupNames (CultureInfoEntry ci)
439                 {
440                         XPathDocument doc = GetXPathDocument (Path.Combine ("langs", GetShortName (Lang) + ".xml"));
441                         XPathNavigator nav = doc.CreateNavigator ();
442
443                         ci.DisplayName = LookupFullName (ci, nav);
444                         
445                         if (Lang == "en") {
446                                 ci.EnglishName = ci.DisplayName;
447                         } else {
448                                 doc = GetXPathDocument (Path.Combine ("langs", "en.xml"));
449                                 nav = doc.CreateNavigator ();
450                                 ci.EnglishName = LookupFullName (ci, nav);
451                         }
452
453                         if (ci.Language == Lang) {
454                                 ci.NativeName = ci.DisplayName;
455                         } else {
456                                 // FIXME: We use ci.Language here.
457                                 // This is nothing more than hack for nb-NO and nn-NO
458                                 // where Parent of them is nn (not nb or nn).
459                                 string lang = ci.Language;
460                                 doc = GetXPathDocument (Path.Combine ("langs", GetShortName (lang) + ".xml"));
461                                 nav = doc.CreateNavigator ();
462                                 ci.NativeName = LookupFullName (ci, nav);
463                         }
464                 }
465
466                 private void AddPattern (ArrayList al, string pattern)
467                 {
468                         if (!al.Contains (pattern))
469                                 al.Add (pattern);
470                 }
471
472                 private void LookupDateTimeInfo (XPathNavigator nav, CultureInfoEntry ci)
473                 {
474                         /**
475                          * TODO: Does anyone have multiple calendars?
476                          */
477                         XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("ldml/dates/calendars/calendar");
478
479                         while (ni.MoveNext ()) {
480                                 DateTimeFormatEntry df = ci.DateTimeFormatEntry;
481                                 string cal_type = ni.Current.GetAttribute ("type", String.Empty);
482
483                                 if (cal_type != String.Empty)
484                                         df.CalendarType = cal_type;
485
486                                 XPathNodeIterator ni2 = (XPathNodeIterator) ni.Current.Evaluate ("optionalCalendars/calendar");
487                                 int opt_cal_count = 0;
488                                 while (ni2.MoveNext ()) {
489                                         int type;
490                                         string greg_type_str;
491                                         XPathNavigator df_nav = ni2.Current;
492                                         switch (df_nav.GetAttribute ("type", String.Empty)) {
493                                         case "Gregorian":
494                                                 type = 0;
495                                                 break;
496                                         case "Hijri":
497                                                 type = 0x01;
498                                                 break;
499                                         case "ThaiBuddhist":
500                                                 type = 0x02;
501                                                 break;
502                                         default:
503                                                 Console.WriteLine ("unknown calendar type:  " +
504                                                                 df_nav.GetAttribute ("type", String.Empty));
505                                                 continue;
506                                         }
507                                         type <<= 24;
508                                         greg_type_str = df_nav.GetAttribute ("greg_type", String.Empty);
509                                         if (greg_type_str != null && greg_type_str != String.Empty) {
510                                                 GregorianCalendarTypes greg_type = (GregorianCalendarTypes)
511                                                         Enum.Parse (typeof (GregorianCalendarTypes), greg_type_str);
512                                                 int greg_type_int = (int) greg_type;
513                                                 type |= greg_type_int;
514                                                 
515                                         }
516                                         Console.WriteLine ("Setting cal type: {0:X}  for   {1}", type, ci.Name);
517                                         ci.CalendarData [opt_cal_count++] = type;
518                                 }
519                                 
520                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("monthNames/month");
521                                 while (ni2.MoveNext ()) {
522                                         if (ni2.CurrentPosition == 1)
523                                                 df.MonthNames.Clear ();
524                                         df.MonthNames.Add (ni2.Current.Value);
525                                 }
526                                 if (df.MonthNames.Count == 12)
527                                         df.MonthNames.Add (String.Empty);
528
529                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dayNames/day");
530                                 while (ni2.MoveNext ()) {
531                                         if (ni2.CurrentPosition == 1)
532                                                 df.DayNames.Clear ();
533                                         df.DayNames.Add (ni2.Current.Value);
534                                 }
535
536                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dayAbbr/day");
537                                 while (ni2.MoveNext ()) {
538                                         if (ni2.CurrentPosition == 1)
539                                                 df.AbbreviatedDayNames.Clear ();
540                                         df.AbbreviatedDayNames.Add (ni2.Current.Value);
541                                 }
542
543                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("monthAbbr/month");
544                                 while (ni2.MoveNext ()) {
545                                         if (ni2.CurrentPosition == 1)
546                                                 df.AbbreviatedMonthNames.Clear ();
547                                         df.AbbreviatedMonthNames.Add (ni2.Current.Value);
548                                 }
549                                 if (df.AbbreviatedMonthNames.Count == 12)
550                                         df.AbbreviatedMonthNames.Add (String.Empty);
551
552                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dateFormats/dateFormatLength");
553                                 while (ni2.MoveNext ()) {
554                                         XPathNavigator df_nav = ni2.Current;
555                                         XPathNodeIterator p = df_nav.Select ("dateFormat/pattern");
556                                         string value = null;
557                                         if (p.MoveNext ())
558                                                 value = p.Current.Value;
559                                         XPathNodeIterator ext = null;
560                                         switch (df_nav.GetAttribute ("type", String.Empty)) {
561                                         case "full":
562                                                 if (value != null)
563                                                         ParseFullDateFormat (df, value);
564                                                 break;
565                                         case "long":
566                                                 if (value != null)
567                                                         df.LongDatePattern = value;
568                                                 ext = df_nav.Select ("extraPatterns/pattern");
569                                                 if (ext.MoveNext ()) {
570                                                         df.LongDatePatterns.Clear ();
571                                                         AddPattern (df.LongDatePatterns, df.LongDatePattern);
572                                                         do {
573                                                                 df.LongDatePatterns.Add (ext.Current.Value);
574                                                         } while (ext.MoveNext ());
575                                                 }
576                                                 else
577                                                         AddPattern (df.LongDatePatterns, df.LongDatePattern);
578                                                 break;
579                                         case "short":
580                                                 if (value != null)
581                                                         df.ShortDatePattern = value;
582                                                 ext = df_nav.Select ("extraPatterns/pattern");
583                                                 if (ext.MoveNext ()) {
584                                                         df.ShortDatePatterns.Clear ();
585                                                         AddPattern (df.ShortDatePatterns, df.ShortDatePattern);
586                                                         do {
587                                                                 df.ShortDatePatterns.Add (ext.Current.Value);
588                                                         } while (ext.MoveNext ());
589                                                 }
590                                                 else
591                                                         AddPattern (df.ShortDatePatterns, df.ShortDatePattern);
592                                                 break;
593                                         case "year_month":
594                                                 if (value != null)
595                                                         df.YearMonthPattern = value;
596                                                 break;
597                                         case "month_day":
598                                                 if (value != null)
599                                                         df.MonthDayPattern = value;
600                                                 break;
601                                         }
602                                 }
603
604                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("timeFormats/timeFormatLength");
605                                 while (ni2.MoveNext ()) {
606                                         XPathNavigator df_nav = ni2.Current;
607                                         XPathNodeIterator p = df_nav.Select ("timeFormat/pattern");
608                                         string value = null;
609                                         if (p.MoveNext ())
610                                                 value = p.Current.Value;
611                                         XPathNodeIterator ext = null;
612                                         switch (df_nav.GetAttribute ("type", String.Empty)) {
613                                         case "long":
614                                                 if (value != null)
615                                                         df.LongTimePattern = value.Replace ('a', 't');
616                                                 ext = df_nav.Select ("extraPatterns/pattern");
617                                                 if (ext.MoveNext ()) {
618                                                         df.LongTimePatterns.Clear ();
619                                                         AddPattern (df.LongTimePatterns, df.LongTimePattern);
620                                                         do {
621                                                                 df.LongTimePatterns.Add (ext.Current.Value);
622                                                         } while (ext.MoveNext ());
623                                                 }
624                                                 else
625                                                         AddPattern (df.LongTimePatterns, df.LongTimePattern);
626                                                 break;
627                                         case "short":
628                                                 if (value != null)
629                                                         df.ShortTimePattern = value.Replace ('a', 't');
630                                                 ext = df_nav.Select ("extraPatterns/pattern");
631                                                 if (ext.MoveNext ()) {
632                                                         df.ShortTimePatterns.Clear ();
633                                                         AddPattern (df.ShortTimePatterns, df.ShortTimePattern);
634                                                         do {
635                                                                 df.ShortTimePatterns.Add (ext.Current.Value);
636                                                         } while (ext.MoveNext ());
637                                                 }
638                                                 else
639                                                         AddPattern (df.ShortTimePatterns, df.ShortTimePattern);
640                                                 break;
641                                         }
642                                 }
643
644                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dateTimeFormats/dateTimeFormatLength/dateTimeFormat/pattern");
645                                 if (ni2.MoveNext ())
646                                         df.RawFullDateTimePattern = ni2.Current.ToString ();/*String.Format (ni2.Current.ToString (),
647                                                         df.LongTimePattern, df.LongDatePattern);*/
648
649                                 XPathNodeIterator am = ni.Current.SelectChildren ("am", "");
650                                 if (am.MoveNext ())
651                                         df.AMDesignator = am.Current.Value;
652                                 XPathNodeIterator pm = ni.Current.SelectChildren ("pm", "");
653                                 if (pm.MoveNext ())
654                                         df.PMDesignator = pm.Current.Value;
655 /*
656                                 string am = (string) ni.Current.Evaluate ("string(am)");
657                                 string pm = (string) ni.Current.Evaluate ("string(pm)");
658
659                                 if (am != String.Empty)
660                                         df.AMDesignator = am;
661                                 if (pm != String.Empty)
662                                         df.PMDesignator = pm;
663 */
664                         }
665
666                         string date_sep = (string) nav.Evaluate ("string(ldml/dates/symbols/dateSeparator)");
667                         string time_sep = (string) nav.Evaluate ("string(ldml/dates/symbols/timeSeparator)");
668
669                         if (date_sep != String.Empty)
670                                 ci.DateTimeFormatEntry.DateSeparator = date_sep;
671                         if (time_sep != String.Empty)
672                                 ci.DateTimeFormatEntry.TimeSeparator = time_sep;
673                 }
674
675                 private void LookupNumberInfo (XPathNavigator nav, CultureInfoEntry ci)
676                 {
677                         XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("ldml/numbers");
678
679                         while (ni.MoveNext ()) {
680                                 LookupNumberSymbols (ni.Current, ci);
681                                 LookupDecimalFormat (ni.Current, ci);
682                                 LookupPercentFormat (ni.Current, ci);
683                                 LookupCurrencyFormat (ni.Current, ci);
684                                 LookupCurrencySymbol (ni.Current, ci);
685                         }
686                 }
687
688                 private void LookupDecimalFormat (XPathNavigator nav, CultureInfoEntry ci)
689                 {
690                         string format = (string) nav.Evaluate ("string(decimalFormats/" +
691                                         "decimalFormatLength/decimalFormat/pattern)");
692
693                         if (format == String.Empty)
694                                 return;
695
696                         string [] part_one, part_two;
697                         string [] pos_neg = format.Split (new char [1] {';'}, 2);
698
699                         // Most of the patterns are common in positive and negative
700                         if (pos_neg.Length == 1)
701                                 pos_neg = new string [] {pos_neg [0], pos_neg [0]};
702
703                         if (pos_neg.Length == 2) {
704                                 
705                                 part_one = pos_neg [0].Split (new char [1] {'.'}, 2);
706                                 if (part_one.Length == 1)
707                                         part_one = new string [] {part_one [0], String.Empty};
708
709                                 if (part_one.Length == 2) {
710                                         // assumed same for both positive and negative
711                                         // decimal digit side
712                                         ci.NumberFormatEntry.NumberDecimalDigits = 0;                                   
713                                         for (int i = 0; i < part_one [1].Length; i++) {
714                                                 if (part_one [1][i] == '#') {
715                                                         ci.NumberFormatEntry.NumberDecimalDigits ++;
716                                                 } else
717                                                         break;                                                          }
718                                         // FIXME: This should be actually done by modifying culture xml files, but too many files to be modified.
719                                         if (ci.NumberFormatEntry.NumberDecimalDigits > 0)
720                                                 ci.NumberFormatEntry.NumberDecimalDigits --;
721
722                                         // decimal grouping side
723                                         part_two = part_one [0].Split (',');
724                                         if (part_two.Length > 1) {
725                                                 int len = part_two.Length - 1;
726                                                 ci.NumberFormatEntry.NumberGroupSizes = new int [len];
727                                                 for (int i = 0; i < len; i++) {
728                                                         string pat = part_two [i + 1];
729                                                         ci.NumberFormatEntry.NumberGroupSizes [i] = pat.Length;
730                                                 }
731                                         } else {
732                                                 ci.NumberFormatEntry.NumberGroupSizes = new int [1] { 3 };
733                                         }
734
735                                         if (pos_neg [1].StartsWith ("(") && pos_neg [1].EndsWith (")")) {
736                                                 ci.NumberFormatEntry.NumberNegativePattern = 0;
737                                         } else if (pos_neg [1].StartsWith ("- ")) {
738                                                 ci.NumberFormatEntry.NumberNegativePattern = 2;
739                                         } else if (pos_neg [1].StartsWith ("-")) {
740                                                 ci.NumberFormatEntry.NumberNegativePattern = 1;
741                                         } else if (pos_neg [1].EndsWith (" -")) {
742                                                 ci.NumberFormatEntry.NumberNegativePattern = 4;
743                                         } else if (pos_neg [1].EndsWith ("-")) {
744                                                 ci.NumberFormatEntry.NumberNegativePattern = 3;
745                                         } else {
746                                                 ci.NumberFormatEntry.NumberNegativePattern = 1;
747                                         }
748                                 }
749                         }
750                 }
751
752                 private void LookupPercentFormat (XPathNavigator nav, CultureInfoEntry ci)
753                 {
754                         string format = (string) nav.Evaluate ("string(percentFormats/" +
755                                         "percentFormatLength/percentFormat/pattern)");
756
757                         if (format == String.Empty)
758                                 return;
759
760                         string [] part_one, part_two;
761
762                         // we don't have percentNegativePattern in CLDR so 
763                         // the percentNegativePattern are just guesses
764                         if (format.StartsWith ("%")) {
765                                 ci.NumberFormatEntry.PercentPositivePattern = 2;
766                                 ci.NumberFormatEntry.PercentNegativePattern = 2;
767                                 format = format.Substring (1);
768                         } else if (format.EndsWith (" %")) {
769                                 ci.NumberFormatEntry.PercentPositivePattern = 0;
770                                 ci.NumberFormatEntry.PercentNegativePattern = 0;
771                                 format = format.Substring (0, format.Length - 2);
772                         } else if (format.EndsWith ("%")) {
773                                 ci.NumberFormatEntry.PercentPositivePattern = 1;
774                                 ci.NumberFormatEntry.PercentNegativePattern = 1;
775                                 format = format.Substring (0, format.Length - 1);
776                         } else {
777                                 ci.NumberFormatEntry.PercentPositivePattern = 0;
778                                 ci.NumberFormatEntry.PercentNegativePattern = 0;
779                         }
780
781                         part_one = format.Split (new char [1] {'.'}, 2);
782                         if (part_one.Length == 2) {
783                                 // assumed same for both positive and negative
784                                 // decimal digit side
785                                 ci.NumberFormatEntry.PercentDecimalDigits = 0;
786                                 for (int i = 0; i < part_one [1].Length; i++) {
787                                         if (part_one [1][i] == '#')
788                                                 ci.NumberFormatEntry.PercentDecimalDigits++;
789                                         else
790                                                 break;
791                                 }
792                         }
793
794                         if (part_one.Length > 0) {
795                                 // percent grouping side
796                                 part_two = part_one [0].Split (',');
797                                 if (part_two.Length > 1) {
798                                         int len = part_two.Length - 1;
799                                         ci.NumberFormatEntry.PercentGroupSizes = new int [len];
800                                         for (int i = 0; i < len; i++) {
801                                                 string pat = part_two [i + 1];
802                                                 if (pat [pat.Length -1] == '0')
803                                                         ci.NumberFormatEntry.PercentDecimalDigits = pat.Length - 1;
804                                                 ci.NumberFormatEntry.PercentGroupSizes [i] = pat.Length;
805                                         }
806                                 } else {
807                                         ci.NumberFormatEntry.PercentGroupSizes = new int [1] { 3 };
808                                         ci.NumberFormatEntry.PercentDecimalDigits = 2;
809                                 }
810                         }
811                 }
812
813                 private void LookupCurrencyFormat (XPathNavigator nav, CultureInfoEntry ci)
814                 {
815                         string format = (string) nav.Evaluate ("string(currencyFormats/" +
816                                         "currencyFormatLength/currencyFormat/pattern)");
817
818                         if (format == String.Empty)
819                                 return;
820
821                         string [] part_one, part_two;
822                         string [] pos_neg = format.Split (new char [1] {';'}, 2);
823         
824                         // Most of the patterns are common in positive and negative
825                         if (pos_neg.Length == 1)
826                                 pos_neg = new string [] {pos_neg [0], pos_neg [0]};
827
828                         if (pos_neg.Length == 2) {
829                                 part_one = pos_neg [0].Split (new char [1] {'.'}, 2);
830                                 if (part_one.Length == 1)
831                                         part_one = new string [] {part_one [0], String.Empty};
832                                 if (part_one.Length == 2) {
833                                         // assumed same for both positive and negative
834                                         // decimal digit side
835                                         ci.NumberFormatEntry.CurrencyDecimalDigits = 0;
836                                         for (int i = 0; i < part_one [1].Length; i++) {
837                                                 if (part_one [1][i] == '0')
838                                                         ci.NumberFormatEntry.CurrencyDecimalDigits++;
839                                                 else
840                                                         break;
841                                         }
842
843                                         // decimal grouping side
844                                         part_two = part_one [0].Split (',');
845                                         if (part_two.Length > 1) {
846                                                 int len = part_two.Length - 1;
847                                                 ci.NumberFormatEntry.CurrencyGroupSizes = new int [len];
848                                                 for (int i = 0; i < len; i++) {
849                                                         string pat = part_two [i + 1];
850                                                         ci.NumberFormatEntry.CurrencyGroupSizes [i] = pat.Length;
851                                                 }
852                                         } else {
853                                                 ci.NumberFormatEntry.CurrencyGroupSizes = new int [1] { 3 };
854                                         }
855
856                                         if (pos_neg [1].StartsWith ("(\u00a4 ") && pos_neg [1].EndsWith (")")) {
857                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 14;
858                                         } else if (pos_neg [1].StartsWith ("(\u00a4") && pos_neg [1].EndsWith (")")) {
859                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 0;
860                                         } else if (pos_neg [1].StartsWith ("\u00a4 ") && pos_neg [1].EndsWith ("-")) {
861                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 11;
862                                         } else if (pos_neg [1].StartsWith ("\u00a4") && pos_neg [1].EndsWith ("-")) {
863                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 3;
864                                         } else if (pos_neg [1].StartsWith ("(") && pos_neg [1].EndsWith (" \u00a4")) {
865                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 15;
866                                         } else if (pos_neg [1].StartsWith ("(") && pos_neg [1].EndsWith ("\u00a4")) {
867                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 4;
868                                         } else if (pos_neg [1].StartsWith ("-") && pos_neg [1].EndsWith (" \u00a4")) {
869                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 8;
870                                         } else if (pos_neg [1].StartsWith ("-") && pos_neg [1].EndsWith ("\u00a4")) {
871                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 5;
872                                         } else if (pos_neg [1].StartsWith ("-\u00a4 ")) {
873                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 9;
874                                         } else if (pos_neg [1].StartsWith ("-\u00a4")) {
875                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 1;
876                                         } else if (pos_neg [1].StartsWith ("\u00a4 -")) {
877                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 12;
878                                         } else if (pos_neg [1].StartsWith ("\u00a4-")) {
879                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 2;
880                                         } else if (pos_neg [1].EndsWith (" \u00a4-")) {
881                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 10;
882                                         } else if (pos_neg [1].EndsWith ("\u00a4-")) {
883                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 7;
884                                         } else if (pos_neg [1].EndsWith ("- \u00a4")) {
885                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 13;
886                                         } else if (pos_neg [1].EndsWith ("-\u00a4")) {
887                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 6;
888                                         } else {
889                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 0;
890                                         }
891                                         
892                                         if (pos_neg [0].StartsWith ("\u00a4 ")) {
893                                                 ci.NumberFormatEntry.CurrencyPositivePattern = 2;
894                                         } else if (pos_neg [0].StartsWith ("\u00a4")) {
895                                                 ci.NumberFormatEntry.CurrencyPositivePattern = 0;
896                                         } else if (pos_neg [0].EndsWith (" \u00a4")) {
897                                                 ci.NumberFormatEntry.CurrencyPositivePattern = 3;
898                                         } else if (pos_neg [0].EndsWith ("\u00a4")) {
899                                                 ci.NumberFormatEntry.CurrencyPositivePattern = 1; 
900                                         } else {
901                                                 ci.NumberFormatEntry.CurrencyPositivePattern = 0;
902                                         }
903                                 }
904                         }
905                 }
906
907                 private void LookupNumberSymbols (XPathNavigator nav, CultureInfoEntry ci)
908                 {
909                         string dec = (string) nav.Evaluate ("string(symbols/decimal)");
910                         string group = (string) nav.Evaluate ("string(symbols/group)");
911                         string percent = (string) nav.Evaluate ("string(symbols/percentSign)");
912                         string positive = (string) nav.Evaluate ("string(symbols/plusSign)");
913                         string negative = (string) nav.Evaluate ("string(symbols/minusSign)");
914                         string per_mille = (string) nav.Evaluate ("string(symbols/perMille)");
915                         string infinity = (string) nav.Evaluate ("string(symbols/infinity)");
916                         string nan = (string) nav.Evaluate ("string(symbols/nan)");
917
918                         if (dec != String.Empty) {
919                                 ci.NumberFormatEntry.NumberDecimalSeparator = dec;
920                                 ci.NumberFormatEntry.PercentDecimalSeparator = dec;
921                                 ci.NumberFormatEntry.CurrencyDecimalSeparator = dec;
922                         }
923
924                         if (group != String.Empty) {
925                                 ci.NumberFormatEntry.NumberGroupSeparator = group;
926                                 ci.NumberFormatEntry.PercentGroupSeparator = group;
927                                 ci.NumberFormatEntry.CurrencyGroupSeparator = group;
928                         }
929
930                         if (percent != String.Empty)
931                                 ci.NumberFormatEntry.PercentSymbol = percent;
932                         if (positive != String.Empty)
933                                 ci.NumberFormatEntry.PositiveSign = positive;
934                         if (negative != String.Empty)
935                                 ci.NumberFormatEntry.NegativeSign = negative;
936                         if (per_mille != String.Empty)
937                                 ci.NumberFormatEntry.PerMilleSymbol = per_mille;
938                         if (infinity != String.Empty)
939                                 ci.NumberFormatEntry.PositiveInfinitySymbol = infinity;
940                         if (nan != String.Empty)
941                                 ci.NumberFormatEntry.NaNSymbol = nan;
942                 }
943
944                 private void LookupCurrencySymbol (XPathNavigator nav, CultureInfoEntry ci)
945                 {
946                         string type = currency_types [ci.Territory] as string;
947
948                         if (type == null) {
949                                 Console.WriteLine ("no currency type for:  " + ci.Territory);
950                                 return;
951                         }
952                         
953                         string cur = (string) nav.Evaluate ("string(currencies/currency [@type='" +
954                                         type + "']/symbol)");
955
956                         if (cur != String.Empty)
957                                 ci.NumberFormatEntry.CurrencySymbol = cur;
958                 }
959
960                 private bool LookupLcids (CultureInfoEntry ci, bool lang)
961                 {
962                         XPathNavigator nav = lcids_doc.CreateNavigator ();
963                         string name = ci.Name;
964                         // Language name does not always consist of locale name.
965                         // (for zh-* it must be either zh-CHS or zh-CHT)
966                         string langName = GetLanguageFixed (ci);
967
968 //                        if (ci.Territory != null)
969 //                                name += "-" + ci.Territory;
970
971                         XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("lcids/lcid[@name='"
972                                         + (lang ? langName : name) + "']");
973                         if (!ni.MoveNext ()) {
974                                 Console.WriteLine ("no lcid found for: {0} ({1}/{2})", name, ci.Language, ci.Territory);
975                                 string file;
976
977                                 if (ci.Territory != null) {
978                                         file = Path.Combine ("locales", ci.Language + "_" + ci.Territory + ".xml");
979                                         Console.WriteLine ("deleting file:  " + file);
980                                         File.Delete (file);
981                                 }
982
983                                 return false;
984                         }
985
986                         string id = ni.Current.GetAttribute ("id", String.Empty);
987                         string parent = ni.Current.GetAttribute ("parent", String.Empty);
988                         string specific = ni.Current.GetAttribute ("specific", String.Empty);
989                         string iso2 = ni.Current.GetAttribute ("iso2", String.Empty);
990                         string iso3 = ni.Current.GetAttribute ("iso3", String.Empty);
991                         string win = ni.Current.GetAttribute ("win", String.Empty);
992                         string icu = ni.Current.GetAttribute ("icu_name", String.Empty);
993
994                         // lcids are in 0x<hex> format
995                         ci.Lcid = id;
996                         ci.ParentLcid = parent;
997                         ci.SpecificLcid = specific;
998                         ci.ISO2Lang = iso2;
999                         ci.ISO3Lang = iso3;
1000                         ci.Win3Lang = win;
1001                         ci.IcuName = icu;
1002                         
1003                         ci.TextInfoEntry = new TextInfoEntry (int.Parse (id.Substring (2), NumberStyles.HexNumber), GetXPathDocument ("textinfo.xml"));
1004
1005                         return true;
1006                 }
1007                 
1008                 private string LookupFullName (CultureInfoEntry ci, XPathNavigator nav)
1009                 {
1010                         string pre = "ldml/localeDisplayNames/";
1011                         string ret;
1012
1013                         // FIXME: We use ci.Language here.
1014                         // This is nothing more than hack for nb-NO or nn-NO
1015                         // where Parent of them is nn (not nb or nn).
1016                         ret = (string) nav.Evaluate ("string("+
1017                                         pre + "languages/language[@type='" + GetShortName (ci.Language) + "'])");
1018
1019                         if (ci.Territory == null)
1020                                 return ret;
1021                         ret += " (" + (string) nav.Evaluate ("string("+
1022                                         pre + "territories/territory[@type='" + ci.Territory + "'])") + ")";
1023
1024                         return ret;
1025                 }
1026
1027                 private void LookupRegions ()
1028                 {
1029                         XPathDocument doc = GetXPathDocument ("supplementalData.xml");
1030                         XPathNavigator nav = doc.CreateNavigator ();
1031                         XPathNodeIterator ni = nav.Select ("supplementalData/currencyData/region");
1032                         while (ni.MoveNext ()) {
1033                                 string territory = (string) ni.Current.GetAttribute ("iso3166", String.Empty);
1034                                 string currency = (string) ni.Current.Evaluate ("string(currency/@iso4217)");
1035                                 RegionInfoEntry region = new RegionInfoEntry ();
1036                                 region.ISO2Name = territory.ToUpper ();
1037                                 region.ISOCurrencySymbol = currency;
1038                                 regions [territory] = region;
1039                         }
1040
1041                         doc = GetXPathDocument ("langs/en.xml");
1042                         nav = doc.CreateNavigator ();
1043                         ni = nav.Select ("/ldml/localeDisplayNames/territories/territory");
1044                         while (ni.MoveNext ()) {
1045                                 RegionInfoEntry r = (RegionInfoEntry)
1046                                         regions [ni.Current.GetAttribute ("type", "")];
1047                                 if (r == null)
1048                                         continue;
1049                                 r.EnglishName = ni.Current.Value;
1050                         }
1051
1052                         Hashtable curNames = new Hashtable ();
1053                         ni = nav.Select ("/ldml/numbers/currencies/currency");
1054                         while (ni.MoveNext ())
1055                                 curNames [ni.Current.GetAttribute ("type", "")] =
1056                                         ni.Current.Evaluate ("string (displayName)");
1057
1058                         foreach (RegionInfoEntry r in regions.Values)
1059                                 r.CurrencyEnglishName =
1060                                         (string) curNames [r.ISOCurrencySymbol];
1061                 }
1062
1063                 private void LookupCurrencyTypes ()
1064                 {
1065                         XPathDocument doc = GetXPathDocument ("supplementalData.xml");
1066                         XPathNavigator nav = doc.CreateNavigator ();
1067
1068                         currency_types = new Hashtable ();
1069
1070                         XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("supplementalData/currencyData/region");
1071                         while (ni.MoveNext ()) {
1072                                 string territory = (string) ni.Current.GetAttribute ("iso3166", String.Empty);
1073                                 string currency = (string) ni.Current.Evaluate ("string(currency/@iso4217)");
1074                                 currency_types [territory] = currency;
1075                         }
1076                 }
1077
1078                 static string control_chars = "eghmsftz";
1079
1080                 // HACK: We are trying to build year_month and month_day patterns from the full pattern.
1081                 private void ParseFullDateFormat (DateTimeFormatEntry df, string full)
1082                 {
1083                         
1084                         string month_day = String.Empty;
1085                         string year_month = String.Empty;
1086                         bool in_month_data = false;
1087                         bool in_year_data = false;
1088                         int day_start = 0, day_end = 0;
1089                         int month_start = 0, month_end = 0;
1090                         int year_start = 0, year_end = 0;
1091                         bool inquote = false;
1092                         
1093                         for (int i = 0; i < full.Length; i++) {
1094                                 char c = full [i];
1095                                 if (!inquote && c == 'M') {
1096                                         month_day += c;
1097                                         year_month += c;
1098                                         in_year_data = true;
1099                                         in_month_data = true;
1100                                         if (month_start == 0)
1101                                                 month_start = i;
1102                                         month_end = i;
1103                                         year_end = year_month.Length;
1104                                 } else if (!inquote && Char.ToLower (c) == 'd') {
1105                                         month_day += c;
1106                                         in_month_data = true;
1107                                         in_year_data = false;
1108                                         if (day_start == 0)
1109                                                 day_start = i;
1110                                         day_end = i;
1111                                 } else if (!inquote && Char.ToLower (c) == 'y') {
1112                                         year_month += c;
1113                                         in_year_data = true;
1114                                         in_month_data = false;
1115                                         if (year_start == 0)
1116                                                 year_start = i;
1117                                         year_end = i;
1118                                 } else if (!inquote && control_chars.IndexOf (Char.ToLower (c)) >= 0) {
1119                                         in_year_data = false;
1120                                         in_month_data = false;
1121                                 } else if (in_year_data || in_month_data) {
1122                                         if (in_month_data)
1123                                                 month_day += c;
1124                                         if (in_year_data)
1125                                                 year_month += c;
1126                                 }
1127
1128                                 if (c == '\'') {
1129                                         inquote = !inquote;
1130                                 }
1131                         }
1132
1133                         if (month_day != String.Empty) {
1134                                 //month_day = month_day.Substring (0, month_end);
1135                                 df.MonthDayPattern = TrimPattern (month_day);
1136                         }
1137                         if (year_month != String.Empty) {
1138                                 //year_month = year_month.Substring (0, year_end);
1139                                 df.YearMonthPattern = TrimPattern (year_month);
1140                         }
1141                 }
1142
1143                 string TrimPattern (string p)
1144                 {
1145                         int idx = 0;
1146                         p = p.Trim ().TrimEnd (',');
1147                         idx = p.LastIndexOf ("' de '"); // spanish dates
1148                         if (idx > 0)
1149                                 p = p.Substring (0, idx);
1150                         idx = p.LastIndexOf ("' ta '"); // finnish
1151                         if (idx > 0)
1152                                 p = p.Substring (0, idx);
1153                         idx = p.LastIndexOf ("'ren'"); // euskara
1154                         if (idx > 0)
1155                                 p = p.Replace ("'ren'", "").Trim ();
1156                         idx = p.LastIndexOf ("'a'"); // estonian
1157                         if (idx > 0)
1158                                 p = p.Substring (0, idx);
1159
1160                         return p.Replace ("'ta '", "'ta'"); // finnish
1161                 }
1162
1163                 private class LcidComparer : IComparer {
1164
1165                         public int Compare (object a, object b)
1166                         {
1167                                 CultureInfoEntry aa = (CultureInfoEntry) a;
1168                                 CultureInfoEntry bb = (CultureInfoEntry) b;
1169                         
1170                                 return aa.Lcid.CompareTo (bb.Lcid);
1171                         }                
1172                 }
1173
1174                 private class NameComparer : IComparer {
1175
1176                         public int Compare (object a, object b)
1177                         {
1178                                 CultureInfoEntry aa = (CultureInfoEntry) a;
1179                                 CultureInfoEntry bb = (CultureInfoEntry) b;
1180
1181                                 return String.CompareOrdinal(aa.Name.ToLower (), bb.Name.ToLower ());
1182                         }
1183                 }
1184
1185                 class RegionComparer : IComparer
1186                 {
1187                         public static RegionComparer Instance = new RegionComparer ();
1188                         
1189                         public int Compare (object o1, object o2)
1190                         {
1191                                 RegionInfoEntry r1 = (RegionInfoEntry) o1;
1192                                 RegionInfoEntry r2 = (RegionInfoEntry) o2;
1193                                 return String.CompareOrdinal (
1194                                         r1.ISO2Name, r2.ISO2Name);
1195                         }
1196                 }
1197
1198                 class RegionLCIDMap
1199                 {
1200                         public RegionLCIDMap (int lcid, int regionId)
1201                         {
1202                                 LCID = lcid;
1203                                 RegionId = regionId;
1204                         }
1205
1206                         public int LCID;
1207                         public int RegionId;
1208                 }
1209         }
1210 }
1211
1212