diff options
| author | Chirayu Krishnappa | 2013-03-08 15:37:56 -0800 |
|---|---|---|
| committer | Chirayu Krishnappa | 2013-03-20 16:19:46 -0700 |
| commit | 23abb990f10270f7992429ef83f86b83c91f75ea (patch) | |
| tree | 7b86997100842a30c03b972710822afa6d2b526c /i18n/src/closureI18nExtractor.js | |
| parent | 0c72708a2bb123d2772f5c6b8cb6adc61f197a72 (diff) | |
| download | angular.js-23abb990f10270f7992429ef83f86b83c91f75ea.tar.bz2 | |
chore($ngLocale): refactor i18n closure slurper logic and parse extended datetime symbols
Diffstat (limited to 'i18n/src/closureI18nExtractor.js')
| -rw-r--r-- | i18n/src/closureI18nExtractor.js | 175 |
1 files changed, 175 insertions, 0 deletions
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; +} |
