Driver.cs 46 KB

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