Export 0.1.45
[framework/web/web-ui-fw.git] / libs / js / globalize / generator / Program.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.IO;
6 using System.Globalization;
7 using System.Web.Script.Serialization;
8 using System.Collections.Specialized;
9 using System.Collections;
10 using System.Text.RegularExpressions;
11 using System.Reflection;
12 using System.Diagnostics;
13 using Globalization;
14
15 namespace Globalization {
16     public class GlobalizationInfo {
17         public string name = "";
18         public string englishName;
19         public string nativeName;
20         public string language;
21         public bool isRTL;
22         public NumberFormatInfo numberFormat;
23         public Dictionary<String, DateFormatInfo> calendars;
24         private CultureInfo culture;
25         public static Dictionary<String, Object> BasisGlobInfo;
26
27
28         private static string[] _numberNegativePatterns = "(n)|-n|- n|n-|n -".Split('|');
29         private static string[] _currencyNegativePatterns = "($n)|-$n|$-n|$n-|(n$)|-n$|n-$|n$-|-n $|-$ n|n $-|$ n-|$ -n|n- $|($ n)|(n $)".Split('|');
30         private static string[] _percentNegativePatterns = "-n %|-n%|-%n|%-n|%n-|n-%|n%-|-% n|n %-|% n-|% -n|n- %".Split('|');
31         private static string[] _currencyPositivePatterns = "$n|n$|$ n|n $".Split('|');
32         private static string[] _percentPositivePatterns = "n %|n%|%n|% n".Split('|');
33
34         public static GlobalizationInfo GetGlobInfo(CultureInfo culture) {
35             var info = new GlobalizationInfo {
36                 culture = culture,
37                 language = (culture == CultureInfo.InvariantCulture || culture.IsNeutralCulture) ? culture.Name : culture.Parent.Name,
38                 name = String.IsNullOrEmpty(culture.Name) ? "invariant" : culture.Name,
39                 englishName = String.IsNullOrEmpty(culture.Name) ? "invariant" : culture.EnglishName,
40                 nativeName = String.IsNullOrEmpty(culture.Name) ? "invariant" : culture.NativeName,
41                 isRTL = culture.TextInfo.IsRightToLeft,
42                 numberFormat = GetNumberFormatInfo(culture),
43                 calendars = GetCalendars(culture)
44             };
45             return info;
46         }
47
48         public static Dictionary<String, DateFormatInfo> GetCalendars(CultureInfo culture) {
49             var calendars = new Dictionary<String, DateFormatInfo>();
50             bool foundStandard = false;
51             var gregorianType = typeof(GregorianCalendar);
52             var defaultCalendar = culture.DateTimeFormat.Calendar;
53             var defaultCalendarType = defaultCalendar.GetType();
54             GregorianCalendarTypes? gregorianCalendarType = null;
55             if (defaultCalendarType == gregorianType) {
56                 gregorianCalendarType = ((GregorianCalendar)defaultCalendar).CalendarType;
57             }
58             var optionalCalendars = culture.OptionalCalendars;
59             foreach (var calendar in optionalCalendars) {
60                 var type = calendar.GetType();
61                 string name;
62                 bool isStandard = false;
63                 if (type == gregorianType) {
64                     var calendarType = ((GregorianCalendar)calendar).CalendarType;
65                     if (calendarType == GregorianCalendarTypes.USEnglish) {
66                         // we include the Gregorian_USEnglish culture as part of the built-in 'en' culture
67                         // because it is so common -- it is an optional calendar of every single english
68                         // speaking culture. So, skip it when found for any other culture.
69                         continue;
70                     }
71                     else if (culture == CultureInfo.InvariantCulture) {
72                         // invariant has one calendar, Gregorian_Localized, which is identical
73                         // to Gregorian_USEnglish.
74                         name = " Gregorian_USEnglish";
75                     }
76                     else {
77                         name = "Gregorian_" + calendarType.ToString();
78                     }
79                     if (!foundStandard && gregorianCalendarType.HasValue && gregorianCalendarType == gregorianCalendarType.Value) {
80                         isStandard = true;
81                         foundStandard = true;
82                     }
83                 }
84                 else {
85                     name = type.Name.Replace("Calendar", "");
86                     if (!foundStandard) {
87                         isStandard = true;
88                         foundStandard = true;
89                     }
90                 }
91                 string key = name;
92                 if (isStandard) {
93                     key = "standard";
94                 }
95                 if (culture != CultureInfo.InvariantCulture) {
96                     culture.DateTimeFormat.Calendar = calendar;
97                 }
98                 var calendarInfo = GetDateTimeFormatInfo(culture, name);
99                 calendars.Add(key, calendarInfo);
100             }
101             if (!foundStandard) {
102                 throw new ApplicationException("Could not locate the standard calendar type for culture '" + culture.Name + "'.");
103             }
104             return calendars;
105         }
106
107         public static GlobalizationInfo.NumberFormatInfo GetNumberFormatInfo(CultureInfo culture) {
108             var nf = culture.NumberFormat;
109             return new GlobalizationInfo.NumberFormatInfo {
110                 decimals = nf.NumberDecimalDigits,
111                 decimalSeparator = nf.NumberDecimalSeparator,
112                 groupSeparator = nf.NumberGroupSeparator,
113                 groupSizes = nf.NumberGroupSizes,
114                 NaN = nf.NaNSymbol,
115                 negative = nf.NegativeSign,
116                 negativeInfinity = nf.NegativeInfinitySymbol,
117                 positive = nf.PositiveSign,
118                 positiveInfinity = nf.PositiveInfinitySymbol,
119                 pattern = new string[] { GetFromStringList(_numberNegativePatterns, nf.NumberNegativePattern) },
120                 currency = new GlobalizationInfo.NumberFormatInfo.NumberClassFormatInfo {
121                     decimals = nf.CurrencyDecimalDigits,
122                     decimalSeparator = nf.CurrencyDecimalSeparator,
123                     groupSeparator = nf.CurrencyGroupSeparator,
124                     groupSizes = nf.CurrencyGroupSizes,
125                     pattern = new string[] {
126                         GetFromStringList(_currencyNegativePatterns, nf.CurrencyNegativePattern),
127                         GetFromStringList(_currencyPositivePatterns, nf.CurrencyPositivePattern)
128                     },
129                     symbol = nf.CurrencySymbol
130                 },
131                 percent = new GlobalizationInfo.NumberFormatInfo.NumberClassFormatInfo {
132                     decimals = nf.PercentDecimalDigits,
133                     decimalSeparator = nf.PercentDecimalSeparator,
134                     groupSeparator = nf.PercentGroupSeparator,
135                     groupSizes = nf.PercentGroupSizes,
136                     pattern = new string[] {
137                         GetFromStringList(_percentNegativePatterns, nf.PercentNegativePattern),
138                         GetFromStringList(_percentPositivePatterns, nf.PercentPositivePattern)
139                     },
140                     symbol = nf.PercentSymbol
141                 }
142             };
143         }
144
145         public static string[] GetAMPMDesignators(CultureInfo culture, string ampm) {
146             return String.IsNullOrEmpty(ampm) ? null : new string[] { ampm, culture.TextInfo.ToLower(ampm), culture.TextInfo.ToUpper(ampm) };
147         }
148
149         public static GlobalizationInfo.DateFormatInfo GetDateTimeFormatInfo(CultureInfo culture, string calendarName) {
150             var df = culture.DateTimeFormat;
151             var info = new GlobalizationInfo.DateFormatInfo {
152                 name = calendarName,
153                 months = new DateFormatInfo.MonthInfo { names = df.MonthNames, namesAbbr = df.AbbreviatedMonthNames },
154                 monthsGenitive = new DateFormatInfo.MonthInfo { names = df.MonthGenitiveNames, namesAbbr = df.AbbreviatedMonthGenitiveNames },
155                 firstDay = (int) df.FirstDayOfWeek,
156                 dateSeparator = df.DateSeparator,
157                 timeSeparator = df.TimeSeparator,
158                 days = new DateFormatInfo.DayInfo { names = df.DayNames, namesAbbr = df.AbbreviatedDayNames, namesShort = df.ShortestDayNames },
159                 eras = GetEraInfo(culture),
160                 twoDigitYearMax = df.Calendar.TwoDigitYearMax,
161                 patterns = GetPatterns(df)
162             };
163             info.AM = GetAMPMDesignators(culture, df.AMDesignator);
164             info.PM = GetAMPMDesignators(culture, df.PMDesignator);
165             if (df.Calendar != null) {
166                 var type = df.Calendar.GetType();
167                 if (type == typeof(HijriCalendar)) {
168                     string convert;
169                     using (var sr = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("Generator.HijriCalendar.js"))) {
170                         convert = sr.ReadToEnd();
171                     }
172                     int adjustment = ((HijriCalendar)df.Calendar).HijriAdjustment;
173                     convert = convert.Replace("%HIJRIADJUSTMENT%", adjustment.ToString(CultureInfo.InvariantCulture));
174                     info.convertScriptBlock = convert;
175                 }
176                 else if (type == typeof(UmAlQuraCalendar)) {
177                     using (var sr = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("Generator.UmAlQuraCalendar.js"))) {
178                         info.convertScriptBlock = sr.ReadToEnd();
179                     }
180                 }
181             }
182             return info;
183         }
184
185         private static GlobalizationInfo.DateFormatInfo.EraInfo[] GetEraInfo(CultureInfo culture) {
186             Calendar cal = culture.DateTimeFormat.Calendar;
187             List<GlobalizationInfo.DateFormatInfo.EraInfo> eras = null;
188             if (cal != null) {
189                 eras = new List<GlobalizationInfo.DateFormatInfo.EraInfo>();
190                 foreach (var eraNum in cal.Eras) {
191                     eras.Add(GetEraInfo(culture, eraNum));
192                 }
193             }
194             return eras == null ? null : eras.ToArray();
195         }
196
197         private static GlobalizationInfo.DateFormatInfo.EraInfo GetEraInfo(CultureInfo culture, int eraNum) {
198             var era = new GlobalizationInfo.DateFormatInfo.EraInfo {
199                 name = culture.DateTimeFormat.GetEraName(eraNum),
200                 offset = 0,
201                 start = null
202             };
203             var calendar = culture.DateTimeFormat.Calendar;
204
205             Type type = calendar.GetType();
206             if (type != typeof(GregorianCalendar)) {
207                 if (type == typeof(TaiwanCalendar)) {
208                     era.offset = 0x777;
209                 }
210                 else if (type == typeof(KoreanCalendar)) {
211                     era.offset = -2333;
212                 }
213                 else if (type == typeof(ThaiBuddhistCalendar)) {
214                     era.offset = -543;
215                 }
216                 else if (type == typeof(JapaneseCalendar)) {
217                     switch (eraNum) {
218                         case 1:
219                             era.start = 0xdf9984200L;
220                             era.offset = 0x7c4;
221                             break;
222                         case 2:
223                             era.start = -1357603200000L;
224                             era.offset = 0x785;
225                             break;
226                         case 3:
227                             era.start = -1812153600000L;
228                             era.offset = 0x777;
229                             break;
230                         case 4:
231                             era.start = null;
232                             era.offset = 0x74b;
233                             break;
234                         default:
235                             throw new InvalidOperationException("Invalid era number for JapaneseCalendar: " + eraNum.ToString());
236                     }
237                 }
238             }
239             return era;
240         }
241
242         private static Dictionary<String, String> GetPatterns(DateTimeFormatInfo df) {
243             var patterns = new Dictionary<String, String> {
244                 { "d", df.ShortDatePattern },
245                 { "D", df.LongDatePattern },
246                 { "t", df.ShortTimePattern },
247                 { "T", df.LongTimePattern },
248                 { "f", df.LongDatePattern + " " + df.ShortTimePattern },
249                 { "F", df.FullDateTimePattern },
250                 { "M", df.MonthDayPattern },
251                 { "S", df.SortableDateTimePattern },
252                 { "Y", df.YearMonthPattern }
253             };
254             return patterns;
255         }
256
257         private static bool ArrayEquality(Array arr1, Array arr2) {
258             if (arr1.Length == arr2.Length) {
259                 for (int i = 0; i < arr1.Length; i++) {
260                     if (!arr1.GetValue(i).Equals(arr2.GetValue(i))) {
261                         return false;
262                     }
263                 }
264                 return true;
265             }
266             return false;
267         }
268
269         private static bool ListEquality(IList arr1, IList arr2) {
270             if (arr1.Count == arr2.Count) {
271                 for (int i = 0; i < arr1.Count; i++) {
272                     var val1 = arr1[i];
273                     var val2 = arr2[i];
274                     if (val1 == null || !val1.Equals(val2)) {
275                         if (val1 is IList && val2 is IList) {
276                             if (!ListEquality(val1 as IList, val2 as IList)) {
277                                 return false;
278                             }
279                         }
280                         else if (val1 is Dictionary<String, Object> && val2 is Dictionary<String, Object>) {
281                             var diff = DiffGlobInfos((Dictionary<String, Object>)val1, (Dictionary<String, Object>)val2);
282                             if (diff.Count > 0) {
283                                 return false;
284                             }
285                         }
286                         else {
287                             return false;
288                         }
289                     }
290                 }
291                 return true;
292             }
293             return false;
294         }
295
296         private static string GetFromStringList(string[] list, int value) {
297             return value < list.Length ? list[value] : null;
298         }
299
300         public Dictionary<String, Object> ToDictionary(bool diffCalendars) {
301             var jss = new JavaScriptSerializer();
302             var str = jss.Serialize(this);
303             var dictionary = jss.Deserialize<Dictionary<String, Object>>(str);
304             var cals = (Dictionary<String, Object>) dictionary["calendars"];
305             Dictionary<String, Object> basisStandardCal = null;
306             if (GlobalizationInfo.BasisGlobInfo != null) {
307                 basisStandardCal = (Dictionary<String, Object>)((Dictionary<String, Object>)GlobalizationInfo.BasisGlobInfo["calendars"])["standard"];
308                 foreach (var pair in this.calendars) {
309                     var cal = (Dictionary<String, Object>)cals[pair.Key];
310                     if (diffCalendars) {
311                         // make each calendar a diff from the standard basis calendar
312                         cals[pair.Key] = cal = DiffGlobInfos(basisStandardCal, cal);
313                     }
314                     // apply convert script if it exists
315                     if (!String.IsNullOrEmpty(pair.Value.convertScriptBlock)) {
316                         cal["convert"] = pair.Value.convertScriptBlock;
317                     }
318                     // remove redundant monthsGenitive array if it is equivilent to months
319                     Dictionary<String, Object> months = cal.ContainsKey("months") ? (Dictionary<String, Object>)cal["months"] : null;
320                     Dictionary<String, Object> monthsGenitive = cal.ContainsKey("monthsGenitive") ? (Dictionary<String, Object>)cal["monthsGenitive"] : null;
321                     Dictionary<String, Object> diff = (months != null && monthsGenitive != null) ? DiffGlobInfos(months, monthsGenitive) : null;
322                     if (diff != null && diff.Count == 0) {
323                         // the genitive months are the same as months, so remove it since it's optional
324                         cal.Remove("monthsGenitive");
325                     }
326                 }
327             }
328             return dictionary;
329         }
330
331         public static string GenerateJavaScript(string extend, string global, CultureInfo culture, string name, Dictionary<String, Object> dictionary, StringBuilder aggregateScript) {
332             string cultureFragment = ToJavaScript(extend, culture, dictionary, 1, false);
333
334             if (aggregateScript != null) {
335                 aggregateScript.AppendFormat(CultureInfo.InvariantCulture, @"
336 Globalize.addCultureInfo( ""{0}"", ""default"", {{
337 {1}
338 }});
339 ", name, cultureFragment, extend);
340             }
341
342                 return string.Format(CultureInfo.InvariantCulture, @"/*
343  * Globalize Culture {0}
344  *
345  * http://github.com/jquery/globalize
346  *
347  * Copyright Software Freedom Conservancy, Inc.
348  * Dual licensed under the MIT or GPL Version 2 licenses.
349  * http://jquery.org/license
350  *
351  * This file was generated by the Globalize Culture Generator
352  * Translation: bugs found in this file need to be fixed in the generator
353  */
354
355 (function( window, undefined ) {{
356
357 var Globalize;
358
359 if ( typeof require !== ""undefined""
360         && typeof exports !== ""undefined""
361         && typeof module !== ""undefined"" ) {{
362         // Assume CommonJS
363         Globalize = require( ""globalize"" );
364 }} else {{
365         // Global variable
366         Globalize = window.Globalize;
367 }}
368
369 Globalize.addCultureInfo( ""{0}"", ""default"", {{
370 {1}
371 }});
372
373 }}( this ));
374 ", name, cultureFragment);
375         }
376
377         private static string Serialize(object value) {
378             // no need to escape single quotes
379             return _jss.Serialize(value).Replace("\\u0027", "'");
380         }
381
382         private static string ToJavaScript(string extend, CultureInfo culture, Dictionary<String, Object> dictionary, int level, bool isCalendars) {
383             StringBuilder sb = new StringBuilder();
384             string padding = _padding.Substring(0, level);
385             bool first = true;
386             foreach (var pair in dictionary) {
387                 if (!first) {
388                     sb.Append(",\n");
389                 }
390                 first = false;
391                 if (pair.Value is Dictionary<String, Object>) {
392                     sb.AppendFormat("{0}{1}: {{\n{2}\n{0}}}", padding, pair.Key, ToJavaScript(extend, culture, (Dictionary<String, Object>)pair.Value, level + 1, pair.Key.Equals("calendars")));
393                 }
394                 else if (pair.Key.Equals("convert")) {
395                     sb.AppendFormat("{0}convert: {{\n{1}\n{0}}}", padding, pair.Value);
396                 }
397                 else if (pair.Key.Equals("groupSeparator")) {
398                     sb.AppendFormat("{0}\",\": {1}", padding, Serialize(pair.Value));
399                 }
400                 else if (pair.Key.Equals("decimalSeparator")) {
401                     sb.AppendFormat("{0}\".\": {1}", padding, Serialize(pair.Value));
402                 }
403                 else if (pair.Key.Equals("positive")) {
404                     sb.AppendFormat("{0}\"+\": {1}", padding, Serialize(pair.Value));
405                 }
406                 else if (pair.Key.Equals("negative")) {
407                     sb.AppendFormat("{0}\"-\": {1}", padding, Serialize(pair.Value));
408                 }
409                 else if (pair.Key.Equals("dateSeparator")) {
410                     sb.AppendFormat("{0}\"/\": {1}", padding, Serialize(pair.Value));
411                 }
412                 else if (pair.Key.Equals("timeSeparator")) {
413                     sb.AppendFormat("{0}\":\": {1}", padding, Serialize(pair.Value));
414                 }
415                 else {
416                     sb.AppendFormat("{0}{1}: {2}", padding, pair.Key, Serialize(pair.Value));
417                 }
418             }
419             return sb.ToString();
420         }
421
422         private static JavaScriptSerializer _jss = new JavaScriptSerializer();
423         private static string _padding = "                                                                                                                                      ";
424
425         private static Dictionary<String, Object> ToDictionary(IEnumerable<KeyValuePair<String, Object>> pairs) {
426             var d = new Dictionary<String, Object>();
427             foreach (var pair in pairs) {
428                 d.Add(pair.Key, pair.Value);
429             }
430             return d;
431         }
432
433         public static Dictionary<String, Object> DiffGlobInfos(Dictionary<String, Object> glob1, Dictionary<String, Object> glob2) {
434             var unique = new Dictionary<String, Object>();
435             var comparer = new KeyValueComparer();
436
437             var diff = ToDictionary(glob2.Except(glob1, comparer));
438             foreach (var pair in glob2) {
439                 if (diff.ContainsKey(pair.Key) && pair.Value != null) {
440                     if (pair.Value is Dictionary<String, Object>) {
441                         var subdiff = glob1.ContainsKey(pair.Key) ? DiffGlobInfos((Dictionary<String, Object>)glob1[pair.Key], (Dictionary<String, Object>)pair.Value) : (Dictionary<String, Object>)pair.Value;
442                         if (subdiff.Count > 0) {
443                             //Debug.WriteLine("Replacing\n    {0}\nwith\n    {1}", _jss.Serialize(diff[pair.Key]), _jss.Serialize(subdiff));
444                             diff[pair.Key] = subdiff;
445                         }
446                         else {
447                             //Debug.WriteLine("\nRemoving {0}\n", _jss.Serialize(pair.Key));
448                             diff.Remove(pair.Key);
449                         }
450                     }
451                     else if (pair.Value is IList) {
452                         if (glob1.ContainsKey(pair.Key) && ListEquality((IList)pair.Value, (IList)glob1[pair.Key])) {
453                             diff.Remove(pair.Key);
454                         }
455                     }
456                 }
457             }
458
459             return diff;
460         }
461
462         public class KeyValueComparer : IEqualityComparer<KeyValuePair<String, Object>> {
463             public bool Equals(KeyValuePair<String, Object> x, KeyValuePair<String, Object> y) {
464                 if (x.Key.Equals(y.Key)) {
465                     if ((x.Value == null && y.Value == null) || (x.Value != null && x.Value.Equals(y.Value))) {
466                         return true;
467                     }
468                     else if (x.Value is ArrayList && y.Value is ArrayList) {
469                         return ListEquality((IList)x.Value, (IList)y.Value);
470                     }
471                     else if (x.Value is Array && y.Value is Array) {
472                         return ArrayEquality(x.Value as Array, y.Value as Array);
473                     }
474                     else if (x.Value is Dictionary<String, Object> && y.Value is Dictionary<String, Object>) {
475                         var diff = DiffGlobInfos((Dictionary<String, Object>)x.Value, (Dictionary<String, Object>)y.Value);
476                         if (diff.Count == 0) {
477                             return true;
478                         }
479                         //else {
480                         //    Debug.WriteLine("    Dictionaries diff:\n        {0}\n        {1}", _jss.Serialize(x.Value), _jss.Serialize(y.Value));
481                         //}
482                     }
483                 }
484                 //Debug.WriteLine("    Diff found: {0}={1}, {2}={3}", x.Key, x.Value, y.Key, y.Value);
485                 return false;
486             }
487
488             public int GetHashCode(KeyValuePair<String, Object> obj) {
489                 return obj.GetHashCode();
490             }
491         }
492
493         public class NumberFormatInfo {
494             public string[] pattern;
495             public int decimals;
496             public string groupSeparator;
497             public string decimalSeparator;
498             public int[] groupSizes;
499             public string NaN;
500             public string negative;
501             public string negativeInfinity;
502             public string positive;
503             public string positiveInfinity;
504             public NumberClassFormatInfo percent;
505             public NumberClassFormatInfo currency;
506
507             public class NumberClassFormatInfo {
508                 public string[] pattern;
509                 public int decimals;
510                 public int[] groupSizes;
511                 public string groupSeparator;
512                 public string decimalSeparator;
513                 public string symbol;
514             }
515         }
516
517         public class DateFormatInfo {
518             public string name;
519             public string dateSeparator;
520             public string timeSeparator;
521             public int firstDay;
522             public DayInfo days;
523             public MonthInfo months;
524             public MonthInfo monthsGenitive;
525             public string[] AM;
526             public string[] PM;
527             public EraInfo[] eras;
528             public int twoDigitYearMax;
529             public Dictionary<String, String> patterns;
530             internal string convertScriptBlock;
531
532             public class EraInfo {
533                 public string name;
534                 public long? start;
535                 public long offset;
536             }
537
538             public class MonthInfo {
539                 public string[] names;
540                 public string[] namesAbbr;
541             }
542
543             public class DayInfo {
544                 public string[] names;
545                 public string[] namesAbbr;
546                 public string[] namesShort;
547             }
548         }
549
550     }
551
552     public class Program {
553
554         private static void WriteCulture(string outputdir, string fileName, string extend, string global, CultureInfo culture, StringBuilder aggregateScript) {
555             var globInfo = GlobalizationInfo.GetGlobInfo(culture);
556             var diff = (String.IsNullOrEmpty(extend) || culture == CultureInfo.InvariantCulture || culture.Name.Equals("en")) ? globInfo.ToDictionary(false) : GlobalizationInfo.DiffGlobInfos(GlobalizationInfo.BasisGlobInfo, globInfo.ToDictionary(true));
557
558             // Fix for Issue #31 - en-US 'englishName' is wrong
559             // Special case diff of englishName for en-US. The generator diff seemingly finds both "en" and "en-US" to
560             // have englishName "English (United States)" but globalize.js (correctly) has the neutral "English" for "en"/"default"
561             if (culture.Name.Equals("en-US")) {
562                 diff.Add("name", globInfo.name);
563                 diff.Add("englishName", globInfo.englishName);
564             }
565
566             var script = GlobalizationInfo.GenerateJavaScript(extend, global, culture, culture.Name, diff, aggregateScript);
567             var filePath = Path.Combine(outputdir, String.Format(fileName, (String.IsNullOrEmpty(culture.Name) ? "invariant" : culture.Name)));
568
569             File.WriteAllText(filePath, script);
570             Console.WriteLine(filePath);
571         }
572
573         [STAThread]
574         static void Main(string[] args) {
575             string outputdir = "lib\\cultures";
576             string extend = "extend";
577             string global = "Globalization";
578             string fileName = "globalize.culture.{0}.js";
579             string aggregateFileName = "globalize.cultures.js";
580             foreach (string param in string.Join(" ", args).SplitCommandLine()) {
581                 if (param.StartsWith("/o:")) {
582                     outputdir = param.Substring("/o:".Length);
583                 }
584                 else if (param == "/?") {
585                     Console.Write(@"
586 Usage:glob-generator [<options>]
587
588 options:
589
590     /o: The directory to put the culture scripts into. The directory will be
591         created if it does not exist. Existing scripts there will be
592         overwritten if necessary.
593         default: ""lib\cultures""
594
595 ");
596                     return;
597                 }
598             }
599             Directory.CreateDirectory(outputdir);
600             GlobalizationInfo.BasisGlobInfo = GlobalizationInfo.GetGlobInfo(CultureInfo.CreateSpecificCulture("en")).ToDictionary(false);
601
602             StringBuilder aggregateScript = new StringBuilder();
603
604 /*
605
606 Globalize.addCultureInfo( ""{0}"", ""default"", {{
607 {1}
608 }});
609
610 }}( this ));
611 "
612 */
613
614             aggregateScript.Append(
615     @"/*
616  * Globalize Cultures
617  *
618  * http://github.com/jquery/globalize
619  *
620  * Copyright Software Freedom Conservancy, Inc.
621  * Dual licensed under the MIT or GPL Version 2 licenses.
622  * http://jquery.org/license
623  *
624  * This file was generated by the Globalize Culture Generator
625  * Translation: bugs found in this file need to be fixed in the generator
626  */
627
628 (function( window, undefined ) {
629
630 var Globalize;
631
632 if ( typeof require !== ""undefined""
633         && typeof exports !== ""undefined""
634         && typeof module !== ""undefined"" ) {
635         // Assume CommonJS
636         Globalize = require( ""globalize"" );
637 } else {
638         // Global variable
639         Globalize = window.Globalize;
640 }
641 ");
642
643             int count = 0;
644             foreach (var culture in CultureInfo.GetCultures(CultureTypes.AllCultures)) {
645                 if (!String.IsNullOrEmpty(culture.Name) && culture != CultureInfo.InvariantCulture && culture.Name != "en") {
646                     WriteCulture(outputdir, fileName, extend, global, culture, aggregateScript);
647                     count++;
648                 }
649             }
650
651             aggregateScript.Append("\r\n}( this ));\r\n");
652             string aggregateScriptString = aggregateScript.ToString();
653             string aggregatePath = Path.Combine(outputdir, aggregateFileName);
654             File.WriteAllText(aggregatePath, aggregateScriptString);
655             Console.WriteLine(aggregatePath);
656
657             Console.WriteLine("Done! Generated scripts for a total of {0} cultures, and 1 aggregate script.", count);
658         }
659     }
660 }