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))) {
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 bool ParseLang (string lang)
193 XPathDocument doc = GetXPathDocument (Path.Combine ("langs", lang + ".xml"));
194 XPathNavigator nav = doc.CreateNavigator ();
195 CultureInfoEntry ci = new CultureInfoEntry ();
196 string lang_type, terr_type;
198 // ci.Name = lang; // TODO: might need to be mapped.
200 lang_type = nav.Evaluate ("string (ldml/identity/language/@type)").ToString ();
201 terr_type = nav.Evaluate ("string (ldml/identity/territory/@type)").ToString ();
203 ci.Language = (lang_type == String.Empty ? null : lang_type);
204 ci.Territory = (terr_type == String.Empty ? null : terr_type);
206 if (!LookupLcids (ci))
209 doc = GetXPathDocument (Path.Combine ("langs", Lang + ".xml"));
210 nav = doc.CreateNavigator ();
211 ci.DisplayName = LookupFullName (ci, nav);
214 ci.EnglishName = ci.DisplayName;
216 doc = GetXPathDocument (Path.Combine ("langs", Lang + ".xml"));
217 nav = doc.CreateNavigator ();
218 ci.EnglishName = LookupFullName (ci, nav);
221 if (ci.Language == Lang) {
222 ci.NativeName = ci.DisplayName;
224 doc = GetXPathDocument (Path.Combine ("langs", lang + ".xml"));
225 nav = doc.CreateNavigator ();
226 ci.NativeName = LookupFullName (ci, nav);
229 // Null these out because langs dont have them
230 ci.DateTimeFormatEntry = null;
231 ci.NumberFormatEntry = null;
239 private void ParseLocale (string locale)
243 ci = LookupCulture (locale);
248 if (langs [ci.Language] == null) {
249 if (!ParseLang (ci.Language)) // If we can't parse the lang we cant have the locale
256 private CultureInfoEntry LookupCulture (string locale)
258 XPathDocument doc = GetXPathDocument (Path.Combine ("locales", locale + ".xml"));
259 XPathNavigator nav = doc.CreateNavigator ();
260 CultureInfoEntry ci = new CultureInfoEntry ();
263 // ci.Name = locale; // TODO: Some of these need to be mapped.
265 // First thing we do is get the lang-territory combo, lcid, and full names
266 ci.Language = nav.Evaluate ("string (ldml/identity/language/@type)").ToString ();
267 ci.Territory = nav.Evaluate ("string (ldml/identity/territory/@type)").ToString ();
269 if (!LookupLcids (ci))
274 * Locale generation is done in six steps, first we
275 * read the root file which is the base invariant data
276 * then the supplemental root data,
277 * then the language file, the supplemental languages
278 * file then the locale file, then the supplemental
279 * locale file. Values in each descending file can
280 * overwrite previous values.
282 doc = GetXPathDocument (Path.Combine ("langs", "root.xml"));
283 nav = doc.CreateNavigator ();
286 doc = GetXPathDocument (Path.Combine ("supp", "root.xml"));
287 nav = doc.CreateNavigator ();
290 doc = GetXPathDocument (Path.Combine ("langs", ci.Language + ".xml"));
291 nav = doc.CreateNavigator ();
294 supp = Path.Combine ("supp", ci.Language + ".xml");
295 if (File.Exists (supp)) {
296 doc = GetXPathDocument (supp);
297 nav = doc.CreateNavigator ();
301 doc = GetXPathDocument (Path.Combine ("locales", locale + ".xml"));
302 nav = doc.CreateNavigator ();
305 supp = Path.Combine ("supp", locale + ".xml");
306 if (File.Exists (supp)) {
307 doc = GetXPathDocument (supp);
308 nav = doc.CreateNavigator ();
315 private void Lookup (XPathNavigator nav, CultureInfoEntry ci)
317 LookupDateTimeInfo (nav, ci);
318 LookupNumberInfo (nav, ci);
321 private void LookupNames (CultureInfoEntry ci)
323 XPathDocument doc = GetXPathDocument (Path.Combine ("langs", Lang + ".xml"));
324 XPathNavigator nav = doc.CreateNavigator ();
326 ci.DisplayName = LookupFullName (ci, nav);
329 ci.EnglishName = ci.DisplayName;
331 doc = GetXPathDocument (Path.Combine ("langs", "en.xml"));
332 nav = doc.CreateNavigator ();
333 ci.EnglishName = LookupFullName (ci, nav);
336 if (ci.Language == Lang) {
337 ci.NativeName = ci.DisplayName;
339 doc = GetXPathDocument (Path.Combine ("langs", ci.Language + ".xml"));
340 nav = doc.CreateNavigator ();
341 ci.NativeName = LookupFullName (ci, nav);
345 private void LookupDateTimeInfo (XPathNavigator nav, CultureInfoEntry ci)
348 * TODO: Does anyone have multiple calendars?
350 XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("ldml/dates/calendars/calendar");
352 while (ni.MoveNext ()) {
353 DateTimeFormatEntry df = ci.DateTimeFormatEntry;
354 string cal_type = ni.Current.GetAttribute ("type", String.Empty);
356 if (cal_type != String.Empty)
357 df.CalendarType = cal_type;
359 XPathNodeIterator ni2 = (XPathNodeIterator) ni.Current.Evaluate ("optionalCalendars/calendar");
360 int opt_cal_count = 0;
361 while (ni2.MoveNext ()) {
363 string greg_type_str;
364 XPathNavigator df_nav = ni2.Current;
365 switch (df_nav.GetAttribute ("type", String.Empty)) {
376 Console.WriteLine ("unknown calendar type: " +
377 df_nav.GetAttribute ("type", String.Empty));
381 greg_type_str = df_nav.GetAttribute ("greg_type", String.Empty);
382 if (greg_type_str != null && greg_type_str != String.Empty) {
383 GregorianCalendarTypes greg_type = (GregorianCalendarTypes)
384 Enum.Parse (typeof (GregorianCalendarTypes), greg_type_str);
385 int greg_type_int = (int) greg_type;
386 type |= greg_type_int;
389 Console.WriteLine ("Setting cal type: {0:X} for {1}", type, ci.Name);
390 ci.CalendarData [opt_cal_count++] = type;
393 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("monthNames/month");
394 while (ni2.MoveNext ()) {
395 if (ni2.CurrentPosition == 1)
396 df.MonthNames.Clear ();
397 df.MonthNames.Add (ni2.Current.Value);
400 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dayNames/day");
401 while (ni2.MoveNext ()) {
402 if (ni2.CurrentPosition == 1)
403 df.DayNames.Clear ();
404 df.DayNames.Add (ni2.Current.Value);
407 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dayAbbr/day");
408 while (ni2.MoveNext ()) {
409 if (ni2.CurrentPosition == 1)
410 df.AbbreviatedDayNames.Clear ();
411 df.AbbreviatedDayNames.Add (ni2.Current.Value);
414 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("monthAbbr/month");
415 while (ni2.MoveNext ()) {
416 if (ni2.CurrentPosition == 1)
417 df.AbbreviatedMonthNames.Clear ();
418 df.AbbreviatedMonthNames.Add (ni2.Current.Value);
421 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dateFormats/dateFormatLength");
422 while (ni2.MoveNext ()) {
423 XPathNavigator df_nav = ni2.Current;
424 XPathNodeIterator p = df_nav.Select ("dateFormat/pattern");
427 value = p.Current.Value;
428 XPathNodeIterator ext = null;
429 switch (df_nav.GetAttribute ("type", String.Empty)) {
432 ParseFullDateFormat (df, value);
436 df.LongDatePattern = value;
437 ext = df_nav.Select ("extraPatterns/pattern");
438 if (ext.MoveNext ()) {
439 df.LongDatePatterns.Clear ();
441 df.LongDatePatterns.Add (ext.Current.Value);
442 } while (ext.MoveNext ());
447 df.ShortDatePattern = value;
448 ext = df_nav.Select ("extraPatterns/pattern");
449 if (ext.MoveNext ()) {
450 df.ShortDatePatterns.Clear ();
452 df.ShortDatePatterns.Add (ext.Current.Value);
453 } while (ext.MoveNext ());
458 df.YearMonthPattern = value;
462 df.MonthDayPattern = value;
467 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("timeFormats/timeFormatLength");
468 while (ni2.MoveNext ()) {
469 XPathNavigator df_nav = ni2.Current;
470 XPathNodeIterator p = df_nav.Select ("timeFormat/pattern");
473 value = p.Current.Value;
474 XPathNodeIterator ext = null;
475 switch (df_nav.GetAttribute ("type", String.Empty)) {
478 df.LongTimePattern = value.Replace ('a', 't');
479 ext = df_nav.Select ("extraPatterns/pattern");
480 if (ext.MoveNext ()) {
481 df.LongTimePatterns.Clear ();
483 df.LongTimePatterns.Add (ext.Current.Value);
484 } while (ext.MoveNext ());
489 df.ShortTimePattern = value.Replace ('a', 't');
490 ext = df_nav.Select ("extraPatterns/pattern");
491 if (ext.MoveNext ()) {
492 df.ShortTimePatterns.Clear ();
494 df.ShortTimePatterns.Add (ext.Current.Value);
495 } while (ext.MoveNext ());
501 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dateTimeFormats/dateTimeFormatLength/dateTimeFormat/pattern");
503 df.FullDateTimePattern = String.Format (ni2.Current.ToString (),
504 df.LongTimePattern, df.LongDatePattern);
506 XPathNodeIterator am = ni.Current.SelectChildren ("am", "");
508 df.AMDesignator = am.Current.Value;
509 XPathNodeIterator pm = ni.Current.SelectChildren ("pm", "");
511 df.PMDesignator = pm.Current.Value;
513 string am = (string) ni.Current.Evaluate ("string(am)");
514 string pm = (string) ni.Current.Evaluate ("string(pm)");
516 if (am != String.Empty)
517 df.AMDesignator = am;
518 if (pm != String.Empty)
519 df.PMDesignator = pm;
523 string date_sep = (string) nav.Evaluate ("string(ldml/dates/symbols/dateSeparator)");
524 string time_sep = (string) nav.Evaluate ("string(ldml/dates/symbols/timeSeparator)");
526 if (date_sep != String.Empty)
527 ci.DateTimeFormatEntry.DateSeparator = date_sep;
528 if (time_sep != String.Empty)
529 ci.DateTimeFormatEntry.TimeSeparator = time_sep;
532 private void LookupNumberInfo (XPathNavigator nav, CultureInfoEntry ci)
534 XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("ldml/numbers");
536 while (ni.MoveNext ()) {
537 LookupNumberSymbols (ni.Current, ci);
538 LookupDecimalFormat (ni.Current, ci);
539 LookupPercentFormat (ni.Current, ci);
540 LookupCurrencyFormat (ni.Current, ci);
541 LookupCurrencySymbol (ni.Current, ci);
545 private void LookupDecimalFormat (XPathNavigator nav, CultureInfoEntry ci)
547 string format = (string) nav.Evaluate ("string(decimalFormats/" +
548 "decimalFormatLength/decimalFormat/pattern)");
550 if (format == String.Empty)
553 string [] part_one, part_two;
554 string [] pos_neg = format.Split (new char [1] {';'}, 2);
556 // Most of the patterns are common in positive and negative
557 if (pos_neg.Length == 1)
558 pos_neg = new string [] {pos_neg [0], pos_neg [0]};
560 if (pos_neg.Length == 2) {
562 part_one = pos_neg [0].Split (new char [1] {'.'}, 2);
563 if (part_one.Length == 1)
564 part_one = new string [] {part_one [0], String.Empty};
566 if (part_one.Length == 2) {
567 // assumed same for both positive and negative
568 // decimal digit side
569 ci.NumberFormatEntry.NumberDecimalDigits = 0;
570 for (int i = 0; i < part_one [1].Length; i++) {
571 if (part_one [1][i] == '#') {
572 ci.NumberFormatEntry.NumberDecimalDigits ++;
575 // FIXME: This should be actually done by modifying culture xml files, but too many files to be modified.
576 if (ci.NumberFormatEntry.NumberDecimalDigits > 0)
577 ci.NumberFormatEntry.NumberDecimalDigits --;
579 // decimal grouping side
580 part_two = part_one [0].Split (',');
581 if (part_two.Length > 1) {
582 int len = part_two.Length - 1;
583 ci.NumberFormatEntry.NumberGroupSizes = new int [len];
584 for (int i = 0; i < len; i++) {
585 string pat = part_two [i + 1];
586 ci.NumberFormatEntry.NumberGroupSizes [i] = pat.Length;
589 ci.NumberFormatEntry.NumberGroupSizes = new int [1] { 3 };
592 if (pos_neg [1].StartsWith ("(") && pos_neg [1].EndsWith (")")) {
593 ci.NumberFormatEntry.NumberNegativePattern = 0;
594 } else if (pos_neg [1].StartsWith ("- ")) {
595 ci.NumberFormatEntry.NumberNegativePattern = 2;
596 } else if (pos_neg [1].StartsWith ("-")) {
597 ci.NumberFormatEntry.NumberNegativePattern = 1;
598 } else if (pos_neg [1].EndsWith (" -")) {
599 ci.NumberFormatEntry.NumberNegativePattern = 4;
600 } else if (pos_neg [1].EndsWith ("-")) {
601 ci.NumberFormatEntry.NumberNegativePattern = 3;
603 ci.NumberFormatEntry.NumberNegativePattern = 1;
609 private void LookupPercentFormat (XPathNavigator nav, CultureInfoEntry ci)
611 string format = (string) nav.Evaluate ("string(percentFormats/" +
612 "percentFormatLength/percentFormat/pattern)");
614 if (format == String.Empty)
617 string [] part_one, part_two;
619 // we don't have percentNegativePattern in CLDR so
620 // the percentNegativePattern are just guesses
621 if (format.StartsWith ("%")) {
622 ci.NumberFormatEntry.PercentPositivePattern = 2;
623 ci.NumberFormatEntry.PercentNegativePattern = 2;
624 format = format.Substring (1);
625 } else if (format.EndsWith (" %")) {
626 ci.NumberFormatEntry.PercentPositivePattern = 0;
627 ci.NumberFormatEntry.PercentNegativePattern = 0;
628 format = format.Substring (0, format.Length - 2);
629 } else if (format.EndsWith ("%")) {
630 ci.NumberFormatEntry.PercentPositivePattern = 1;
631 ci.NumberFormatEntry.PercentNegativePattern = 1;
632 format = format.Substring (0, format.Length - 1);
634 ci.NumberFormatEntry.PercentPositivePattern = 0;
635 ci.NumberFormatEntry.PercentNegativePattern = 0;
638 part_one = format.Split (new char [1] {'.'}, 2);
639 if (part_one.Length == 2) {
640 // assumed same for both positive and negative
641 // decimal digit side
642 ci.NumberFormatEntry.PercentDecimalDigits = 0;
643 for (int i = 0; i < part_one [1].Length; i++) {
644 if (part_one [1][i] == '#')
645 ci.NumberFormatEntry.PercentDecimalDigits++;
651 if (part_one.Length > 0) {
652 // percent grouping side
653 part_two = part_one [0].Split (',');
654 if (part_two.Length > 1) {
655 int len = part_two.Length - 1;
656 ci.NumberFormatEntry.PercentGroupSizes = new int [len];
657 for (int i = 0; i < len; i++) {
658 string pat = part_two [i + 1];
659 if (pat [pat.Length -1] == '0')
660 ci.NumberFormatEntry.PercentDecimalDigits = pat.Length - 1;
661 ci.NumberFormatEntry.PercentGroupSizes [i] = pat.Length;
664 ci.NumberFormatEntry.PercentGroupSizes = new int [1] { 3 };
665 ci.NumberFormatEntry.PercentDecimalDigits = 2;
670 private void LookupCurrencyFormat (XPathNavigator nav, CultureInfoEntry ci)
672 string format = (string) nav.Evaluate ("string(currencyFormats/" +
673 "currencyFormatLength/currencyFormat/pattern)");
675 if (format == String.Empty)
678 string [] part_one, part_two;
679 string [] pos_neg = format.Split (new char [1] {';'}, 2);
681 // Most of the patterns are common in positive and negative
682 if (pos_neg.Length == 1)
683 pos_neg = new string [] {pos_neg [0], pos_neg [0]};
685 if (pos_neg.Length == 2) {
686 part_one = pos_neg [0].Split (new char [1] {'.'}, 2);
687 if (part_one.Length == 1)
688 part_one = new string [] {part_one [0], String.Empty};
689 if (part_one.Length == 2) {
690 // assumed same for both positive and negative
691 // decimal digit side
692 ci.NumberFormatEntry.CurrencyDecimalDigits = 0;
693 for (int i = 0; i < part_one [1].Length; i++) {
694 if (part_one [1][i] == '0')
695 ci.NumberFormatEntry.CurrencyDecimalDigits++;
700 // decimal grouping side
701 part_two = part_one [0].Split (',');
702 if (part_two.Length > 1) {
703 int len = part_two.Length - 1;
704 ci.NumberFormatEntry.CurrencyGroupSizes = new int [len];
705 for (int i = 0; i < len; i++) {
706 string pat = part_two [i + 1];
707 ci.NumberFormatEntry.CurrencyGroupSizes [i] = pat.Length;
710 ci.NumberFormatEntry.CurrencyGroupSizes = new int [1] { 3 };
713 if (pos_neg [1].StartsWith ("(\u00a4 ") && pos_neg [1].EndsWith (")")) {
714 ci.NumberFormatEntry.CurrencyNegativePattern = 14;
715 } else if (pos_neg [1].StartsWith ("(\u00a4") && pos_neg [1].EndsWith (")")) {
716 ci.NumberFormatEntry.CurrencyNegativePattern = 0;
717 } else if (pos_neg [1].StartsWith ("\u00a4 ") && pos_neg [1].EndsWith ("-")) {
718 ci.NumberFormatEntry.CurrencyNegativePattern = 11;
719 } else if (pos_neg [1].StartsWith ("\u00a4") && pos_neg [1].EndsWith ("-")) {
720 ci.NumberFormatEntry.CurrencyNegativePattern = 3;
721 } else if (pos_neg [1].StartsWith ("(") && pos_neg [1].EndsWith (" \u00a4")) {
722 ci.NumberFormatEntry.CurrencyNegativePattern = 15;
723 } else if (pos_neg [1].StartsWith ("(") && pos_neg [1].EndsWith ("\u00a4")) {
724 ci.NumberFormatEntry.CurrencyNegativePattern = 4;
725 } else if (pos_neg [1].StartsWith ("-") && pos_neg [1].EndsWith (" \u00a4")) {
726 ci.NumberFormatEntry.CurrencyNegativePattern = 8;
727 } else if (pos_neg [1].StartsWith ("-") && pos_neg [1].EndsWith ("\u00a4")) {
728 ci.NumberFormatEntry.CurrencyNegativePattern = 5;
729 } else if (pos_neg [1].StartsWith ("-\u00a4 ")) {
730 ci.NumberFormatEntry.CurrencyNegativePattern = 9;
731 } else if (pos_neg [1].StartsWith ("-\u00a4")) {
732 ci.NumberFormatEntry.CurrencyNegativePattern = 1;
733 } else if (pos_neg [1].StartsWith ("\u00a4 -")) {
734 ci.NumberFormatEntry.CurrencyNegativePattern = 12;
735 } else if (pos_neg [1].StartsWith ("\u00a4-")) {
736 ci.NumberFormatEntry.CurrencyNegativePattern = 2;
737 } else if (pos_neg [1].EndsWith (" \u00a4-")) {
738 ci.NumberFormatEntry.CurrencyNegativePattern = 10;
739 } else if (pos_neg [1].EndsWith ("\u00a4-")) {
740 ci.NumberFormatEntry.CurrencyNegativePattern = 7;
741 } else if (pos_neg [1].EndsWith ("- \u00a4")) {
742 ci.NumberFormatEntry.CurrencyNegativePattern = 13;
743 } else if (pos_neg [1].EndsWith ("-\u00a4")) {
744 ci.NumberFormatEntry.CurrencyNegativePattern = 6;
746 ci.NumberFormatEntry.CurrencyNegativePattern = 0;
749 if (pos_neg [0].StartsWith ("\u00a4 ")) {
750 ci.NumberFormatEntry.CurrencyPositivePattern = 2;
751 } else if (pos_neg [0].StartsWith ("\u00a4")) {
752 ci.NumberFormatEntry.CurrencyPositivePattern = 0;
753 } else if (pos_neg [0].EndsWith (" \u00a4")) {
754 ci.NumberFormatEntry.CurrencyPositivePattern = 3;
755 } else if (pos_neg [0].EndsWith ("\u00a4")) {
756 ci.NumberFormatEntry.CurrencyPositivePattern = 1;
758 ci.NumberFormatEntry.CurrencyPositivePattern = 0;
764 private void LookupNumberSymbols (XPathNavigator nav, CultureInfoEntry ci)
766 string dec = (string) nav.Evaluate ("string(symbols/decimal)");
767 string group = (string) nav.Evaluate ("string(symbols/group)");
768 string percent = (string) nav.Evaluate ("string(symbols/percentSign)");
769 string positive = (string) nav.Evaluate ("string(symbols/plusSign)");
770 string negative = (string) nav.Evaluate ("string(symbols/minusSign)");
771 string per_mille = (string) nav.Evaluate ("string(symbols/perMille)");
772 string infinity = (string) nav.Evaluate ("string(symbols/infinity)");
773 string nan = (string) nav.Evaluate ("string(symbols/nan)");
775 if (dec != String.Empty) {
776 ci.NumberFormatEntry.NumberDecimalSeparator = dec;
777 ci.NumberFormatEntry.PercentDecimalSeparator = dec;
778 ci.NumberFormatEntry.CurrencyDecimalSeparator = dec;
781 if (group != String.Empty) {
782 ci.NumberFormatEntry.NumberGroupSeparator = group;
783 ci.NumberFormatEntry.PercentGroupSeparator = group;
784 ci.NumberFormatEntry.CurrencyGroupSeparator = group;
787 if (percent != String.Empty)
788 ci.NumberFormatEntry.PercentSymbol = percent;
789 if (positive != String.Empty)
790 ci.NumberFormatEntry.PositiveSign = positive;
791 if (negative != String.Empty)
792 ci.NumberFormatEntry.NegativeSign = negative;
793 if (per_mille != String.Empty)
794 ci.NumberFormatEntry.PerMilleSymbol = per_mille;
795 if (infinity != String.Empty)
796 ci.NumberFormatEntry.PositiveInfinitySymbol = infinity;
797 if (nan != String.Empty)
798 ci.NumberFormatEntry.NaNSymbol = nan;
801 private void LookupCurrencySymbol (XPathNavigator nav, CultureInfoEntry ci)
803 string type = currency_types [ci.Territory] as string;
806 Console.WriteLine ("no currency type for: " + ci.Territory);
810 string cur = (string) nav.Evaluate ("string(currencies/currency [@type='" +
811 type + "']/symbol)");
813 if (cur != String.Empty)
814 ci.NumberFormatEntry.CurrencySymbol = cur;
817 private bool LookupLcids (CultureInfoEntry ci)
819 XPathDocument doc = GetXPathDocument ("lcids.xml");
820 XPathNavigator nav = doc.CreateNavigator ();
821 string name = ci.Language;
823 if (ci.Territory != null)
824 name += "-" + ci.Territory;
826 XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("lcids/lcid[@name='"
828 if (!ni.MoveNext ()) {
830 if (ci.Territory != null) {
831 file = Path.Combine ("locales", ci.Language + "_" + ci.Territory + ".xml");
833 Console.WriteLine ("deleting file: " + file);
835 Console.WriteLine ("no lcid found for: " + name);
839 string id = ni.Current.GetAttribute ("id", String.Empty);
840 string parent = ni.Current.GetAttribute ("parent", String.Empty);
841 string specific = ni.Current.GetAttribute ("specific", String.Empty);
842 string iso2 = ni.Current.GetAttribute ("iso2", String.Empty);
843 string iso3 = ni.Current.GetAttribute ("iso3", String.Empty);
844 string win = ni.Current.GetAttribute ("win", String.Empty);
845 string icu = ni.Current.GetAttribute ("icu_name", String.Empty);
847 // lcids are in 0x<hex> format
849 ci.ParentLcid = parent;
850 ci.SpecificLcid = specific;
856 ci.TextInfoEntry = new TextInfoEntry (int.Parse (id.Substring (2), NumberStyles.HexNumber), GetXPathDocument ("textinfo.xml"));
861 private string LookupFullName (CultureInfoEntry ci, XPathNavigator nav)
863 string pre = "ldml/localeDisplayNames/";
866 ret = (string) nav.Evaluate ("string("+
867 pre + "languages/language[@type='" + ci.Language + "'])");
869 if (ci.Territory == null)
871 ret += " (" + (string) nav.Evaluate ("string("+
872 pre + "territories/territory[@type='" + ci.Territory + "'])") + ")";
877 private void LookupCurrencyTypes ()
879 XPathDocument doc = GetXPathDocument ("supplementalData.xml");
880 XPathNavigator nav = doc.CreateNavigator ();
882 currency_types = new Hashtable ();
884 XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("supplementalData/currencyData/region");
885 while (ni.MoveNext ()) {
886 string territory = (string) ni.Current.GetAttribute ("iso3166", String.Empty);
887 string currency = (string) ni.Current.Evaluate ("string(currency/@iso4217)");
888 currency_types [territory] = currency;
892 static string control_chars = "ghmsftz";
894 // HACK: We are trying to build year_month and month_day patterns from the full pattern.
895 private void ParseFullDateFormat (DateTimeFormatEntry df, string full)
898 string month_day = String.Empty;
899 string year_month = String.Empty;
900 bool in_month_data = false;
901 bool in_year_data = false;
904 bool inquote = false;
906 for (int i = 0; i < full.Length; i++) {
908 if (!inquote && c == 'M') {
912 in_month_data = true;
913 month_end = month_day.Length;
914 year_end = year_month.Length;
915 } else if (!inquote && Char.ToLower (c) == 'd') {
917 in_month_data = true;
918 in_year_data = false;
919 month_end = month_day.Length;
920 } else if (!inquote && Char.ToLower (c) == 'y') {
923 in_month_data = false;
924 year_end = year_month.Length;
925 } else if (!inquote && control_chars.IndexOf (Char.ToLower (c)) >= 0) {
926 in_year_data = false;
927 in_month_data = false;
928 } else if (in_year_data || in_month_data) {
940 if (month_day != String.Empty) {
941 month_day = month_day.Substring (0, month_end);
942 df.MonthDayPattern = month_day;
944 if (year_month != String.Empty) {
945 year_month = year_month.Substring (0, year_end);
946 df.YearMonthPattern = year_month;
950 private class LcidComparer : IComparer {
952 public int Compare (object a, object b)
954 CultureInfoEntry aa = (CultureInfoEntry) a;
955 CultureInfoEntry bb = (CultureInfoEntry) b;
957 return aa.Lcid.CompareTo (bb.Lcid);
961 private class NameComparer : IComparer {
963 public int Compare (object a, object b)
965 CultureInfoEntry aa = (CultureInfoEntry) a;
966 CultureInfoEntry bb = (CultureInfoEntry) b;
968 return aa.Name.ToLower ().CompareTo (bb.Name.ToLower ());