Skip to content

Commit

Permalink
Use Intl.NumberFormat to format numbers
Browse files Browse the repository at this point in the history
  • Loading branch information
adamhooper authored and ljharb committed Oct 21, 2016
1 parent 2d06f0f commit 8570d35
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 6 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,25 @@ polyglot.t("car", 2);
=> "2 cars"
```

Interpolated `Number`s will be number-formatted according to the `locale`:

```js
polyglot.t("num_cars", 2000);
=> "2,000 cars"
```

On a default Node install, this may only work in English. To format in
non-English locales (e.g., to output "2.000" in France or use other numerals),
compile Node with "full" ICU data or include the `full-icu` package in your
project:

1. `npm install --save full-icu`
2. Run `node --full-data-dir=node_modules/full-icu` instead of just `node`, or
set the `NODE_ICU_DATA=node_modules/full-icu` environment variable.

If you're running Polyglot within a browser, it can number-format in any
locale the web browser supports.

If you like, you can provide a default value in case the phrase is missing.
Use the special option key "_" to specify a default.

Expand Down
28 changes: 24 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ var tokenRegex = /%\{(.*?)\}/g;
//
// You should pass in a third argument, the locale, to specify the correct plural type.
// It defaults to `'en'` with 2 plural forms.
function transformPhrase(phrase, substitutions, locale) {
function transformPhrase(phrase, substitutions, locale, numberFormat) {
if (typeof phrase !== 'string') {
throw new TypeError('Polyglot.transformPhrase expects argument #1 to be string');
}
Expand All @@ -144,8 +144,14 @@ function transformPhrase(phrase, substitutions, locale) {
// Interpolate: Creates a `RegExp` object for each interpolation placeholder.
result = result.replace(tokenRegex, function (expression, argument) {
if (!has(options, argument)) { return ''; }

var replacement = options[argument];
if (typeof replacement === 'number') {
replacement = numberFormat.format(replacement);
}

// Ensure replacement value is escaped to prevent special $-prefixed regex replace tokens.
return replace.call(options[argument], dollarRegex, dollarBillsYall);
return replace.call(replacement, dollarRegex, dollarBillsYall);
});

return result;
Expand All @@ -157,6 +163,12 @@ function Polyglot(options) {
this.phrases = {};
this.extend(opts.phrases || {});
this.currentLocale = opts.locale || 'en';
if (typeof Intl === 'object') {
this.numberFormat = new Intl.NumberFormat(this.currentLocale);
} else {
// Fallback for IE<11
this.numberFormat = { format: function (n) { return String(n); } };
}
this.allowMissing = !!opts.allowMissing;
this.warn = opts.warn || warn;
}
Expand All @@ -165,7 +177,15 @@ function Polyglot(options) {
//
// Get or set locale. Internally, Polyglot only uses locale for pluralization.
Polyglot.prototype.locale = function (newLocale) {
if (newLocale) this.currentLocale = newLocale;
if (newLocale) {
this.currentLocale = newLocale;
if (typeof Intl === 'object') {
this.numberFormat = new Intl.NumberFormat(this.currentLocale);
} else {
// Fallback for IE<11
this.numberFormat = { format: function (n) { return String(n); } };
}
}
return this.currentLocale;
};

Expand Down Expand Up @@ -314,7 +334,7 @@ Polyglot.prototype.t = function (key, options) {
result = key;
}
if (typeof phrase === 'string') {
result = transformPhrase(phrase, opts, this.currentLocale);
result = transformPhrase(phrase, opts, this.currentLocale, this.numberFormat);
}
return result;
};
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"scripts": {
"pretest": "npm run --silent lint",
"test": "npm run --silent tests-only",
"tests-only": "mocha test/*.js --reporter spec",
"tests-only": "NODE_ICU_DATA=node_modules/full-icu mocha test/*.js --reporter spec",
"lint": "eslint *.js test/*.js",
"docs": "docco -o docs/ index.js"
},
Expand Down Expand Up @@ -36,6 +36,7 @@
"eslint": "^3.9.1",
"eslint-config-airbnb-base": "^10.0.1",
"eslint-plugin-import": "^2.2.0",
"full-icu": "^1.0.3",
"mocha": "^3.1.2",
"should": "^11.1.1",
"uglify-js": "2.7.3"
Expand Down
17 changes: 16 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ describe('t', function () {
hello: 'Hello',
hi_name_welcome_to_place: 'Hi, %{name}, welcome to %{place}!',
name_your_name_is_name: '%{name}, your name is %{name}!',
empty_string: ''
empty_string: '',
number: '%{number}'
};

var polyglot;
Expand Down Expand Up @@ -89,6 +90,14 @@ describe('t', function () {
expect(instance.t('nav.cta.join_now')).to.equal('Join now!');
expect(instance.t('header.sign_in')).to.equal('Sign In');
});

it('uses an Intl.NumberFormat', function () {
var en = new Polyglot({ phrases: phrases, locale: 'en' });
var fr = new Polyglot({ phrases: phrases, locale: 'fr' });

expect(en.t('number', { number: 1234.56 })).to.equal('1,234.56');
expect(fr.t('number', { number: 1234.56 })).to.equal('1 234,56');
});
});

describe('pluralize', function () {
Expand Down Expand Up @@ -165,6 +174,12 @@ describe('locale', function () {
polyglot.locale('fr');
expect(polyglot.locale()).to.equal('fr');
});

it('updates number format when setting locale', function () {
polyglot.locale('fr');
polyglot.extend({ x: '%{n}' });
expect(polyglot.t('x', { n: 1234.56 })).to.equal('1 234,56');
});
});

describe('extend', function () {
Expand Down

0 comments on commit 8570d35

Please sign in to comment.