2 // Mono.Tools.LocalBuilder.Driver
5 // Jackson Harper (jackson@ximian.com)
7 // (C) 2004 Novell, Inc (http://www.novell.com)
15 using System.Xml.XPath;
16 using System.Collections;
17 using System.Globalization;
18 using System.Text.RegularExpressions;
20 namespace Mono.Tools.LocaleBuilder {
24 public static void Main (string [] args)
26 Driver d = new Driver ();
31 private static void ParseArgs (string [] args, Driver d)
33 for (int i = 0; i < args.Length; i++) {
34 if (args [i] == "--lang" && i+1 < args.Length)
36 else if (args [i] == "--locales" && i+1 < args.Length)
37 d.Locales = args [++i];
38 else if (args [i] == "--header" && i + 1 < args.Length)
39 d.HeaderFileName = args [++i];
44 private string locales;
45 private string header_name;
46 private ArrayList cultures;
47 private Hashtable langs;
48 private Hashtable currency_types;
50 // The lang is the language that display names will be displayed in
60 public string Locales {
61 get { return locales; }
62 set { locales = value; }
65 public string HeaderFileName {
67 if (header_name == null)
68 return "culture-info-tables.h";
71 set { header_name = value; }
76 Regex locales_regex = null;
78 locales_regex = new Regex (Locales);
80 langs = new Hashtable ();
81 cultures = new ArrayList ();
83 LookupCurrencyTypes ();
85 foreach (string file in Directory.GetFiles ("locales", "*.xml")) {
86 string fn = Path.GetFileNameWithoutExtension (file);
87 if (locales_regex == null || locales_regex.IsMatch (fn)) {
93 * Dump each table individually. Using StringBuilders
94 * because it is easier to debug, should switch to just
95 * writing to streams eventually.
97 using (StreamWriter writer = new StreamWriter (HeaderFileName, false, new UTF8Encoding (false, true))) {
98 writer.NewLine = "\n";
100 writer.WriteLine ("/* This is a generated file. Do not edit. See tools/locale-builder. */");
101 writer.WriteLine ("#ifndef MONO_METADATA_CULTURE_INFO_TABLES");
102 writer.WriteLine ("#define MONO_METADATA_CULTURE_INFO_TABLES 1");
103 writer.WriteLine ("\n");
105 writer.WriteLine ("#define NUM_CULTURE_ENTRIES " + cultures.Count);
106 writer.WriteLine ("\n");
108 // Sort the cultures by lcid
109 cultures.Sort (new LcidComparer ());
111 StringBuilder builder = new StringBuilder ();
113 int count = cultures.Count;
114 for (int i = 0; i < count; i++) {
115 CultureInfoEntry ci = (CultureInfoEntry) cultures [i];
116 if (ci.DateTimeFormatEntry == null)
118 ci.DateTimeFormatEntry.AppendTableRow (builder);
119 ci.DateTimeFormatEntry.Row = row++;
121 builder.Append (',');
122 builder.Append ('\n');
125 writer.WriteLine ("static const DateTimeFormatEntry datetime_format_entries [] = {");
126 writer.Write (builder);
127 writer.WriteLine ("};\n\n");
129 builder = new StringBuilder ();
131 for (int i=0; i < count; i++) {
132 CultureInfoEntry ci = (CultureInfoEntry) cultures [i];
133 if (ci.NumberFormatEntry == null)
135 ci.NumberFormatEntry.AppendTableRow (builder);
136 ci.NumberFormatEntry.Row = row++;
138 builder.Append (',');
139 builder.Append ('\n');
142 writer.WriteLine ("static const NumberFormatEntry number_format_entries [] = {");
143 writer.Write (builder);
144 writer.WriteLine ("};\n\n");
146 builder = new StringBuilder ();
148 for (int i = 0; i < count; i++) {
149 CultureInfoEntry ci = (CultureInfoEntry) cultures [i];
150 ci.AppendTableRow (builder);
153 builder.Append (',');
154 builder.Append ('\n');
157 writer.WriteLine ("static const CultureInfoEntry culture_entries [] = {");
158 writer.Write (builder);
159 writer.WriteLine ("};\n\n");
161 cultures.Sort (new NameComparer ()); // Sort based on name
162 builder = new StringBuilder ();
163 for (int i = 0; i < count; i++) {
164 CultureInfoEntry ci = (CultureInfoEntry) cultures [i];
165 builder.Append ("\t{" + Entry.EncodeStringIdx (ci.Name.ToLower ()) + ", ");
166 builder.Append (ci.Row + "}");
168 builder.Append (',');
169 builder.Append ('\n');
172 writer.WriteLine ("static const CultureInfoNameEntry culture_name_entries [] = {");
173 writer.Write (builder);
174 writer.WriteLine ("};\n\n");
176 writer.WriteLine ("static const char locale_strings [] = {");
177 writer.Write (Entry.GetStrings ());
178 writer.WriteLine ("};\n\n");
180 writer.WriteLine ("#endif\n");
184 private XPathDocument GetXPathDocument (string path)
186 XmlTextReader xtr = new XmlTextReader (path);
187 xtr.XmlResolver = null;
188 return new XPathDocument (xtr);
191 private string GetShortName (string lang)
193 return lang == "zh-CHS" ? "zh" : lang;
196 private bool ParseLang (string lang)
198 XPathDocument doc = GetXPathDocument (Path.Combine ("langs", GetShortName (lang) + ".xml"));
199 XPathNavigator nav = doc.CreateNavigator ();
200 CultureInfoEntry ci = new CultureInfoEntry ();
201 string lang_type, terr_type;
203 // ci.Name = lang; // TODO: might need to be mapped.
205 lang_type = nav.Evaluate ("string (ldml/identity/language/@type)").ToString ();
206 terr_type = nav.Evaluate ("string (ldml/identity/territory/@type)").ToString ();
208 ci.Language = (lang_type == String.Empty ? null : lang_type);
209 ci.Territory = (terr_type == String.Empty ? null : terr_type);
211 if (!LookupLcids (ci, true))
214 doc = GetXPathDocument (Path.Combine ("langs", GetShortName (Lang) + ".xml"));
215 nav = doc.CreateNavigator ();
216 ci.DisplayName = LookupFullName (ci, nav);
219 ci.EnglishName = ci.DisplayName;
221 doc = GetXPathDocument (Path.Combine ("langs", GetShortName (lang) + ".xml"));
222 nav = doc.CreateNavigator ();
223 ci.EnglishName = LookupFullName (ci, nav);
226 if (ci.Language == Lang) {
227 ci.NativeName = ci.DisplayName;
229 doc = GetXPathDocument (Path.Combine ("langs", GetShortName (lang) + ".xml"));
230 nav = doc.CreateNavigator ();
231 ci.NativeName = LookupFullName (ci, nav);
234 // Null these out because langs dont have them
235 ci.DateTimeFormatEntry = null;
236 ci.NumberFormatEntry = null;
244 private void ParseLocale (string locale)
248 ci = LookupCulture (locale);
253 if (langs [ci.Language] == null) {
254 if (!ParseLang (ci.Language)) // If we can't parse the lang we cant have the locale
261 private CultureInfoEntry LookupCulture (string locale)
263 XPathDocument doc = GetXPathDocument (Path.Combine ("locales", locale + ".xml"));
264 XPathNavigator nav = doc.CreateNavigator ();
265 CultureInfoEntry ci = new CultureInfoEntry ();
268 // ci.Name = locale; // TODO: Some of these need to be mapped.
270 // First thing we do is get the lang-territory combo, lcid, and full names
271 ci.Language = nav.Evaluate ("string (ldml/identity/language/@type)").ToString ();
272 ci.Territory = nav.Evaluate ("string (ldml/identity/territory/@type)").ToString ();
274 if (!LookupLcids (ci, false))
279 * Locale generation is done in six steps, first we
280 * read the root file which is the base invariant data
281 * then the supplemental root data,
282 * then the language file, the supplemental languages
283 * file then the locale file, then the supplemental
284 * locale file. Values in each descending file can
285 * overwrite previous values.
287 doc = GetXPathDocument (Path.Combine ("langs", "root.xml"));
288 nav = doc.CreateNavigator ();
291 doc = GetXPathDocument (Path.Combine ("supp", "root.xml"));
292 nav = doc.CreateNavigator ();
295 doc = GetXPathDocument (Path.Combine ("langs", GetShortName (ci.Language) + ".xml"));
296 nav = doc.CreateNavigator ();
299 supp = Path.Combine ("supp", ci.Language + ".xml");
300 if (File.Exists (supp)) {
301 doc = GetXPathDocument (supp);
302 nav = doc.CreateNavigator ();
306 doc = GetXPathDocument (Path.Combine ("locales", locale + ".xml"));
307 nav = doc.CreateNavigator ();
310 supp = Path.Combine ("supp", locale + ".xml");
311 if (File.Exists (supp)) {
312 doc = GetXPathDocument (supp);
313 nav = doc.CreateNavigator ();
320 private void Lookup (XPathNavigator nav, CultureInfoEntry ci)
322 LookupDateTimeInfo (nav, ci);
323 LookupNumberInfo (nav, ci);
326 private void LookupNames (CultureInfoEntry ci)
328 XPathDocument doc = GetXPathDocument (Path.Combine ("langs", GetShortName (Lang) + ".xml"));
329 XPathNavigator nav = doc.CreateNavigator ();
331 ci.DisplayName = LookupFullName (ci, nav);
334 ci.EnglishName = ci.DisplayName;
336 doc = GetXPathDocument (Path.Combine ("langs", "en.xml"));
337 nav = doc.CreateNavigator ();
338 ci.EnglishName = LookupFullName (ci, nav);
341 if (ci.Language == Lang) {
342 ci.NativeName = ci.DisplayName;
344 doc = GetXPathDocument (Path.Combine ("langs", GetShortName (ci.Language) + ".xml"));
345 nav = doc.CreateNavigator ();
346 ci.NativeName = LookupFullName (ci, nav);
350 private void LookupDateTimeInfo (XPathNavigator nav, CultureInfoEntry ci)
353 * TODO: Does anyone have multiple calendars?
355 XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("ldml/dates/calendars/calendar");
357 while (ni.MoveNext ()) {
358 DateTimeFormatEntry df = ci.DateTimeFormatEntry;
359 string cal_type = ni.Current.GetAttribute ("type", String.Empty);
361 if (cal_type != String.Empty)
362 df.CalendarType = cal_type;
364 XPathNodeIterator ni2 = (XPathNodeIterator) ni.Current.Evaluate ("optionalCalendars/calendar");
365 int opt_cal_count = 0;
366 while (ni2.MoveNext ()) {
368 string greg_type_str;
369 XPathNavigator df_nav = ni2.Current;
370 switch (df_nav.GetAttribute ("type", String.Empty)) {
381 Console.WriteLine ("unknown calendar type: " +
382 df_nav.GetAttribute ("type", String.Empty));
386 greg_type_str = df_nav.GetAttribute ("greg_type", String.Empty);
387 if (greg_type_str != null && greg_type_str != String.Empty) {
388 GregorianCalendarTypes greg_type = (GregorianCalendarTypes)
389 Enum.Parse (typeof (GregorianCalendarTypes), greg_type_str);
390 int greg_type_int = (int) greg_type;
391 type |= greg_type_int;
394 Console.WriteLine ("Setting cal type: {0:X} for {1}", type, ci.Name);
395 ci.CalendarData [opt_cal_count++] = type;
398 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("monthNames/month");
399 while (ni2.MoveNext ()) {
400 if (ni2.CurrentPosition == 1)
401 df.MonthNames.Clear ();
402 df.MonthNames.Add (ni2.Current.Value);
405 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dayNames/day");
406 while (ni2.MoveNext ()) {
407 if (ni2.CurrentPosition == 1)
408 df.DayNames.Clear ();
409 df.DayNames.Add (ni2.Current.Value);
412 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dayAbbr/day");
413 while (ni2.MoveNext ()) {
414 if (ni2.CurrentPosition == 1)
415 df.AbbreviatedDayNames.Clear ();
416 df.AbbreviatedDayNames.Add (ni2.Current.Value);
419 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("monthAbbr/month");
420 while (ni2.MoveNext ()) {
421 if (ni2.CurrentPosition == 1)
422 df.AbbreviatedMonthNames.Clear ();
423 df.AbbreviatedMonthNames.Add (ni2.Current.Value);
426 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dateFormats/dateFormatLength");
427 while (ni2.MoveNext ()) {
428 XPathNavigator df_nav = ni2.Current;
429 XPathNodeIterator p = df_nav.Select ("dateFormat/pattern");
432 value = p.Current.Value;
433 XPathNodeIterator ext = null;
434 switch (df_nav.GetAttribute ("type", String.Empty)) {
437 ParseFullDateFormat (df, value);
441 df.LongDatePattern = value;
442 ext = df_nav.Select ("extraPatterns/pattern");
443 if (ext.MoveNext ()) {
444 df.LongDatePatterns.Clear ();
446 df.LongDatePatterns.Add (ext.Current.Value);
447 } while (ext.MoveNext ());
452 df.ShortDatePattern = value;
453 ext = df_nav.Select ("extraPatterns/pattern");
454 if (ext.MoveNext ()) {
455 df.ShortDatePatterns.Clear ();
457 df.ShortDatePatterns.Add (ext.Current.Value);
458 } while (ext.MoveNext ());
463 df.YearMonthPattern = value;
467 df.MonthDayPattern = value;
472 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("timeFormats/timeFormatLength");
473 while (ni2.MoveNext ()) {
474 XPathNavigator df_nav = ni2.Current;
475 XPathNodeIterator p = df_nav.Select ("timeFormat/pattern");
478 value = p.Current.Value;
479 XPathNodeIterator ext = null;
480 switch (df_nav.GetAttribute ("type", String.Empty)) {
483 df.LongTimePattern = value.Replace ('a', 't');
484 ext = df_nav.Select ("extraPatterns/pattern");
485 if (ext.MoveNext ()) {
486 df.LongTimePatterns.Clear ();
488 df.LongTimePatterns.Add (ext.Current.Value);
489 } while (ext.MoveNext ());
494 df.ShortTimePattern = value.Replace ('a', 't');
495 ext = df_nav.Select ("extraPatterns/pattern");
496 if (ext.MoveNext ()) {
497 df.ShortTimePatterns.Clear ();
499 df.ShortTimePatterns.Add (ext.Current.Value);
500 } while (ext.MoveNext ());
506 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dateTimeFormats/dateTimeFormatLength/dateTimeFormat/pattern");
508 df.FullDateTimePattern = String.Format (ni2.Current.ToString (),
509 df.LongTimePattern, df.LongDatePattern);
511 XPathNodeIterator am = ni.Current.SelectChildren ("am", "");
513 df.AMDesignator = am.Current.Value;
514 XPathNodeIterator pm = ni.Current.SelectChildren ("pm", "");
516 df.PMDesignator = pm.Current.Value;
518 string am = (string) ni.Current.Evaluate ("string(am)");
519 string pm = (string) ni.Current.Evaluate ("string(pm)");
521 if (am != String.Empty)
522 df.AMDesignator = am;
523 if (pm != String.Empty)
524 df.PMDesignator = pm;
528 string date_sep = (string) nav.Evaluate ("string(ldml/dates/symbols/dateSeparator)");
529 string time_sep = (string) nav.Evaluate ("string(ldml/dates/symbols/timeSeparator)");
531 if (date_sep != String.Empty)
532 ci.DateTimeFormatEntry.DateSeparator = date_sep;
533 if (time_sep != String.Empty)
534 ci.DateTimeFormatEntry.TimeSeparator = time_sep;
537 private void LookupNumberInfo (XPathNavigator nav, CultureInfoEntry ci)
539 XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("ldml/numbers");
541 while (ni.MoveNext ()) {
542 LookupNumberSymbols (ni.Current, ci);
543 LookupDecimalFormat (ni.Current, ci);
544 LookupPercentFormat (ni.Current, ci);
545 LookupCurrencyFormat (ni.Current, ci);
546 LookupCurrencySymbol (ni.Current, ci);
550 private void LookupDecimalFormat (XPathNavigator nav, CultureInfoEntry ci)
552 string format = (string) nav.Evaluate ("string(decimalFormats/" +
553 "decimalFormatLength/decimalFormat/pattern)");
555 if (format == String.Empty)
558 string [] part_one, part_two;
559 string [] pos_neg = format.Split (new char [1] {';'}, 2);
561 // Most of the patterns are common in positive and negative
562 if (pos_neg.Length == 1)
563 pos_neg = new string [] {pos_neg [0], pos_neg [0]};
565 if (pos_neg.Length == 2) {
567 part_one = pos_neg [0].Split (new char [1] {'.'}, 2);
568 if (part_one.Length == 1)
569 part_one = new string [] {part_one [0], String.Empty};
571 if (part_one.Length == 2) {
572 // assumed same for both positive and negative
573 // decimal digit side
574 ci.NumberFormatEntry.NumberDecimalDigits = 0;
575 for (int i = 0; i < part_one [1].Length; i++) {
576 if (part_one [1][i] == '#') {
577 ci.NumberFormatEntry.NumberDecimalDigits ++;
580 // FIXME: This should be actually done by modifying culture xml files, but too many files to be modified.
581 if (ci.NumberFormatEntry.NumberDecimalDigits > 0)
582 ci.NumberFormatEntry.NumberDecimalDigits --;
584 // decimal grouping side
585 part_two = part_one [0].Split (',');
586 if (part_two.Length > 1) {
587 int len = part_two.Length - 1;
588 ci.NumberFormatEntry.NumberGroupSizes = new int [len];
589 for (int i = 0; i < len; i++) {
590 string pat = part_two [i + 1];
591 ci.NumberFormatEntry.NumberGroupSizes [i] = pat.Length;
594 ci.NumberFormatEntry.NumberGroupSizes = new int [1] { 3 };
597 if (pos_neg [1].StartsWith ("(") && pos_neg [1].EndsWith (")")) {
598 ci.NumberFormatEntry.NumberNegativePattern = 0;
599 } else if (pos_neg [1].StartsWith ("- ")) {
600 ci.NumberFormatEntry.NumberNegativePattern = 2;
601 } else if (pos_neg [1].StartsWith ("-")) {
602 ci.NumberFormatEntry.NumberNegativePattern = 1;
603 } else if (pos_neg [1].EndsWith (" -")) {
604 ci.NumberFormatEntry.NumberNegativePattern = 4;
605 } else if (pos_neg [1].EndsWith ("-")) {
606 ci.NumberFormatEntry.NumberNegativePattern = 3;
608 ci.NumberFormatEntry.NumberNegativePattern = 1;
614 private void LookupPercentFormat (XPathNavigator nav, CultureInfoEntry ci)
616 string format = (string) nav.Evaluate ("string(percentFormats/" +
617 "percentFormatLength/percentFormat/pattern)");
619 if (format == String.Empty)
622 string [] part_one, part_two;
624 // we don't have percentNegativePattern in CLDR so
625 // the percentNegativePattern are just guesses
626 if (format.StartsWith ("%")) {
627 ci.NumberFormatEntry.PercentPositivePattern = 2;
628 ci.NumberFormatEntry.PercentNegativePattern = 2;
629 format = format.Substring (1);
630 } else if (format.EndsWith (" %")) {
631 ci.NumberFormatEntry.PercentPositivePattern = 0;
632 ci.NumberFormatEntry.PercentNegativePattern = 0;
633 format = format.Substring (0, format.Length - 2);
634 } else if (format.EndsWith ("%")) {
635 ci.NumberFormatEntry.PercentPositivePattern = 1;
636 ci.NumberFormatEntry.PercentNegativePattern = 1;
637 format = format.Substring (0, format.Length - 1);
639 ci.NumberFormatEntry.PercentPositivePattern = 0;
640 ci.NumberFormatEntry.PercentNegativePattern = 0;
643 part_one = format.Split (new char [1] {'.'}, 2);
644 if (part_one.Length == 2) {
645 // assumed same for both positive and negative
646 // decimal digit side
647 ci.NumberFormatEntry.PercentDecimalDigits = 0;
648 for (int i = 0; i < part_one [1].Length; i++) {
649 if (part_one [1][i] == '#')
650 ci.NumberFormatEntry.PercentDecimalDigits++;
656 if (part_one.Length > 0) {
657 // percent grouping side
658 part_two = part_one [0].Split (',');
659 if (part_two.Length > 1) {
660 int len = part_two.Length - 1;
661 ci.NumberFormatEntry.PercentGroupSizes = new int [len];
662 for (int i = 0; i < len; i++) {
663 string pat = part_two [i + 1];
664 if (pat [pat.Length -1] == '0')
665 ci.NumberFormatEntry.PercentDecimalDigits = pat.Length - 1;
666 ci.NumberFormatEntry.PercentGroupSizes [i] = pat.Length;
669 ci.NumberFormatEntry.PercentGroupSizes = new int [1] { 3 };
670 ci.NumberFormatEntry.PercentDecimalDigits = 2;
675 private void LookupCurrencyFormat (XPathNavigator nav, CultureInfoEntry ci)
677 string format = (string) nav.Evaluate ("string(currencyFormats/" +
678 "currencyFormatLength/currencyFormat/pattern)");
680 if (format == String.Empty)
683 string [] part_one, part_two;
684 string [] pos_neg = format.Split (new char [1] {';'}, 2);
686 // Most of the patterns are common in positive and negative
687 if (pos_neg.Length == 1)
688 pos_neg = new string [] {pos_neg [0], pos_neg [0]};
690 if (pos_neg.Length == 2) {
691 part_one = pos_neg [0].Split (new char [1] {'.'}, 2);
692 if (part_one.Length == 1)
693 part_one = new string [] {part_one [0], String.Empty};
694 if (part_one.Length == 2) {
695 // assumed same for both positive and negative
696 // decimal digit side
697 ci.NumberFormatEntry.CurrencyDecimalDigits = 0;
698 for (int i = 0; i < part_one [1].Length; i++) {
699 if (part_one [1][i] == '0')
700 ci.NumberFormatEntry.CurrencyDecimalDigits++;
705 // decimal grouping side
706 part_two = part_one [0].Split (',');
707 if (part_two.Length > 1) {
708 int len = part_two.Length - 1;
709 ci.NumberFormatEntry.CurrencyGroupSizes = new int [len];
710 for (int i = 0; i < len; i++) {
711 string pat = part_two [i + 1];
712 ci.NumberFormatEntry.CurrencyGroupSizes [i] = pat.Length;
715 ci.NumberFormatEntry.CurrencyGroupSizes = new int [1] { 3 };
718 if (pos_neg [1].StartsWith ("(\u00a4 ") && pos_neg [1].EndsWith (")")) {
719 ci.NumberFormatEntry.CurrencyNegativePattern = 14;
720 } else if (pos_neg [1].StartsWith ("(\u00a4") && pos_neg [1].EndsWith (")")) {
721 ci.NumberFormatEntry.CurrencyNegativePattern = 0;
722 } else if (pos_neg [1].StartsWith ("\u00a4 ") && pos_neg [1].EndsWith ("-")) {
723 ci.NumberFormatEntry.CurrencyNegativePattern = 11;
724 } else if (pos_neg [1].StartsWith ("\u00a4") && pos_neg [1].EndsWith ("-")) {
725 ci.NumberFormatEntry.CurrencyNegativePattern = 3;
726 } else if (pos_neg [1].StartsWith ("(") && pos_neg [1].EndsWith (" \u00a4")) {
727 ci.NumberFormatEntry.CurrencyNegativePattern = 15;
728 } else if (pos_neg [1].StartsWith ("(") && pos_neg [1].EndsWith ("\u00a4")) {
729 ci.NumberFormatEntry.CurrencyNegativePattern = 4;
730 } else if (pos_neg [1].StartsWith ("-") && pos_neg [1].EndsWith (" \u00a4")) {
731 ci.NumberFormatEntry.CurrencyNegativePattern = 8;
732 } else if (pos_neg [1].StartsWith ("-") && pos_neg [1].EndsWith ("\u00a4")) {
733 ci.NumberFormatEntry.CurrencyNegativePattern = 5;
734 } else if (pos_neg [1].StartsWith ("-\u00a4 ")) {
735 ci.NumberFormatEntry.CurrencyNegativePattern = 9;
736 } else if (pos_neg [1].StartsWith ("-\u00a4")) {
737 ci.NumberFormatEntry.CurrencyNegativePattern = 1;
738 } else if (pos_neg [1].StartsWith ("\u00a4 -")) {
739 ci.NumberFormatEntry.CurrencyNegativePattern = 12;
740 } else if (pos_neg [1].StartsWith ("\u00a4-")) {
741 ci.NumberFormatEntry.CurrencyNegativePattern = 2;
742 } else if (pos_neg [1].EndsWith (" \u00a4-")) {
743 ci.NumberFormatEntry.CurrencyNegativePattern = 10;
744 } else if (pos_neg [1].EndsWith ("\u00a4-")) {
745 ci.NumberFormatEntry.CurrencyNegativePattern = 7;
746 } else if (pos_neg [1].EndsWith ("- \u00a4")) {
747 ci.NumberFormatEntry.CurrencyNegativePattern = 13;
748 } else if (pos_neg [1].EndsWith ("-\u00a4")) {
749 ci.NumberFormatEntry.CurrencyNegativePattern = 6;
751 ci.NumberFormatEntry.CurrencyNegativePattern = 0;
754 if (pos_neg [0].StartsWith ("\u00a4 ")) {
755 ci.NumberFormatEntry.CurrencyPositivePattern = 2;
756 } else if (pos_neg [0].StartsWith ("\u00a4")) {
757 ci.NumberFormatEntry.CurrencyPositivePattern = 0;
758 } else if (pos_neg [0].EndsWith (" \u00a4")) {
759 ci.NumberFormatEntry.CurrencyPositivePattern = 3;
760 } else if (pos_neg [0].EndsWith ("\u00a4")) {
761 ci.NumberFormatEntry.CurrencyPositivePattern = 1;
763 ci.NumberFormatEntry.CurrencyPositivePattern = 0;
769 private void LookupNumberSymbols (XPathNavigator nav, CultureInfoEntry ci)
771 string dec = (string) nav.Evaluate ("string(symbols/decimal)");
772 string group = (string) nav.Evaluate ("string(symbols/group)");
773 string percent = (string) nav.Evaluate ("string(symbols/percentSign)");
774 string positive = (string) nav.Evaluate ("string(symbols/plusSign)");
775 string negative = (string) nav.Evaluate ("string(symbols/minusSign)");
776 string per_mille = (string) nav.Evaluate ("string(symbols/perMille)");
777 string infinity = (string) nav.Evaluate ("string(symbols/infinity)");
778 string nan = (string) nav.Evaluate ("string(symbols/nan)");
780 if (dec != String.Empty) {
781 ci.NumberFormatEntry.NumberDecimalSeparator = dec;
782 ci.NumberFormatEntry.PercentDecimalSeparator = dec;
783 ci.NumberFormatEntry.CurrencyDecimalSeparator = dec;
786 if (group != String.Empty) {
787 ci.NumberFormatEntry.NumberGroupSeparator = group;
788 ci.NumberFormatEntry.PercentGroupSeparator = group;
789 ci.NumberFormatEntry.CurrencyGroupSeparator = group;
792 if (percent != String.Empty)
793 ci.NumberFormatEntry.PercentSymbol = percent;
794 if (positive != String.Empty)
795 ci.NumberFormatEntry.PositiveSign = positive;
796 if (negative != String.Empty)
797 ci.NumberFormatEntry.NegativeSign = negative;
798 if (per_mille != String.Empty)
799 ci.NumberFormatEntry.PerMilleSymbol = per_mille;
800 if (infinity != String.Empty)
801 ci.NumberFormatEntry.PositiveInfinitySymbol = infinity;
802 if (nan != String.Empty)
803 ci.NumberFormatEntry.NaNSymbol = nan;
806 private void LookupCurrencySymbol (XPathNavigator nav, CultureInfoEntry ci)
808 string type = currency_types [ci.Territory] as string;
811 Console.WriteLine ("no currency type for: " + ci.Territory);
815 string cur = (string) nav.Evaluate ("string(currencies/currency [@type='" +
816 type + "']/symbol)");
818 if (cur != String.Empty)
819 ci.NumberFormatEntry.CurrencySymbol = cur;
822 private bool LookupLcids (CultureInfoEntry ci, bool lang)
824 XPathDocument doc = GetXPathDocument ("lcids.xml");
825 XPathNavigator nav = doc.CreateNavigator ();
826 string name = ci.Name;
827 // Language name does not always consist of locale name.
828 // (for zh-* it must be either zh-CHS or zh-CHT)
829 string langName = ci.Language;
831 // if (ci.Territory != null)
832 // name += "-" + ci.Territory;
834 XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("lcids/lcid[@name='"
835 + (lang ? langName : name) + "']");
836 if (!ni.MoveNext ()) {
837 Console.WriteLine ("no lcid found for: {0} ({1}/{2})", name, ci.Language, ci.Territory);
840 if (ci.Territory != null) {
841 file = Path.Combine ("locales", ci.Language + "_" + ci.Territory + ".xml");
843 Console.WriteLine ("deleting file: " + file);
849 string id = ni.Current.GetAttribute ("id", String.Empty);
850 string parent = ni.Current.GetAttribute ("parent", String.Empty);
851 string specific = ni.Current.GetAttribute ("specific", String.Empty);
852 string iso2 = ni.Current.GetAttribute ("iso2", String.Empty);
853 string iso3 = ni.Current.GetAttribute ("iso3", String.Empty);
854 string win = ni.Current.GetAttribute ("win", String.Empty);
855 string icu = ni.Current.GetAttribute ("icu_name", String.Empty);
857 // lcids are in 0x<hex> format
859 ci.ParentLcid = parent;
860 ci.SpecificLcid = specific;
866 ci.TextInfoEntry = new TextInfoEntry (int.Parse (id.Substring (2), NumberStyles.HexNumber), GetXPathDocument ("textinfo.xml"));
871 private string LookupFullName (CultureInfoEntry ci, XPathNavigator nav)
873 string pre = "ldml/localeDisplayNames/";
876 ret = (string) nav.Evaluate ("string("+
877 pre + "languages/language[@type='" + GetShortName (ci.Language) + "'])");
879 if (ci.Territory == null)
881 ret += " (" + (string) nav.Evaluate ("string("+
882 pre + "territories/territory[@type='" + ci.Territory + "'])") + ")";
887 private void LookupCurrencyTypes ()
889 XPathDocument doc = GetXPathDocument ("supplementalData.xml");
890 XPathNavigator nav = doc.CreateNavigator ();
892 currency_types = new Hashtable ();
894 XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("supplementalData/currencyData/region");
895 while (ni.MoveNext ()) {
896 string territory = (string) ni.Current.GetAttribute ("iso3166", String.Empty);
897 string currency = (string) ni.Current.Evaluate ("string(currency/@iso4217)");
898 currency_types [territory] = currency;
902 static string control_chars = "ghmsftz";
904 // HACK: We are trying to build year_month and month_day patterns from the full pattern.
905 private void ParseFullDateFormat (DateTimeFormatEntry df, string full)
908 string month_day = String.Empty;
909 string year_month = String.Empty;
910 bool in_month_data = false;
911 bool in_year_data = false;
914 bool inquote = false;
916 for (int i = 0; i < full.Length; i++) {
918 if (!inquote && c == 'M') {
922 in_month_data = true;
923 month_end = month_day.Length;
924 year_end = year_month.Length;
925 } else if (!inquote && Char.ToLower (c) == 'd') {
927 in_month_data = true;
928 in_year_data = false;
929 month_end = month_day.Length;
930 } else if (!inquote && Char.ToLower (c) == 'y') {
933 in_month_data = false;
934 year_end = year_month.Length;
935 } else if (!inquote && control_chars.IndexOf (Char.ToLower (c)) >= 0) {
936 in_year_data = false;
937 in_month_data = false;
938 } else if (in_year_data || in_month_data) {
950 if (month_day != String.Empty) {
951 month_day = month_day.Substring (0, month_end);
952 df.MonthDayPattern = month_day;
954 if (year_month != String.Empty) {
955 year_month = year_month.Substring (0, year_end);
956 df.YearMonthPattern = year_month;
960 private class LcidComparer : IComparer {
962 public int Compare (object a, object b)
964 CultureInfoEntry aa = (CultureInfoEntry) a;
965 CultureInfoEntry bb = (CultureInfoEntry) b;
967 return aa.Lcid.CompareTo (bb.Lcid);
971 private class NameComparer : IComparer {
973 public int Compare (object a, object b)
975 CultureInfoEntry aa = (CultureInfoEntry) a;
976 CultureInfoEntry bb = (CultureInfoEntry) b;
978 return aa.Name.ToLower ().CompareTo (bb.Name.ToLower ());