Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unit formatting module #512

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,23 @@ module.exports = function( grunt ) {
}
}
},
{
name: "globalize.unit",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GitHub has some kind of issue with this file. Looking at the source locally, I also get messed up syntax highlighting in my editor (SublimeText). Looks like an issue with the regex on line 192, completely unrelated to this PR though. Somehow this helps:

diff --git a/Gruntfile.js b/Gruntfile.js
index ce458fc..60819b1 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -189,7 +189,7 @@ module.exports = function( grunt ) {
                             // Remove browserify wrappers.
                             .replace( /^\(function\(f\){if\(typeof exports==="object"&&type.*/, "" )
                             .replace( "},{}],2:[function(require,module,exports){", "" )
-                            .replace( /},{"\.\/messageformat-parser":1,"make-plural\/plural.*/, "" )
+                            .replace( /},{"\.\/messageformat-parser\":1,"make-plural\/plural.*/, "" )
                             .replace( /},{}\]},{},\[2\]\)\(2\)[\s\S]*?$/, "" )

                             // Set `MessageFormat.plurals` and remove `make-plural/plurals`

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jzaefferer can you file a separate ticket for this please?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the record, #527 (fixed by #529)

include: [ "unit" ],
exclude: [
"cldr",
"./core",
"./number",
"./plural"
],
create: true,
override: {
wrap: {
startFile: "src/build/intro-unit.js",
endFile: "src/build/outro.js"
}
}
},
{
name: "globalize-runtime",
include: [ "core-runtime" ],
Expand Down Expand Up @@ -473,6 +490,22 @@ module.exports = function( grunt ) {
endFile: "src/build/outro.js"
}
}
},
{
name: "globalize.unit-runtime",
include: [ "unit-runtime" ],
exclude: [
"./core-runtime",
"./number-runtime",
"./plural-runtime"
],
create: true,
override: {
wrap: {
startFile: "src/build/intro-unit-runtime.js",
endFile: "src/build/outro.js"
}
}
}
]
}
Expand Down Expand Up @@ -535,6 +568,7 @@ module.exports = function( grunt ) {
"tmp/globalize/plural.min.js": [ "dist/globalize/plural.js" ],
"tmp/globalize/message.min.js": [ "dist/globalize/message.js" ],
"tmp/globalize/relative-time.min.js": [ "dist/globalize/relative-time.js" ],
"tmp/globalize/unit.min.js": [ "dist/globalize/unit.js" ],

"tmp/globalize-runtime.min.js": [ "dist/globalize-runtime.js" ],
"tmp/globalize-runtime/currency.min.js": [
Expand All @@ -546,7 +580,8 @@ module.exports = function( grunt ) {
"tmp/globalize-runtime/plural.min.js": [ "dist/globalize-runtime/plural.js" ],
"tmp/globalize-runtime/relative-time.min.js": [
"dist/globalize-runtime/relative-time.js"
]
],
"tmp/globalize-runtime/unit.min.js": [ "dist/globalize-runtime/unit.js" ]
}
}
},
Expand Down
31 changes: 29 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Node.js module.
- [Currency module](#currency-module)
- [Plural module](#plural-module)
- [Relative time module](#relative-time-module)
- [Unit module](#unit-module)
- more to come...
- [Error reference](#error-reference)
- [Development](#development)
Expand Down Expand Up @@ -148,6 +149,7 @@ information on its usage.
| globalize/number.js | 3.1KB | 1.8KB | [Number module](#number-module) provides number formatting and parsing |
| globalize/plural.js | 2.3KB | 0.4KB | [Plural module](#plural-module) provides pluralization support |
| globalize/relative-time.js | 0.8KB | 0.6KB | [Relative time module](#relative-time-module) provides relative time formatting support |
| globalize/unit.js | 0.9KB | 0.5KB | [Unit module](#unit-module) provides unit formatting support |

### Browser Support

Expand Down Expand Up @@ -213,8 +215,9 @@ requirements. See table below.
| Number module | cldr/main/`locale`/numbers.json<br>cldr/supplemental/numberingSystems.json |
| Plural module | cldr/supplemental/plurals.json (for cardinals)<br>cldr/supplemental/ordinals.json (for ordinals) |
| Relative time module | cldr/main/`locale`/dateFields.json<br>+CLDR JSON files from number and plural modules |
| Unit module | cldr/main/`locale`/units.json<br>+CLDR JSON files from number and plural module |

As an alternative to deducing this yourself, use this [online tool](http://johnnyreilly.github.io/globalize-so-what-cha-want/). The tool allows you to select the modules you're interested in using and tells you the Globalize files *and* CLDR JSON that you need.
As an alternative to deducing this yourself, use this [online tool](http://johnnyreilly.github.io/globalize-so-what-cha-want/). The tool allows you to select the modules you're interested in using and tells you the Globalize files *and* CLDR JSON that you need.

*(b) How am I supposed to get and load CLDR content?*

Expand Down Expand Up @@ -571,6 +574,29 @@ handle dependencies and CLDR loading manually yourself.

Alias for `.relativeTimeFormatter( unit, options )( value )`.

## Unit module

- **`.unitFormatter( unit, options )`**

Returns a function that formats a unit according to the given unit, options, and the
default/instance locale.

```javascript
.unitFormatter( "second", { form: "long" } )( 10 )
// > "10 seconds"

.unitFormatter( "second", { form: "short" } )( 10 )
// > "10 secs"

.unitFormatter( "second", { form: "narrow" } )( 10 )
// > "10s"
```

[Read more...](doc/api/unit/unit-formatter.md)

- **`.formatUnit( value, unit, options )`**

Alias for `.unitFormatter( unit, options )( value )`.

## Error reference

Expand Down Expand Up @@ -663,6 +689,8 @@ handle dependencies and CLDR loading manually yourself.
│ ├── plural/ (plural source code)
│ ├── relative-time.js (relative time module)
│ ├── relative-time/ (relative time source code)
│ ├── unit.js (unit module)
│ ├── unit/ (unit source code)
│ └── util/ (basic JavaScript helpers polyfills, eg array.map)
└── test/ (unit and functional test files)
├── fixtures/ (CLDR fixture data)
Expand Down Expand Up @@ -727,4 +755,3 @@ dependencies (for more details, see above).
```bash
grunt
```

77 changes: 77 additions & 0 deletions doc/api/unit/unit-formatter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
## .unitFormatter( unit, options ) ➜ function( value )

Returns a function that formats a unit according to the given unit, options, and the
default/instance locale.

The returned function is invoked with one argument: the number `value` to
be formatted.

### Parameters

**unit**

String value indicating the unit to be formatted. eg. "day", "week", "month", etc.
Could also be a compound unit, eg. "mile-per-hour" or "mile/hour"

**options**

- form: [String] eg. "long", "short" or "narrow".
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no options for formatting the number itself?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shoot. Good point.

I would add a numberFormatter option that takes a formatter as option.


- numberFormatter: [Object] `Globalize.numberFormatter` instance. Defaults to the
default numberFormatter for the current locale.

**value**

The number to be formatted.

### Example

Prior to using any unit methods, you must load `cldr/main/{locale}/units.json` and the
CLDR content required by the plural module. Read [CLDR content][] if you need
more information.

[CLDR content]: ../../../README.md#2-cldr-content

You can use the static method `Globalize.unitFormatter()`, which uses the default
locale.

```javascript
var customNumberFormatter, formatter;

Globalize.locale( "en" );
formatter = Globalize.unitFormatter( "month", { form: "long" } );

formatter( 1 );
// > "1 month"

formatter( 3 );
// > "3 months"

formatter( 3000 );
// > "3,000 months"
```

You can pass a custom number formatter to format the number of units.

```javascript
var customNumberFormatter, formatter;

Globalize.locale( "en" );
customNumberFormatter = Globalize.numberFormatter({ useGrouping = false })
formatter = Globalize.unitFormatter( "mile-per-hour", {
form: "narrow", numberFormatter: customNumberFormatter
} );

formatter(5000)
// > "5000mph"
```

You can use the instance method `.unitFormatter()`, which uses the instance locale.

```javascript
var globalize = new Globalize( "en" ),
formatter = globalize.unitFormatter( "mile-per-hour", { form: "narrow" } );

formatter( 10 );
// > "10mph"
```
40 changes: 40 additions & 0 deletions src/build/intro-unit-runtime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Globalize Runtime v@VERSION
*
* http://github.com/jquery/globalize
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license
* http://jquery.org/license
*
* Date: @DATE
*/
/*!
* Globalize Runtime v@VERSION @DATE Released under the MIT license
* http://git.io/TrdQbw
*/
(function( root, factory ) {

// UMD returnExports
if ( typeof define === "function" && define.amd ) {

// AMD
define([
"../globalize-runtime",
"./plural"
], factory );
} else if ( typeof exports === "object" ) {

// Node, CommonJS
module.exports = factory(
require( "../globalize-runtime" ),
require( "./plural" )
);
} else {

// Extend global
factory( root.Globalize );
}
}(this, function( Globalize ) {

var runtimeKey = Globalize._runtimeKey;
44 changes: 44 additions & 0 deletions src/build/intro-unit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Globalize v@VERSION
*
* http://github.com/jquery/globalize
*
* Copyright 2010, 2014 jQuery Foundation, Inc. and other contributors
* Released under the MIT license
* http://jquery.org/license
*
* Date: @DATE
*/
/*!
* Globalize v@VERSION @DATE Released under the MIT license
* http://git.io/TrdQbw
*/
(function( root, factory ) {

// UMD returnExports
if ( typeof define === "function" && define.amd ) {

// AMD
define([
"cldr",
"../globalize",
"./number",
"./plural"
], factory );
} else if ( typeof exports === "object" ) {

// Node, CommonJS
module.exports = factory( require( "cldrjs" ), require( "globalize" ) );
} else {

// Extend global
factory( root.Cldr, root.Globalize );
}
}(this, function( Cldr, Globalize ) {

var formatMessage = Globalize._formatMessage,
runtimeBind = Globalize._runtimeBind,
validateParameterPresence = Globalize._validateParameterPresence,
validateParameterTypePlainObject = Globalize._validateParameterTypePlainObject,
validateParameterTypeNumber = Globalize._validateParameterTypeNumber,
validateParameterTypeString = Globalize._validateParameterTypeString;
24 changes: 24 additions & 0 deletions src/unit-runtime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
define([
"./common/runtime-key",
"./core-runtime",
"./unit/formatter-fn",

"./number-runtime",
"./plural-runtime"
], function( runtimeKey, Globalize, unitFormatterFn ) {

Globalize._unitFormatterFn = unitFormatterFn;

Globalize.formatUnit =
Globalize.prototype.formatUnit = function( value, unit, options ) {
return this.unitFormatter( unit, options )( value );
};

Globalize.unitFormatter =
Globalize.prototype.unitFormatter = function( unit, options ) {
return Globalize[ runtimeKey( "unitFormatter", this._locale, [ unit, options ] ) ];
};

return Globalize;

});
73 changes: 73 additions & 0 deletions src/unit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
define([
"./core",
"./common/runtime-bind",
"./common/validate/parameter-presence",
"./common/validate/parameter-type/number",
"./common/validate/parameter-type/plain-object",
"./common/validate/parameter-type/string",
"./unit/formatter-fn",
"./unit/properties",

"./number",
"./plural"
], function( Globalize, runtimeBind, validateParameterPresence, validateParameterTypeNumber,
validateParameterTypePlainObject, validateParameterTypeString, unitFormatterFn,
unitProperties ) {

/**
* Globalize.formatUnit( value, unit, options )
*
* @value [Number]
*
* @unit [String]: The unit (e.g "second", "day", "year")
*
* @options [Object]
* - form: [String] "long", "short" (default), or "narrow".
*
* Format units such as seconds, minutes, days, weeks, etc.
*/
Globalize.formatUnit =
Globalize.prototype.formatUnit = function( value, unit, options ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's update this according to the below please. So, it's accessible via both static function (Globalize.formatUnit) and instance method (new Globalize(locale).formatUnit)

Globalize.formatUnit =
Globalize.prototype.formatUnit = function( ...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed on my local branch (commenting for my own tracking benefit)

validateParameterPresence( value, "value" );
validateParameterTypeNumber( value, "value" );

return this.unitFormatter( unit, options )( value );
};

/**
* Globalize.unitFormatter( unit, options )
*
* @unit [String]: The unit (e.g "second", "day", "year")
*
* @options [Object]
* - form: [String] "long", "short" (default), or "narrow".
*
* - numberFormatter: [Object] Globalize.numberFormatter object. The default value is the
* default numberFormatter for the current locale.
*/
Globalize.unitFormatter =
Globalize.prototype.unitFormatter = function( unit, options ) {
var args, form, numberFormatter, pluralGenerator, returnFn, properties;

validateParameterPresence( unit, "unit" );
validateParameterTypeString( unit, "unit" );

validateParameterPresence( options, "options" );
validateParameterTypePlainObject( options, "options" );

args = [ unit, options ];
form = options.form || "long";
properties = unitProperties( unit, form, this.cldr );

numberFormatter = options.numberFormatter || this.numberFormatter();
pluralGenerator = this.pluralGenerator();
returnFn = unitFormatterFn( properties, numberFormatter, pluralGenerator );

runtimeBind( args, this.cldr, returnFn, [ numberFormatter, pluralGenerator, properties ] );

return returnFn;
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, .formatUnit() should be an alias for .unitFormatter(). Like .formatNumber() https://github.com/jquery/globalize/blob/master/src/number.js#L148-L154 is for .numberFormatter() https://github.com/jquery/globalize/blob/master/src/number.js#L107-L137

Note the process of setting up the formatter in general is somewhat an expensive operation. This process includes traversing CLDR data and cherry-picking the specific data (which I name properties) for the particular options used by the formatter. Therefore, the formatter generator should return a function that in turn allows to execute the formatting (i.e., allow user to setup once and execute several times for optimal performance; and to allow runtime compiled formatters). Please, just let me know on any questions.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little confused by this - is it right to say unitFormatter should be the only function that touches the cldr data, and that src/unit/format.js should operate on the already extracted content?

Here's an example of one approach I was looking at for this, let me know if it looks sound.
https://gist.github.com/alunny/c9777c4c171b986fad9f

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your gist diff seems perfect!

The point of keeping src/unit/format.js CLDR-free (and as light as possible) is that it goes in the runtime module. The other functions that setup and prepare/create the properties (e.g., src/unit/get.js) can be safely ignored in the runtime modules.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good - had to update the tests too, but now everything's passing.


return Globalize;

});
Loading