message-format
Advanced tools
Comparing version 0.0.3 to 0.0.4
{ | ||
"name": "message-format", | ||
"version": "0.0.3", | ||
"version": "0.0.4", | ||
"description": "Intl.MessageFormat polyfill supporting ICU message format", | ||
@@ -27,3 +27,4 @@ "main": "dist/message-format.js", | ||
"benchmark": "6to5-node --loose=all scripts/benchmark.js", | ||
"prepublish": "6to5 --loose all src --out-dir dist", | ||
"cldr": "scripts/cldr.sh", | ||
"prepublish": "6to5 --loose=all src --out-dir dist", | ||
"test": "jshint src/*.js && karma start --browsers Firefox --single-run" | ||
@@ -30,0 +31,0 @@ }, |
@@ -9,4 +9,2 @@ # message-format | ||
This is still a work in progress. Only the default English locale is available. | ||
Quick Start | ||
@@ -46,2 +44,9 @@ ----------- | ||
### Loading locale data | ||
message-format supports plural rules for all CLDR languages. Locale-aware | ||
formatting of number, date, and time are delegated to the `Intl` objects, | ||
and select is the same across all locales. You don't need to load any extra | ||
files for particular locales for message-format. | ||
### Unsupported ICU Formats | ||
@@ -91,4 +96,4 @@ | ||
- `options` is an optional object containing options that change the behavior of `MessageFormat`. | ||
-- `cache` default `true`, if `true` cache the result of preparing the `format` function. The cache is shared across all instances of `MessageFormat`. | ||
-- `escape` default `"'"`, if any other character, escape the single character following each escape character. | ||
- `cache` default `true`, if `true` cache the result of preparing the `format` function. The cache is shared across all instances of `MessageFormat`. | ||
- `escape` default `'`, if any other character, escape the single character following each escape character. | ||
@@ -114,4 +119,4 @@ ### `MessageFormat` instances | ||
var message = new MessageFormat('Welcome back, {name}!'); | ||
message.format({ name:'Bob' }); // 'Welcome back, Bob!' | ||
message.format({ name:'Bill' }); // 'Welcome back, Bill!' | ||
message.format({ name:'Bob' }); // "Welcome back, Bob!" | ||
message.format({ name:'Bill' }); // "Welcome back, Bill!" | ||
``` | ||
@@ -123,6 +128,10 @@ | ||
1 `''` is always `'` | ||
2 `'` begins an escaped string only if followed immediately by a syntax char (`'{}#'`) | ||
3 `'` ends an escaped string, unless it is doubled. See #1 | ||
1. `''` is always `'` | ||
2. `'` begins an escaped string only if followed immediately by a syntax char (`{}#`) | ||
3. `'` ends an escaped string, unless it is doubled. See #1 | ||
The recommendation from ICU is to use the ASCII apostrophe (`'` U+0027) only | ||
for escaping syntax characters, and use the pretty single quote (`’` U+2019) | ||
for actual apostrophes and single quotes in a message pattern. | ||
```js | ||
@@ -141,9 +150,9 @@ var message = new MessageFormat('This isn\'\'t a \'{simple}\' \'string\''); | ||
var message = new MessageFormat('You took {n,number} pictures since {d,date} {d,time}'); | ||
message.format({ n:4000, d:new Date() }); // 'You took 4,000 pictures since Jan 1, 2015 9:33:04 AM' | ||
message.format({ n:4000, d:new Date() }); // "You took 4,000 pictures since Jan 1, 2015 9:33:04 AM" | ||
message = new MessageFormat('{ n, number, percent }'); | ||
message.format({ n:0.1 }); // '10%' | ||
message.format({ n:0.1 }); // "10%" | ||
message = new MessageFormat('{ shorty, date, short }'); | ||
message.format({ shorty:new Date() }); // '1/1/15' | ||
message.format({ shorty:new Date() }); // "1/1/15" | ||
``` | ||
@@ -174,3 +183,3 @@ | ||
numBananas: 27 | ||
}) // 'On 1/1/15 Curious George ate 27 bananas at his house.' | ||
}) // "On 1/1/15 Curious George ate 27 bananas at his house." | ||
``` | ||
@@ -177,0 +186,0 @@ |
import IntlPolyfill from 'intl' | ||
import IntlMF from 'intl-messageformat' | ||
import MessageFormat from './src/message-format' | ||
import Parser from './src/parser' | ||
import MessageFormat from '../src/message-format' | ||
import Parser from '../src/parser' | ||
import Benchmark from 'benchmark' | ||
@@ -6,0 +6,0 @@ |
@@ -17,6 +17,8 @@ /** | ||
constructor(locale, data, elements) { | ||
constructor(locale, data, formats, elements, options) { | ||
this.originalLocale = locale | ||
this.localeData = data | ||
this.formats = formats | ||
this.elements = elements | ||
this.enableCache = ('cache' in options) ? options.cache : true | ||
} | ||
@@ -26,4 +28,4 @@ | ||
compile(elements, parent) { | ||
elements = elements || this.elements | ||
elements = elements.map(element => this.compileElement(element, parent)) | ||
elements = elements || this.elements | ||
elements = elements.map(element => this.compileElement(element, parent)) | ||
@@ -35,9 +37,9 @@ // optimize common case | ||
return function format(args) { | ||
let message = '' | ||
for (let e = 0, ee = elements.length; e < ee; ++e) { | ||
message += elements[e](args) | ||
} | ||
return message | ||
} | ||
return function format(args) { | ||
let message = '' | ||
for (let e = 0, ee = elements.length; e < ee; ++e) { | ||
message += elements[e](args) | ||
} | ||
return message | ||
} | ||
} | ||
@@ -52,10 +54,10 @@ | ||
let | ||
id = element[0], | ||
type = element[1], | ||
style = element[2] | ||
id = element[0], | ||
type = element[1], | ||
style = element[2] | ||
if ('#' === id) { | ||
let | ||
id = parent[0], | ||
offset = parent[2] | ||
id = parent[0], | ||
offset = parent[2] | ||
return this.compileNumber(id, offset, null) | ||
@@ -72,4 +74,4 @@ } | ||
let | ||
offset = element[2], | ||
options = element[3] | ||
offset = element[2], | ||
options = element[3] | ||
return this.compilePlural(id, offset, options) | ||
@@ -88,13 +90,18 @@ case 'select': | ||
let | ||
key = 'number:' + style, | ||
locale = this.originalLocale, | ||
data = this.localeData, | ||
cache = data.cache, | ||
formats = this.formats, | ||
cache = formats.cache, | ||
key = locale + ':number:' + style, | ||
func | ||
if (this.enableCache && key in cache) { | ||
func = cache[key] | ||
if (!func) { | ||
func = cache[key] = new Intl.NumberFormat(locale, data.formats.number[style]).format | ||
} else { | ||
func = cache[key] = new Intl.NumberFormat(locale, formats.number[style]).format | ||
if (this.enableCache) { | ||
cache[key] = func | ||
} | ||
} | ||
return function format(args) { | ||
return func(+args[id] - offset) | ||
} | ||
return func(+args[id] - offset) | ||
} | ||
} | ||
@@ -106,13 +113,18 @@ | ||
let | ||
key = type + ':' + style, | ||
locale = this.originalLocale, | ||
data = this.localeData, | ||
cache = data.cache, | ||
formats = this.formats, | ||
cache = formats.cache, | ||
key = locale + ':' + type + ':' + style, | ||
func | ||
if (this.enableCache && key in cache) { | ||
func = cache[key] | ||
if (!func) { | ||
func = cache[key] = new Intl.DateTimeFormat(locale, data.formats[type][style]).format | ||
} else { | ||
func = cache[key] = new Intl.DateTimeFormat(locale, formats[type][style]).format | ||
if (this.enableCache) { | ||
cache[key] = func | ||
} | ||
} | ||
return function format(args) { | ||
return func(args[id]) | ||
} | ||
return function format(args) { | ||
return func(args[id]) | ||
} | ||
} | ||
@@ -129,13 +141,13 @@ | ||
}) | ||
return function format(args) { | ||
let | ||
arg = +args[id], | ||
exactSelector = '=' + arg, | ||
keywordSelector = plural(arg - offset), | ||
func = | ||
options[exactSelector] || | ||
options[keywordSelector] || | ||
options.other | ||
return func(args) | ||
} | ||
return function format(args) { | ||
let | ||
arg = +args[id], | ||
exactSelector = '=' + arg, | ||
keywordSelector = plural(arg - offset), | ||
func = | ||
options[exactSelector] || | ||
options[keywordSelector] || | ||
options.other | ||
return func(args) | ||
} | ||
} | ||
@@ -149,10 +161,10 @@ | ||
}) | ||
return function format(args) { | ||
let | ||
selector = args[id], | ||
func = | ||
options[selector] || | ||
options.other | ||
return func(args) | ||
} | ||
return function format(args) { | ||
let | ||
selector = args[id], | ||
func = | ||
options[selector] || | ||
options.other | ||
return func(args) | ||
} | ||
} | ||
@@ -163,9 +175,9 @@ | ||
return function format(args) { | ||
return '' + args[id] | ||
} | ||
return '' + args[id] | ||
} | ||
} | ||
static compile(locale, data, elements) { | ||
return new Compiler(locale, data, elements).compile() | ||
static compile(locale, data, formats, elements, options) { | ||
return new Compiler(locale, data, formats, elements, options).compile() | ||
} | ||
@@ -172,0 +184,0 @@ |
@@ -7,2 +7,3 @@ /*! Intl.MessageFormat polyfill v0.0.1 | ||
import Compiler from './compiler' | ||
import locales from './locales' | ||
@@ -21,6 +22,6 @@ /** | ||
let | ||
closest = lookupClosestLocale(locale, MessageFormat.data), | ||
data = MessageFormat.data[closest], | ||
closest = lookupClosestLocale(locale, MessageFormat.data.locales), | ||
data = MessageFormat.data.locales[closest], | ||
enableCache = ('cache' in options) ? options.cache : true, | ||
cache = data.cache, | ||
cache = MessageFormat.data.formats.cache, | ||
key = 'message:' + pattern, | ||
@@ -35,3 +36,5 @@ format | ||
data, | ||
Parser.parse(pattern, options) | ||
MessageFormat.data.formats, | ||
Parser.parse(pattern, options), | ||
options | ||
) | ||
@@ -63,3 +66,3 @@ if (enableCache) { | ||
let | ||
locales = [].concat(locale || []) | ||
locales = [].concat(locale || []) | ||
for (let l = 0, ll = locales.length; l < ll; ++l) { | ||
@@ -82,28 +85,36 @@ let current = locales[l].split('-') | ||
let data = { | ||
en: { | ||
locale: 'en', | ||
locales: { | ||
en: { | ||
locale: 'en', | ||
plural(n) { | ||
return n === 1 ? 'one' : 'other' | ||
} | ||
} | ||
}, | ||
formats: { | ||
cache: {}, | ||
plural(n) { | ||
return n === 1 ? 'one' : 'other' | ||
number: { | ||
currency: { style:'currency', currency:'USD' }, | ||
percent: { style:'percent' } | ||
}, | ||
formats: { | ||
number: { | ||
currency: { style:'currency', currency:'USD' }, | ||
percent: { style:'percent' } | ||
}, | ||
date: { | ||
short: { month:'numeric', day:'numeric', year:'2-digit' }, | ||
medium: { month:'short', day:'numeric', year:'numeric' }, | ||
long: { month:'long', day:'numeric', year:'numeric' }, | ||
full: { month:'long', day:'numeric', year:'numeric', weekday:'long' } | ||
}, | ||
time: { | ||
short: { hour:'numeric', minute:'numeric' }, | ||
medium: { hour:'numeric', minute:'numeric', second:'numeric' }, | ||
long: { hour:'numeric', minute:'numeric', second:'numeric', timeZoneName:'short' }, | ||
full: { hour:'numeric', minute:'numeric', second:'numeric', timeZoneName:'short' } | ||
} | ||
date: { | ||
short: { month:'numeric', day:'numeric', year:'2-digit' }, | ||
medium: { month:'short', day:'numeric', year:'numeric' }, | ||
long: { month:'long', day:'numeric', year:'numeric' }, | ||
full: { month:'long', day:'numeric', year:'numeric', weekday:'long' } | ||
}, | ||
time: { | ||
short: { hour:'numeric', minute:'numeric' }, | ||
medium: { hour:'numeric', minute:'numeric', second:'numeric' }, | ||
long: { hour:'numeric', minute:'numeric', second:'numeric', timeZoneName:'short' }, | ||
full: { hour:'numeric', minute:'numeric', second:'numeric', timeZoneName:'short' } | ||
} | ||
} | ||
} | ||
Object.keys(locales).forEach(function(locale) { | ||
let plural = locales[locale] | ||
data.locales[locale] = { | ||
locale, plural | ||
} | ||
}) | ||
Object.defineProperty(MessageFormat, 'data', { value:data }) | ||
@@ -110,0 +121,0 @@ |
@@ -58,4 +58,4 @@ import MessageFormat from '../src/message-format' | ||
`On {takenDate, date, short} {name} took {numPhotos, plural, | ||
=0 {no photos.} | ||
=1 {one photo.} | ||
=0 {no photos.} | ||
=1 {one photo.} | ||
other {# photos.}}`, | ||
@@ -69,2 +69,23 @@ message = new MessageFormat(pattern, 'en-US') | ||
it('handles plurals for other locales', () => { | ||
let | ||
pattern = | ||
`{n, plural, | ||
zero {zero} | ||
one {one} | ||
two {two} | ||
few {few} | ||
many {many} | ||
other {other}}`, | ||
message = new MessageFormat(pattern, 'ar') | ||
expect(message.resolvedOptions().locale).toBe('ar') | ||
expect(message.format({ n:0 })).toBe('zero') | ||
expect(message.format({ n:1 })).toBe('one') | ||
expect(message.format({ n:2 })).toBe('two') | ||
expect(message.format({ n:3 })).toBe('few') | ||
expect(message.format({ n:11 })).toBe('many') | ||
}) | ||
it('handles select', () => { | ||
@@ -74,5 +95,5 @@ let | ||
`{ gender, select, | ||
male {it's his turn} | ||
female {it's her turn} | ||
other {it's their turn}}`, | ||
male {it's his turn} | ||
female {it's her turn} | ||
other {it's their turn}}`, | ||
message = new MessageFormat(pattern, 'en-US') | ||
@@ -79,0 +100,0 @@ .format({ gender:'female' }) |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
47169
16
1432
200
1