diff options
| author | Chirayu Krishnappa | 2013-03-08 15:37:56 -0800 | 
|---|---|---|
| committer | Igor Minar | 2013-04-04 09:28:23 -0700 | 
| commit | 139c5320191ec5e9f37ba689c5e8e917087f6bfb (patch) | |
| tree | f10def71eb791afcbd7b79d7eb29d31f1a337147 | |
| parent | e5b57bf01c3e71a7048ef07986628f8809281dbe (diff) | |
| download | angular.js-139c5320191ec5e9f37ba689c5e8e917087f6bfb.tar.bz2 | |
chore($ngLocale): refactor slurper & parse extended datetime symbols
| -rwxr-xr-x | i18n/run-tests.sh | 5 | ||||
| -rw-r--r-- | i18n/spec/closureI18nExtractorSpec.js | 250 | ||||
| -rw-r--r-- | i18n/spec/parserSpec.js | 2 | ||||
| -rw-r--r-- | i18n/src/closureI18nExtractor.js | 175 | ||||
| -rwxr-xr-x | i18n/src/closureSlurper.js | 155 | ||||
| -rw-r--r-- | i18n/src/parser.js | 4 | ||||
| -rwxr-xr-x | i18n/update-closure.sh | 5 | ||||
| -rw-r--r-- | package.json | 11 | 
8 files changed, 500 insertions, 107 deletions
| diff --git a/i18n/run-tests.sh b/i18n/run-tests.sh new file mode 100755 index 00000000..68b13401 --- /dev/null +++ b/i18n/run-tests.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -e +PARENT_DIR="$(dirname "$0")" +jasmine-node "$PARENT_DIR"/spec/ diff --git a/i18n/spec/closureI18nExtractorSpec.js b/i18n/spec/closureI18nExtractorSpec.js new file mode 100644 index 00000000..f4f190d5 --- /dev/null +++ b/i18n/spec/closureI18nExtractorSpec.js @@ -0,0 +1,250 @@ +var closureI18nExtractor = require('../src/closureI18nExtractor.js'); +var converter = require('../src/converter.js'); +findLocaleId = closureI18nExtractor.findLocaleId; +extractNumberSymbols = closureI18nExtractor.extractNumberSymbols; +extractCurrencySymbols = closureI18nExtractor.extractCurrencySymbols; +extractDateTimeSymbols = closureI18nExtractor.extractDateTimeSymbols; + + +function newTestLocaleInfo() { +  return { fr_CA: { +    DATETIME_FORMATS: { +      MONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', +        'octobre', 'novembre', 'décembre'], +      SHORTMONTH: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', +        'nov.', 'déc.'], +      DAY: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'], +      SHORTDAY: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'], +      AMPMS: ['AM', 'PM'], +      medium: 'yyyy-MM-dd HH:mm:ss', +      short: 'yy-MM-dd HH:mm', +      fullDate: 'EEEE d MMMM y', +      longDate: 'd MMMM y', +      mediumDate: 'yyyy-MM-dd', +      shortDate: 'yy-MM-dd', +      mediumTime: 'HH:mm:ss', +      shortTime: 'HH:mm' +    }, +    NUMBER_FORMATS: { +      "DECIMAL_SEP": ".", +      "GROUP_SEP": ",", +      "PATTERNS": [{ +        "minInt": 1, +        "minFrac": 0, +        "macFrac": 0, +        "posPre": "", +        "posSuf": "", +        "negPre": "-", +        "negSuf": "", +        "gSize": 3, +        "lgSize": 3, +        "maxFrac": 3 +      }, { +        "minInt": 1, +        "minFrac": 2, +        "macFrac": 0, +        "posPre": "¤", +        "posSuf": "", +        "negPre": "¤-", +        "negSuf": "", +        "gSize": 3, +        "lgSize": 3, +        "maxFrac": 2 +      }], +      "CURRENCY_SYM": "£" +    }}}; +} + + +describe("findLocaleId", function () { +  it("should find the id from numbers", function() { +    expect(findLocaleId("NumberFormatSymbols_en_GB", "num")).toEqual("en_GB"); +  }); + + +  it("should find the id from datetime", function() { +    expect(findLocaleId("DateTimeSymbols_en_ISO", "datetime")).toEqual("en_ISO"); +  }); + + +  it("should throw an error otherwise", function() { +    expect(function() { +      findLocaleId("str", "otherwise") +    }).toThrow("unknown type in findLocaleId: otherwise"); +  }); +}); + +describe("extractNumberSymbols", function () { +  it("should extract number data", function() { +    var CONTENT = [ +      "goog.provide('goog.i18n.NumberFormatSymbols_en_GB');", +      "goog.i18n.NumberFormatSymbols_en_GB = {", +      "DECIMAL_SEP: '.',", +      "GROUP_SEP: ',',", +      "PERCENT: '%',", +      "ZERO_DIGIT: '0',", +      "PLUS_SIGN: '+',", +      "MINUS_SIGN: '-',", +      "EXP_SYMBOL: 'E',", +      "PERMILL: '\u2030',", +      "INFINITY: '\u221E',", +      "NAN: 'NaN',", +      "DECIMAL_PATTERN: '#,##0.###',", +      "SCIENTIFIC_PATTERN: '#E0',", +      "PERCENT_PATTERN: '#,##0%',", +      "CURRENCY_PATTERN: '\u00A4#,##0.00',", +      "DEF_CURRENCY_CODE: 'GBP' };" +    ].join('\n'); + +    var currencySymbols = {'GBP':[2, '£', 'GB£']}; + +    var expectedNumberFormats = converter.convertNumberData( +        { +          DECIMAL_SEP:'.', +          GROUP_SEP:',', +          DECIMAL_PATTERN:'#,##0.###', +          CURRENCY_PATTERN:'\u00A4#,##0.00', +          DEF_CURRENCY_CODE: 'GBP' +        }, currencySymbols +    ); + +    var localeInfo = {}; +    extractNumberSymbols(CONTENT, localeInfo, currencySymbols); + +    expect(localeInfo).toEqual({ +      'en_GB': { NUMBER_FORMATS: expectedNumberFormats } +    }); +  }) +}); + +describe("extractCurrencySymbols", function () { +  it("should extract currency data", function() { +    var CONTENT = [ +      "goog.i18n.currency.CurrencyInfo = {", +      "  'GBP':[2, '£', 'GB£'],", +      "};", +      "goog.i18n.currency.CurrencyInfoTier2 = {", +      "  'AOA':[2, 'Kz', 'Kz'],", +      "};" +    ].join('\n'); + +    var localeInfo = {}; +    expect(extractCurrencySymbols(CONTENT)).toEqual({ +      'GBP':[2, '£', 'GB£'], +      'AOA':[2, 'Kz', 'Kz'] +    }); +  }); +}); + + +describe("extractDateTimeSymbols", function () { +  it("should extract date time data", function() { +    var CONTENT = [ +"goog.i18n.DateTimeSymbols_fr_CA = {", +"  ERAS: ['av. J.-C.', 'ap. J.-C.'],", +"  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],", +"  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],", +"  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',", +"      'N', 'D'],", +"  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',", +"      'août', 'septembre', 'octobre', 'novembre', 'décembre'],", +"  STANDALONEMONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin',", +"      'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],", +"  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',", +"      'août', 'sept.', 'oct.', 'nov.', 'déc.'],", +"  STANDALONESHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin',", +"      'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'],", +"  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',", +"      'samedi'],", +"  STANDALONEWEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi',", +"      'vendredi', 'samedi'],", +"  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],", +"  STANDALONESHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.',", +"      'sam.'],", +"  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],", +"  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],", +"  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],", +"  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],", +"  AMPMS: ['AM', 'PM'],", +"  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'yyyy-MM-dd', 'yy-MM-dd'],", +"  TIMEFORMATS: ['HH \\'h\\' mm \\'min\\' ss \\'s\\' zzzz', 'HH:mm:ss z',", +"      'HH:mm:ss', 'HH:mm'],", +"  FIRSTDAYOFWEEK: 6,", +"  WEEKENDRANGE: [5, 6],", +"  FIRSTWEEKCUTOFFDAY: 2", +"};" +    ].join('\n'); +      var localeInfo = {}; +      var expectedLocaleInfo = { +          fr_CA: { +              DATETIME_FORMATS: { +                  MONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', +                      'octobre', 'novembre', 'décembre'], +                  SHORTMONTH: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', +                      'nov.', 'déc.'], +                  DAY: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'], +                  SHORTDAY: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'], +                  AMPMS: ['AM', 'PM'], +                  medium: 'yyyy-MM-dd HH:mm:ss', +                  short: 'yy-MM-dd HH:mm', +                  fullDate: 'EEEE d MMMM y', +                  longDate: 'd MMMM y', +                  mediumDate: 'yyyy-MM-dd', +                  shortDate: 'yy-MM-dd', +                  mediumTime: 'HH:mm:ss', +                  shortTime: 'HH:mm' +              } +            } +          }; +      extractDateTimeSymbols(CONTENT, localeInfo); +      expect(localeInfo).toEqual(expectedLocaleInfo); +      }) +}); + +describe("pluralExtractor", function() { +  it("should output PLURAL_CAT in the output string code", function() { +    var localeIds = ["fr_CA"]; +    var content = ( +        "goog.provide('goog.i18n.pluralRules');\n" + +        "\n" + +        "goog.i18n.pluralRules.Keyword = {\n" + +        "  ZERO: 'zero',\n" + +        "  ONE: 'one',\n" + +        "  TWO: 'two',\n" + +        "  FEW: 'few',\n" + +        "  MANY: 'many',\n" + +        "  OTHER: 'other'\n" + +        "};\n" + +        "\n" + +        "goog.i18n.pluralRules.frSelect_ = function(n) {\n" + +        "  if (n >= 0 && n < 2) {\n" + +        "    return goog.i18n.pluralRules.Keyword.ONE;\n" + +        "  }\n" + +        "  return goog.i18n.pluralRules.Keyword.OTHER;\n" + +        "};\n" + +        "\n" + +        "if (goog.LOCALE == 'fr') {\n" + +        "  goog.i18n.pluralRules.select = goog.i18n.pluralRules.frSelect_;\n" + +        "}" +        ); +    var localeInfo = newTestLocaleInfo(); +    closureI18nExtractor.pluralExtractor(content, localeInfo); +    var pluralCat = localeInfo["fr_CA"].pluralCat; +    expect(pluralCat).toBeDefined(); +    // pluralCat is the source text for the pluralCat and contains @@ +    // placeholders that need to be stripped before evaluation. +    // Ref: closureI18nExtractor.pluralExtractor. +    pluralCat = pluralCat.replace(/^@@|@@$/g, ''); +    // pluralCat requires these constants to exist. +    var PLURAL_CATEGORY = { +      ZERO: "zero", ONE: "one", TWO: "two", +      FEW: "few", MANY: "many", OTHER: "other" +      }; +    // Obtain the function by evaluating the source text. +    pluralCat = eval("(" + pluralCat + ")"); +    // Confirm some expectations for pluralCat in fr_CA. +    expect(pluralCat(0)).toEqual("one"); +    expect(pluralCat(3)).toEqual("other"); +  }) +}); + diff --git a/i18n/spec/parserSpec.js b/i18n/spec/parserSpec.js index 2904e31c..e9aff2be 100644 --- a/i18n/spec/parserSpec.js +++ b/i18n/spec/parserSpec.js @@ -24,6 +24,8 @@ describe('parsePattern', function() {      parseAndExpect('#,##,##0.###', '', '-', '', '', 1, 0, 3, 2, 3);      parseAndExpect("#,##0.###;\'\u202A\'-#,##0.###\'\u202C\'",          '', '\u202A-', '', '\u202C', 1, 0, 3, 3, 3); +    parseAndExpect('#0.###;#0.###-', '', '', '', '-', 1, 0, 3, 0, 0); +    });    it('should parse CURRENCY patterns', function() { diff --git a/i18n/src/closureI18nExtractor.js b/i18n/src/closureI18nExtractor.js new file mode 100644 index 00000000..a26f89b7 --- /dev/null +++ b/i18n/src/closureI18nExtractor.js @@ -0,0 +1,175 @@ +'use strict'; + +var converter = require('./converter.js'); + +exports.extractNumberSymbols = extractNumberSymbols; +exports.extractCurrencySymbols = extractCurrencySymbols; +exports.extractDateTimeSymbols = extractDateTimeSymbols; +exports.pluralExtractor = pluralExtractor; +exports.outputLocale = outputLocale; +exports.correctedLocaleId = correctedLocaleId; +exports.findLocaleId = findLocaleId; + +var goog = { provide: function() {}, +  require: function() {}, +  i18n: {currency: {}, pluralRules: {}} }; + +function findLocaleId(str, type) { +  if (type === 'num') { +    return (str.match(/^NumberFormatSymbols_(.+)$/) || [])[1]; +  } + +  if (type != 'datetime') { throw new Error('unknown type in findLocaleId: ' + type); } + +  return (str.match(/^DateTimeSymbols_(.+)$/) || [])[1]; +} + + +function getInfoForLocale(localeInfo, localeID) { +  if (!localeInfo[localeID]) { +    localeInfo[localeID] = {}; +    //localeIds.push(localeID); +  } +  return localeInfo[localeID]; +} + +function extractNumberSymbols(content, localeInfo, currencySymbols) { +  //eval script in the current context so that we get access to all the symbols +  eval(content.toString()); +  for (var propName in goog.i18n) { +    var localeID = findLocaleId(propName, 'num'); +    if (localeID) { +      var info = getInfoForLocale(localeInfo, localeID); +      info.NUMBER_FORMATS = +          converter.convertNumberData(goog.i18n[propName], currencySymbols); +    } +  } +} + +function extractCurrencySymbols(content) { +  //eval script in the current context so that we get access to all the symbols +  eval(content.toString()); +  var currencySymbols = goog.i18n.currency.CurrencyInfo; +  currencySymbols.__proto__ = goog.i18n.currency.CurrencyInfoTier2; + +  return currencySymbols; +} + +function extractDateTimeSymbols(content, localeInfo) { +  //eval script in the current context so that we get access to all the symbols +  eval(content.toString()); +  for (var propName in goog.i18n) { +    var localeID = findLocaleId(propName, 'datetime'); +    if (localeID) { +      var info = getInfoForLocale(localeInfo, localeID); +      localeInfo[localeID].DATETIME_FORMATS = +          converter.convertDatetimeData(goog.i18n[propName]); +    } +  } +} + +function pluralExtractor(content, localeInfo) { +  var contentText = content.toString(); +  var localeIds = Object.keys(localeInfo); +  for (var i = 0; i < localeIds.length; i++) { +    //We don't need to care about country ID because the plural rules in more specific id are +    //always the same as those in its language ID. +    // e.g. plural rules for en_SG is the same as those for en. +    goog.LOCALE = localeIds[i].match(/[^_]+/)[0]; +    try { +      eval(contentText); +    } catch(e) { +      console.log("Error in eval(contentText): " + e.stack); +    } +    if (!goog.i18n.pluralRules.select) { +      console.log('No select for lang [' + goog.LOCALE + ']'); +      continue; +    } +    var temp = goog.i18n.pluralRules.select.toString(). +        replace(/goog.i18n.pluralRules.Keyword/g, 'PLURAL_CATEGORY').replace(/\n/g, ''); + +    ///@@ is a crazy place holder to be replaced before writing to file +    localeInfo[localeIds[i]].pluralCat = "@@" + temp + "@@"; +  } +} + +function correctedLocaleId(localeID) { +// e.g. from zh_CN to zh-CN, from en_US to en-US +  return localeID.replace(/_/g, '-').toLowerCase(); +} + +function canonicalizeForJsonStringify(unused_key, object) { +  // This function is intended to be called as the 2nd argument to +  // JSON.stringify.  The goal here is to ensure that the generated JSON has +  // objects with their keys in ascending order.  Without this, it's much +  // harder to diff the generated files in src/ngLocale as the order isn't +  // exactly consistent.  We've gotten lucky in the past. +  // +  // Iteration order, for string keys, ends up being the same as insertion +  // order.  Refer :- +  //    1. http://ejohn.org/blog/javascript-in-chrome/ +  //       (search for "for loop order"). +  //       Currently all major browsers loop over the properties of an object +  //       in the order in which they were defined. +  //         - John Resig +  //    2. https://code.google.com/p/v8/issues/detail?id=164 +  //       ECMA-262 does not specify enumeration order. The de facto standard +  //       is to match insertion order, which V8 also does ... +  if (typeof object != "object") { +    return object; +  } +  var result = {}; +  Object.keys(object).sort().forEach(function(key) { +    result[key] = object[key]; +  }); +  return result; +} + +function outputLocale(localeInfo, localeID) { +  var fallBackID = localeID.match(/[A-Za-z]+/)[0], +      localeObj = localeInfo[localeID], +      fallBackObj = localeInfo[fallBackID]; + +  // fallBack to language formats when country format is missing +  // e.g. if NUMBER_FORMATS of en_xyz is not present, use the NUMBER_FORMATS of en instead +  if (!localeObj.NUMBER_FORMATS) { +    localeObj.NUMBER_FORMATS = fallBackObj.NUMBER_FORMATS; +  } + +  // datetimesymbolsext.js provides more top level locales than the other +  // files.  We process datetimesymbolsext.js because we want the country +  // specific formats that are missing from datetimesymbols.js.  However, we +  // don't want to write locale files that only have dateformat (i.e. missing +  // number formats.)  So we skip them. +  if (!localeObj.NUMBER_FORMATS) { +    console.log("Skipping locale %j: Don't have any number formats", localeID); +    return null; +  } + +  if (!localeObj.DATETIME_FORMATS) { +    localeObj.DATETIME_FORMATS = fallBackObj.DATETIME_FORMATS; +  } +  localeObj.id = correctedLocaleId(localeID); + +  var prefix = +      'angular.module("ngLocale", [], ["$provide", function($provide) {\n' + +          'var PLURAL_CATEGORY = {' + +          'ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"' + +          '};\n' + +          '$provide.value("$locale", '; + +  var suffix = ');\n}]);'; + +  localeObj = { +    DATETIME_FORMATS: localeObj.DATETIME_FORMATS, +    NUMBER_FORMATS: localeObj.NUMBER_FORMATS, +    pluralCat: localeObj.pluralCat, +    id: localeObj.id +  }; + +  var content = JSON.stringify(localeInfo[localeID], canonicalizeForJsonStringify, '  ') +      .replace(/\¤/g, '\\u00A4') +      .replace(/"@@|@@"/g, ''); + +  return prefix + content + suffix; +} diff --git a/i18n/src/closureSlurper.js b/i18n/src/closureSlurper.js index 2e78ef64..9f236cf2 100755 --- a/i18n/src/closureSlurper.js +++ b/i18n/src/closureSlurper.js @@ -1,130 +1,85 @@  #!/usr/bin/env node  'use strict'; -var Q  = require('qq'), +var Q  = require('q'),      qfs  = require('q-fs'),      converter = require('./converter.js'),      util = require('./util.js'), +    closureI18nExtractor = require('./closureI18nExtractor.js'),      localeInfo = {}, -    localeIds = [],      currencySymbols,      goog = { provide: function() {},               require: function() {},               i18n: {currency: {}, pluralRules: {}} }; -createFolder('../../src/ngLocale/').then(function() { -  var promiseA = Q.defer(), -      promiseB = Q.defer(); -  qfs.read(__dirname + '/../closure/currencySymbols.js', 'b').then(function(content) { -    eval(content.toString()); -    currencySymbols = goog.i18n.currency.CurrencyInfo; -    currencySymbols.__proto__ = goog.i18n.currency.CurrencyInfoTier2; +var NG_LOCALE_DIR = '../src/ngLocale/'; -    qfs.read(__dirname + '/../closure/numberSymbols.js', 'b').then(function(content) { -      //eval script in the current context so that we get access to all the symbols -      eval(content.toString()); -      for (var propName in goog.i18n) { -        var localeID = util.findLocaleId(propName, 'num'); -        if (localeID) { -          if (!localeInfo[localeID]) { -            localeInfo[localeID] = {}; -            localeIds.push(localeID); -          } -          var convertedData = converter.convertNumberData(goog.i18n[propName], currencySymbols); -          localeInfo[localeID].NUMBER_FORMATS = convertedData; -        } -      } -      promiseA.resolve(); -    }); -  }); - - -  qfs.read(__dirname + '/../closure/datetimeSymbols.js', 'b').then(function(content) { -    eval(content.toString()); -    for (var propName in goog.i18n) { -      var localeID = util.findLocaleId(propName, 'datetime'); -      if (localeID) { -        if (!localeInfo[localeID]) { -          localeInfo[localeID] = {}; -          localeIds.push(localeID); -        } -        var convertedData = converter.convertDatetimeData(goog.i18n[propName]); -        localeInfo[localeID].DATETIME_FORMATS = convertedData; -      } -    } - -    promiseB.resolve(); -  }); +function readSymbols() { +  console.log("Processing currency and number symbols ..."); +  var numericStagePromise = qfs.read(__dirname + '/../closure/currencySymbols.js', 'b') +    .then(function(content) { +      var currencySymbols = closureI18nExtractor.extractCurrencySymbols(content); +      return qfs.read(__dirname + '/../closure/numberSymbols.js', 'b').then(function(content) { +          closureI18nExtractor.extractNumberSymbols(content, localeInfo, currencySymbols); +        }); +      }); -  return Q.join(promiseA.promise, promiseB.promise, noop); -}).then(function() { -  var promise = Q.defer(); +  console.log("Processing datetime symbols ..."); +  var datetimeStagePromise = qfs.read(__dirname + '/../closure/datetimeSymbols.js', 'b') +      .then(function(content) { +        closureI18nExtractor.extractDateTimeSymbols(content, localeInfo); +        return qfs.read(__dirname + '/../closure/datetimeSymbolsExt.js', 'b').then(function(content) { +            closureI18nExtractor.extractDateTimeSymbols(content, localeInfo); +        }); +    }); -  qfs.read(__dirname + '/../closure/pluralRules.js').then(function(content) { -    for(var i = 0; i < localeIds.length; i++) { -      //We don't need to care about country ID because the plural rules in more specific id are -      //always the same as those in its language ID. -      // e.g. plural rules for en_SG is the same as those for en. -      goog.LOCALE = localeIds[i].match(/[^_]+/)[0]; -      eval(content); -      var temp = goog.i18n.pluralRules.select.toString(). -                     replace(/goog.i18n.pluralRules.Keyword/g, 'PLURAL_CATEGORY').replace(/\n/g, ''); +    return Q.all([numericStagePromise, datetimeStagePromise]); +} -      ///@@ is a crazy place holder to be replaced before writing to file -      localeInfo[localeIds[i]].pluralCat = "@@" + temp + "@@"; -    } -    promise.resolve(); +function extractPlurals() { +  console.log('Extracting Plurals ...'); +  return qfs.read(__dirname + '/../closure/pluralRules.js').then(function(content) { +    closureI18nExtractor.pluralExtractor(content, localeInfo);    }); +} -  return promise.promise; -}).then(function() { +function writeLocaleFiles() { +  console.log('Final stage: Writing angular locale files to directory: %j', NG_LOCALE_DIR); +  var writePromises = []; +  var localeIds = Object.keys(localeInfo); +  var num_files = 0;    localeIds.forEach(function(localeID) { -    var fallBackID = localeID.match(/[A-Za-z]+/)[0], -        localeObj = localeInfo[localeID], -        fallBackObj = localeInfo[fallBackID]; - -    // fallBack to language formats when country format is missing -    // e.g. if NUMBER_FORMATS of en_xyz is not present, use the NUMBER_FORMATS of en instead -    if (!localeObj.NUMBER_FORMATS) { -      localeObj.NUMBER_FORMATS = fallBackObj.NUMBER_FORMATS; -    } - -    if (!localeObj.DATETIME_FORMATS) { -       localeObj.DATETIME_FORMATS = fallBackObj.DATETIME_FORMATS; -    } - -    // e.g. from zh_CN to zh-CN, from en_US to en-US -    var correctedLocaleId = localeID.replace(/_/g, '-').toLowerCase(); -    localeObj.id = correctedLocaleId; - -    var prefix = -      'angular.module("ngLocale", [], ["$provide", function($provide) {\n' + -         'var PLURAL_CATEGORY = {' + -           'ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"' + -         '};\n' + -         '$provide.value("$locale", '; - -    var suffix = ');\n}]);'; - -    var content = JSON.stringify(localeInfo[localeID]).replace(/\¤/g,'\\u00A4'). -                      replace(/"@@|@@"/g, ''); - -    var toWrite = prefix + content + suffix; -    qfs.write(__dirname + '/../locale/' + 'angular-locale_' + correctedLocaleId + '.js', toWrite); +    var content = closureI18nExtractor.outputLocale(localeInfo, localeID); +    if (!content) return; +    var correctedLocaleId = closureI18nExtractor.correctedLocaleId(localeID); +    var filename = NG_LOCALE_DIR + 'angular-locale_' + correctedLocaleId + '.js' +    writePromises.push( +      qfs.write(filename, content) +      .then(function () { +        console.log('Wrote ' + filename); +        ++num_files; +        })); +    console.log('Writing ' + filename);    }); -  console.log('Generated ' + localeIds.length + ' locale files!'); -}).end(); - -function noop() {}; +  console.log('Generated %j locale files.', localeIds.length); +  return Q.all(writePromises).then(function() { return num_files }); +}  /**  * Make a folder under current directory.  * @param folder {string} name of the folder to be made  */  function createFolder(folder) { -  return qfs.isDirectory(__dirname + '/' + folder).then(function(isDir) { -    if (!isDir) return qfs.makeDirectory(__dirname + '/' + folder); +  return qfs.isDirectory(folder).then(function(isDir) { +    if (!isDir) return qfs.makeDirectory(folder).then(function() { +        console.log('Created directory %j', folder); });    });  } + +createFolder(NG_LOCALE_DIR) +  .then(readSymbols) +  .then(extractPlurals) +  .then(writeLocaleFiles) +  .done(function(num_files) { console.log("Wrote %j files.\nAll Done!", num_files); }); diff --git a/i18n/src/parser.js b/i18n/src/parser.js index db31aeef..31dea510 100644 --- a/i18n/src/parser.js +++ b/i18n/src/parser.js @@ -45,8 +45,8 @@ function parsePattern(pattern) {    }    var groups = integer.split(GROUP_SEP); -  p.gSize = groups[1].length; -  p.lgSize = (groups[2] || groups[1]).length; +  p.gSize = groups[1] ? groups[1].length : 0; +  p.lgSize = (groups[2] || groups[1]) ? (groups[2] || groups[1]).length : 0;    if (negative) {      var trunkLen = positive.length - p.posPre.length - p.posSuf.length, diff --git a/i18n/update-closure.sh b/i18n/update-closure.sh index 80337f13..2ddf8892 100755 --- a/i18n/update-closure.sh +++ b/i18n/update-closure.sh @@ -1,9 +1,14 @@  #!/bin/bash +set -e  # Exit on error. +  BASE_DIR=`dirname $0`  cd $BASE_DIR +set -x  # Trace commands as they're executed. +  curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/currency.js > closure/currencySymbols.js  curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/datetimesymbols.js > closure/datetimeSymbols.js +curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/datetimesymbolsext.js > closure/datetimeSymbolsExt.js  curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/numberformatsymbols.js > closure/numberSymbols.js  curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/pluralrules.js > closure/pluralRules.js diff --git a/package.json b/package.json index 148895c4..771bea68 100644 --- a/package.json +++ b/package.json @@ -2,14 +2,15 @@    "name": "AngularJS",    "version": "0.0.0",    "dependencies": { -    "jasmine-node": "1.2.3", -    "q-fs": "0.1.36", -    "qq": "0.3.5",      "grunt": "0.4.0",      "grunt-contrib-clean": "0.4.0", -    "grunt-contrib-copy": "0.4.0", -    "grunt-contrib-connect": "0.1.2",      "grunt-contrib-compress": "0.4.1", +    "grunt-contrib-connect": "0.1.2", +    "grunt-contrib-copy": "0.4.0", +    "jasmine-node": "1.2.3", +    "q": "~0.9.2", +    "q-fs": "0.1.36", +    "qq": "0.3.5",      "shelljs": "0.1.2",      "karma": "0.8.4",      "yaml-js": "0.0.5" | 
