aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChirayu Krishnappa2013-03-08 15:37:56 -0800
committerIgor Minar2013-04-04 09:28:23 -0700
commit139c5320191ec5e9f37ba689c5e8e917087f6bfb (patch)
treef10def71eb791afcbd7b79d7eb29d31f1a337147
parente5b57bf01c3e71a7048ef07986628f8809281dbe (diff)
downloadangular.js-139c5320191ec5e9f37ba689c5e8e917087f6bfb.tar.bz2
chore($ngLocale): refactor slurper & parse extended datetime symbols
-rwxr-xr-xi18n/run-tests.sh5
-rw-r--r--i18n/spec/closureI18nExtractorSpec.js250
-rw-r--r--i18n/spec/parserSpec.js2
-rw-r--r--i18n/src/closureI18nExtractor.js175
-rwxr-xr-xi18n/src/closureSlurper.js155
-rw-r--r--i18n/src/parser.js4
-rwxr-xr-xi18n/update-closure.sh5
-rw-r--r--package.json11
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"