mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-16 23:30:23 +00:00
chore($ngLocale): refactor i18n closure slurper logic and parse extended datetime symbols
This commit is contained in:
parent
0c72708a2b
commit
23abb990f1
8 changed files with 503 additions and 110 deletions
5
i18n/run-tests.sh
Executable file
5
i18n/run-tests.sh
Executable file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
PARENT_DIR="$(dirname "$0")"
|
||||||
|
jasmine-node "$PARENT_DIR"/spec/
|
||||||
250
i18n/spec/closureI18nExtractorSpec.js
Normal file
250
i18n/spec/closureI18nExtractorSpec.js
Normal file
|
|
@ -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");
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
@ -24,6 +24,8 @@ describe('parsePattern', function() {
|
||||||
parseAndExpect('#,##,##0.###', '', '-', '', '', 1, 0, 3, 2, 3);
|
parseAndExpect('#,##,##0.###', '', '-', '', '', 1, 0, 3, 2, 3);
|
||||||
parseAndExpect("#,##0.###;\'\u202A\'-#,##0.###\'\u202C\'",
|
parseAndExpect("#,##0.###;\'\u202A\'-#,##0.###\'\u202C\'",
|
||||||
'', '\u202A-', '', '\u202C', 1, 0, 3, 3, 3);
|
'', '\u202A-', '', '\u202C', 1, 0, 3, 3, 3);
|
||||||
|
parseAndExpect('#0.###;#0.###-', '', '', '', '-', 1, 0, 3, 0, 0);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse CURRENCY patterns', function() {
|
it('should parse CURRENCY patterns', function() {
|
||||||
|
|
|
||||||
175
i18n/src/closureI18nExtractor.js
Normal file
175
i18n/src/closureI18nExtractor.js
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -1,130 +1,85 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Q = require('qq'),
|
var Q = require('q'),
|
||||||
qfs = require('q-fs'),
|
qfs = require('q-fs'),
|
||||||
converter = require('./converter.js'),
|
converter = require('./converter.js'),
|
||||||
util = require('./util.js'),
|
util = require('./util.js'),
|
||||||
|
closureI18nExtractor = require('./closureI18nExtractor.js'),
|
||||||
localeInfo = {},
|
localeInfo = {},
|
||||||
localeIds = [],
|
|
||||||
currencySymbols,
|
currencySymbols,
|
||||||
goog = { provide: function() {},
|
goog = { provide: function() {},
|
||||||
require: function() {},
|
require: function() {},
|
||||||
i18n: {currency: {}, pluralRules: {}} };
|
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) {
|
var NG_LOCALE_DIR = '../src/ngLocale/';
|
||||||
eval(content.toString());
|
|
||||||
currencySymbols = goog.i18n.currency.CurrencyInfo;
|
|
||||||
currencySymbols.__proto__ = goog.i18n.currency.CurrencyInfoTier2;
|
|
||||||
|
|
||||||
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();
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return Q.all([numericStagePromise, datetimeStagePromise]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractPlurals() {
|
||||||
|
console.log('Extracting Plurals ...');
|
||||||
|
return qfs.read(__dirname + '/../closure/pluralRules.js').then(function(content) {
|
||||||
|
closureI18nExtractor.pluralExtractor(content, localeInfo);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeLocaleFiles() {
|
||||||
qfs.read(__dirname + '/../closure/datetimeSymbols.js', 'b').then(function(content) {
|
console.log('Final stage: Writing angular locale files to directory: %j', NG_LOCALE_DIR);
|
||||||
eval(content.toString());
|
var writePromises = [];
|
||||||
for (var propName in goog.i18n) {
|
var localeIds = Object.keys(localeInfo);
|
||||||
var localeID = util.findLocaleId(propName, 'datetime');
|
var num_files = 0;
|
||||||
if (localeID) {
|
|
||||||
if (!localeInfo[localeID]) {
|
|
||||||
localeInfo[localeID] = {};
|
|
||||||
localeIds.push(localeID);
|
|
||||||
}
|
|
||||||
var convertedData = converter.convertDatetimeData(goog.i18n[propName]);
|
|
||||||
localeInfo[localeID].DATETIME_FORMATS = convertedData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
promiseB.resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
return Q.join(promiseA.promise, promiseB.promise, noop);
|
|
||||||
}).then(function() {
|
|
||||||
var promise = Q.defer();
|
|
||||||
|
|
||||||
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, '');
|
|
||||||
|
|
||||||
///@@ is a crazy place holder to be replaced before writing to file
|
|
||||||
localeInfo[localeIds[i]].pluralCat = "@@" + temp + "@@";
|
|
||||||
}
|
|
||||||
promise.resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
return promise.promise;
|
|
||||||
}).then(function() {
|
|
||||||
localeIds.forEach(function(localeID) {
|
localeIds.forEach(function(localeID) {
|
||||||
var fallBackID = localeID.match(/[A-Za-z]+/)[0],
|
var content = closureI18nExtractor.outputLocale(localeInfo, localeID);
|
||||||
localeObj = localeInfo[localeID],
|
if (!content) return;
|
||||||
fallBackObj = localeInfo[fallBackID];
|
var correctedLocaleId = closureI18nExtractor.correctedLocaleId(localeID);
|
||||||
|
var filename = NG_LOCALE_DIR + 'angular-locale_' + correctedLocaleId + '.js'
|
||||||
// fallBack to language formats when country format is missing
|
writePromises.push(
|
||||||
// e.g. if NUMBER_FORMATS of en_xyz is not present, use the NUMBER_FORMATS of en instead
|
qfs.write(filename, content)
|
||||||
if (!localeObj.NUMBER_FORMATS) {
|
.then(function () {
|
||||||
localeObj.NUMBER_FORMATS = fallBackObj.NUMBER_FORMATS;
|
console.log('Wrote ' + filename);
|
||||||
}
|
++num_files;
|
||||||
|
}));
|
||||||
if (!localeObj.DATETIME_FORMATS) {
|
console.log('Writing ' + filename);
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
console.log('Generated ' + localeIds.length + ' locale files!');
|
console.log('Generated %j locale files.', localeIds.length);
|
||||||
}).end();
|
return Q.all(writePromises).then(function() { return num_files });
|
||||||
|
}
|
||||||
function noop() {};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a folder under current directory.
|
* Make a folder under current directory.
|
||||||
* @param folder {string} name of the folder to be made
|
* @param folder {string} name of the folder to be made
|
||||||
*/
|
*/
|
||||||
function createFolder(folder) {
|
function createFolder(folder) {
|
||||||
return qfs.isDirectory(__dirname + '/' + folder).then(function(isDir) {
|
return qfs.isDirectory(folder).then(function(isDir) {
|
||||||
if (!isDir) return qfs.makeDirectory(__dirname + '/' + folder);
|
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); });
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@ function parsePattern(pattern) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var groups = integer.split(GROUP_SEP);
|
var groups = integer.split(GROUP_SEP);
|
||||||
p.gSize = groups[1].length;
|
p.gSize = groups[1] ? groups[1].length : 0;
|
||||||
p.lgSize = (groups[2] || groups[1]).length;
|
p.lgSize = (groups[2] || groups[1]) ? (groups[2] || groups[1]).length : 0;
|
||||||
|
|
||||||
if (negative) {
|
if (negative) {
|
||||||
var trunkLen = positive.length - p.posPre.length - p.posSuf.length,
|
var trunkLen = positive.length - p.posPre.length - p.posSuf.length,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e # Exit on error.
|
||||||
|
|
||||||
BASE_DIR=`dirname $0`
|
BASE_DIR=`dirname $0`
|
||||||
cd $BASE_DIR
|
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/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/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/numberformatsymbols.js > closure/numberSymbols.js
|
||||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/pluralrules.js > closure/pluralRules.js
|
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/pluralrules.js > closure/pluralRules.js
|
||||||
|
|
|
||||||
13
package.json
13
package.json
|
|
@ -2,16 +2,17 @@
|
||||||
"name": "AngularJS",
|
"name": "AngularJS",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"testacular": "0.5.9",
|
|
||||||
"jasmine-node": "1.2.3",
|
|
||||||
"q-fs": "0.1.36",
|
|
||||||
"qq": "0.3.5",
|
|
||||||
"grunt": "0.4.0",
|
"grunt": "0.4.0",
|
||||||
"grunt-contrib-clean": "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-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",
|
"shelljs": "0.1.2",
|
||||||
|
"testacular": "0.5.9",
|
||||||
"yaml-js": "0.0.5"
|
"yaml-js": "0.0.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue