Skip to content

Commit c072695

Browse files
committed
Message: add support for custom formatters
Most globalize modules can now be used directly from within messages. Also fixes selectOrdinal not using the correct plural function. The messageformat compiler and runtime are forked from messageformat.js. Fixes globalizejs#563
1 parent 04ee130 commit c072695

File tree

22 files changed

+512
-94
lines changed

22 files changed

+512
-94
lines changed

Gruntfile.js

+25-54
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ module.exports = function( grunt ) {
123123
paths: {
124124
cldr: "../external/cldrjs/dist/cldr",
125125
"make-plural": "../external/make-plural/make-plural",
126-
messageformat: "../external/messageformat/messageformat"
126+
"messageformat-parser": "../node_modules/messageformat-parser/parser",
127+
"reserved-words": "../node_modules/reserved-words/lib/reserved-words"
127128
},
128129
skipSemiColonInsertion: true,
129130
skipModuleInsertion: true,
@@ -136,8 +137,7 @@ module.exports = function( grunt ) {
136137
// Only for root id's (the ones in src, not in src's subpaths). Note there's no
137138
// conditional code checking for this type.
138139
onBuildWrite: function( id, path, contents ) {
139-
var messageformat,
140-
name = camelCase( id.replace( /util\/|common\//, "" ) );
140+
var name = camelCase( id.replace( /util\/|common\//, "" ) );
141141

142142
// MakePlural
143143
if ( ( /make-plural/ ).test( id ) ) {
@@ -185,67 +185,38 @@ module.exports = function( grunt ) {
185185
"/* jshint ignore:end */"
186186
].join( "\n" ) );
187187

188-
// messageformat
189-
} else if ( ( /messageformat/ ).test( id ) ) {
190-
return contents
188+
// messageformat-parser
189+
} else if ( ( /messageformat-parser/.test( id ) ) ) {
191190

192-
// Remove browserify wrappers.
193-
.replace( /^\(function\(f\)\{if\(typeof exports==="object"&&type.*/, "" )
194-
.replace( "},{}],2:[function(require,module,exports){", "" )
195-
.replace( /\},\{"\.\/messageformat-parser":1,"make-plural\/plural.*/, "" )
196-
.replace( /\},\{\}\]\},\{\},\[2\]\)\(2\)[\s\S]*?$/, "" )
197-
198-
// Set `MessageFormat.plurals` and remove `make-plural/plurals`
199-
// completely. This is populated by Globalize on demand.
200-
.replace( /var _cp = \[[\s\S]*?$/, "" )
201-
.replace(
202-
"MessageFormat.plurals = require('make-plural/plurals')",
203-
"MessageFormat.plurals = {}"
204-
)
205-
206-
// Set `MessageFormat._parse`
207-
.replace(
208-
"MessageFormat._parse = require('./messageformat-parser').parse;",
209-
""
210-
)
211-
.replace( /module\.exports = \(function\(\) \{([\s\S]*?)\n\}\)\(\);/, [
212-
"MessageFormat._parse = (function() {",
213-
"$1",
214-
"}()).parse;"
191+
return contents
192+
.replace( /^/, [
193+
"var Parser;",
194+
"/* jshint ignore:start */\n",
195+
"Parser = (function() {"
215196
].join( "\n" ) )
197+
.replace( "module.exports = ", "return " )
198+
.replace( /$/, [
199+
"}());",
200+
"/* jshint ignore:end */"
201+
].join( "\n" ) );
216202

217-
// Remove unused code.
218-
.replace( /if \(!pluralFunc\) \{\n[\s\S]*?\n \}/, "" )
219-
.replace( /if \(!locale\) \{\n[\s\S]*? \}\n/, "this.lc = [locale];" )
220-
.replace( /(MessageFormat\.formatters) = \{[\s\S]*?\n\};/, "$1 = {};" )
221-
.replace( /MessageFormat\.prototype\.setIntlSupport[\s\S]*?\n\};/, "" )
203+
// reserved-words
204+
} else if ( ( /reserved-words/.test( id ) ) ) {
222205

223-
// Wrap everything into a var assignment.
224-
.replace( "module.exports = MessageFormat;", "" )
206+
return contents
225207
.replace( /^/, [
226-
"var MessageFormat;",
227-
"/* jshint ignore:start */",
228-
"MessageFormat = (function() {"
208+
"var reserved;",
209+
"/* jshint ignore:start */\n",
210+
"reserved = (function() {",
211+
"var exports = {};"
229212
].join( "\n" ) )
213+
.replace( "var assert = require\('assert'\);", "" )
214+
.replace( /^\s*assert\(.*;\s*$/gm, "" )
230215
.replace( /$/, [
231-
"return MessageFormat;",
216+
"return exports;",
232217
"}());",
233218
"/* jshint ignore:end */"
234219
].join( "\n" ) );
235-
236-
// message-runtime
237-
} else if ( ( /message-runtime/ ).test( id ) ) {
238-
messageformat = require( "./external/messageformat/messageformat" );
239-
delete messageformat.prototype.runtime.fmt;
240-
delete messageformat.prototype.runtime.pluralFuncs;
241-
contents = contents.replace( "Globalize._messageFormat = {};", [
242-
"/* jshint ignore:start */",
243-
"Globalize._messageFormat = (function() {",
244-
messageformat.prototype.runtime.toString(),
245-
"return {number: number, plural: plural, select: select};",
246-
"}());",
247-
"/* jshint ignore:end */"
248-
].join( "\n" ) );
249220
}
250221

251222
// 1, and 2: Remove define() wrap.

examples/amd-bower/main.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ require([
3636
"json!cldr-data/supplemental/currencyData.json",
3737
"json!cldr-data/supplemental/likelySubtags.json",
3838
"json!cldr-data/supplemental/plurals.json",
39+
"json!cldr-data/supplemental/ordinals.json",
3940
"json!cldr-data/supplemental/timeData.json",
4041
"json!cldr-data/supplemental/weekData.json",
4142
"json!messages/en.json",
@@ -49,7 +50,7 @@ require([
4950
"globalize/relative-time",
5051
"globalize/unit"
5152
], function( Globalize, enGregorian, enCurrencies, enDateFields, enNumbers, enUnits, currencyData,
52-
likelySubtags, pluralsData, timeData, weekData, messages ) {
53+
likelySubtags, pluralsData, ordinalsData, timeData, weekData, messages ) {
5354

5455
var en, like, number;
5556

@@ -63,6 +64,7 @@ require([
6364
enUnits,
6465
likelySubtags,
6566
pluralsData,
67+
ordinalsData,
6668
timeData,
6769
weekData
6870
);

examples/globalize-compiler/cldr.json

+8
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,14 @@
142142
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
143143
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
144144
}
145+
},
146+
"plurals-type-ordinal": {
147+
"en": {
148+
"pluralRule-count-one": "n % 10 = 1 and n % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …",
149+
"pluralRule-count-two": "n % 10 = 2 and n % 100 != 12 @integer 2, 22, 32, 42, 52, 62, 72, 82, 102, 1002, …",
150+
"pluralRule-count-few": "n % 10 = 3 and n % 100 != 13 @integer 3, 23, 33, 43, 53, 63, 73, 83, 103, 1003, …",
151+
"pluralRule-count-other": " @integer 0, 4~18, 100, 1000, 10000, 100000, 1000000, …"
152+
}
145153
}
146154
}
147155
}

examples/node-npm/main.js

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Globalize.load(
1111
require( "cldr-data/supplemental/currencyData" ),
1212
require( "cldr-data/supplemental/likelySubtags" ),
1313
require( "cldr-data/supplemental/plurals" ),
14+
require( "cldr-data/supplemental/ordinals" ),
1415
require( "cldr-data/supplemental/timeData" ),
1516
require( "cldr-data/supplemental/weekData" )
1617
);

examples/plain-javascript/index.html

+8
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,14 @@ <h2>Demo output</h2>
210210
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
211211
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
212212
}
213+
},
214+
"plurals-type-ordinal": {
215+
"en": {
216+
"pluralRule-count-one": "n % 10 = 1 and n % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …",
217+
"pluralRule-count-two": "n % 10 = 2 and n % 100 != 12 @integer 2, 22, 32, 42, 52, 62, 72, 82, 102, 1002, …",
218+
"pluralRule-count-few": "n % 10 = 3 and n % 100 != 13 @integer 3, 23, 33, 43, 53, 63, 73, 83, 103, 1003, …",
219+
"pluralRule-count-other": " @integer 0, 4~18, 100, 1000, 10000, 100000, 1000000, …"
220+
}
213221
}
214222
}
215223
});

package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,9 @@
8989
"grunt-git-authors": "^3.1.0",
9090
"grunt-jscs": "1.8.0",
9191
"gzip-js": "0.3.2",
92-
"matchdep": "0.3.0"
92+
"matchdep": "0.3.0",
93+
"messageformat-parser": "^1.0.0",
94+
"reserved-words": "^0.1.1"
9395
},
9496
"commitplease": {
9597
"nohook": true

src/common/validate/parameter-type/plural-type.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ return function( value, name ) {
66
validateParameterType(
77
value,
88
name,
9-
value === undefined || value === "cardinal" || value === "ordinal",
10-
"String \"cardinal\" or \"ordinal\""
9+
value === undefined || value === "cardinal" || value === "ordinal" || value === "both",
10+
"String \"cardinal\" or \"ordinal\" or \"both\""
1111
);
1212
};
1313

src/core.js

+6
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ Globalize.locale = function( locale ) {
8686
return this.cldr;
8787
};
8888

89+
Globalize._messageFmts = {};
90+
91+
Globalize.addMessageFormatterFunction = function( name, fn ) {
92+
Globalize._messageFmts[name] = fn;
93+
};
94+
8995
/**
9096
* Optimization to avoid duplicating some internal functions across modules.
9197
*/

src/currency.js

+8
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,14 @@ Globalize.prototype.currencyFormatter = function( currency, options ) {
9191
return returnFn;
9292
};
9393

94+
Globalize.addMessageFormatterFunction( "currency", function( currency, style ) {
95+
var options = {};
96+
if ( style ) {
97+
options.style = style;
98+
}
99+
return this.currencyFormatter( currency, options );
100+
});
101+
94102
/**
95103
* .currencyParser( currency [, options] )
96104
*

src/date.js

+10
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,16 @@ Globalize.prototype.dateToPartsFormatter = function( options ) {
138138
return returnFn;
139139
};
140140

141+
[ "date", "time", "datetime" ].map(function( type ) {
142+
Globalize.addMessageFormatterFunction( type, function( p ) {
143+
var options = {};
144+
if ( p ) {
145+
options[type] = p;
146+
}
147+
return this.dateFormatter( options );
148+
});
149+
});
150+
141151
/**
142152
* .dateParser( options )
143153
*

src/message-runtime.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ define([
22
"./common/runtime-key",
33
"./common/validate/parameter-type/message-variables",
44
"./core-runtime",
5-
"./message/formatter-fn"
6-
], function( runtimeKey, validateParameterTypeMessageVariables, Globalize, messageFormatterFn ) {
5+
"./message/formatter-fn",
6+
"./message/formatter-runtime"
7+
], function( runtimeKey, validateParameterTypeMessageVariables, Globalize, messageFormatterFn,
8+
messageFormatterRuntime
9+
) {
710

811
Globalize._messageFormatterFn = messageFormatterFn;
9-
Globalize._messageFormat = {};
12+
Globalize._messageFormat = new messageFormatterRuntime(); // TODO setStrictNumber
1013
Globalize._validateParameterTypeMessageVariables = validateParameterTypeMessageVariables;
1114

1215
Globalize.messageFormatter =

src/message.js

+47-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
define([
22
"cldr",
3-
"messageformat",
43
"./common/create-error",
54
"./common/create-error/plural-module-presence",
65
"./common/runtime-bind",
@@ -12,15 +11,18 @@ define([
1211
"./common/validate/parameter-type",
1312
"./common/validate/parameter-type/plain-object",
1413
"./core",
14+
"./message/compiler",
15+
"./message/formatter-runtime",
1516
"./message/formatter-fn",
1617
"./message/formatter-runtime-bind",
1718
"./util/always-array",
1819

1920
"cldr/event"
20-
], function( Cldr, MessageFormat, createError, createErrorPluralModulePresence, runtimeBind,
21+
], function( Cldr, createError, createErrorPluralModulePresence, runtimeBind,
2122
validateDefaultLocale, validateMessageBundle, validateMessagePresence, validateMessageType,
2223
validateParameterPresence, validateParameterType, validateParameterTypePlainObject, Globalize,
23-
messageFormatterFn, messageFormatterRuntimeBind, alwaysArray ) {
24+
messageCompiler, messageFormatterRuntime, messageFormatterFn, messageFormatterRuntimeBind,
25+
alwaysArray ) {
2426

2527
var slice = [].slice;
2628

@@ -82,17 +84,52 @@ Globalize.prototype.messageFormatter = function( path ) {
8284
}
8385
validateMessageType( path, message );
8486

85-
// Is plural module present? Yes, use its generator. Nope, use an error generator.
86-
pluralGenerator = this.plural !== undefined ?
87-
this.pluralGenerator() :
88-
createErrorPluralModulePresence;
87+
var compiler = new messageCompiler( this, Globalize._messageFmts );
88+
var formatterSrc = compiler
89+
.compile( message, cldr.locale );
90+
91+
var pluralType = false;
92+
if ( compiler.pluralTypes.cardinal && compiler.pluralTypes.ordinal ) {
93+
pluralType = "both";
94+
} else if ( compiler.pluralTypes.cardinal ) {
95+
pluralType = "cardinal";
96+
} else if ( compiler.pluralTypes.ordinal ) {
97+
pluralType = "ordinal";
98+
}
99+
100+
if ( pluralType !== false ) {
101+
102+
// Is plural module present? Yes, use its generator. Nope, use an error generator.
103+
pluralGenerator = this.plural !== undefined ?
104+
this.pluralGenerator( { type: pluralType } ) :
105+
createErrorPluralModulePresence;
106+
}
89107

90-
formatter = new MessageFormat( cldr.locale, pluralGenerator ).compile( message );
108+
var runtime = new messageFormatterRuntime( compiler.strictNumberSign );
109+
110+
/* jshint evil:true */
111+
formatter = new Function(
112+
"number, plural, select, fmt", messageCompiler.funcname( cldr.locale ),
113+
"return " + formatterSrc )(
114+
runtime.number, runtime.plural, runtime.select, compiler.formatters, pluralGenerator
115+
);
91116

92117
returnFn = messageFormatterFn( formatter );
93118

94-
runtimeBind( args, cldr, returnFn,
95-
[ messageFormatterRuntimeBind( cldr, formatter ), pluralGenerator ] );
119+
var runtimeArgs = [
120+
messageFormatterRuntimeBind(
121+
cldr, formatter, compiler.runtime, pluralType, cldr.locale, compiler.formatters
122+
)
123+
];
124+
125+
if ( pluralGenerator ) {
126+
runtimeArgs.push( pluralGenerator );
127+
}
128+
runtimeArgs = runtimeArgs.concat(
129+
compiler.formatters
130+
);
131+
132+
runtimeBind( args, cldr, returnFn, runtimeArgs );
96133

97134
return returnFn;
98135
};

0 commit comments

Comments
 (0)