Skip to content

Commit c20bd16

Browse files
committed
Add "localizePrice" helper
1 parent 2244238 commit c20bd16

File tree

5 files changed

+105
-1
lines changed

5 files changed

+105
-1
lines changed

helpers/lib/icu-detect.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
'use strict';
2+
3+
const hasFullICU = (() => {
4+
try {
5+
const january = new Date(9e8);
6+
const spanish = new Intl.DateTimeFormat('es', { month: 'long' });
7+
return spanish.format(january) === 'enero';
8+
} catch (err) {
9+
return false;
10+
}
11+
})();
12+
13+
module.exports = {
14+
hasFullICU
15+
};

helpers/localizePrice.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
'use strict';
2+
const utils = require('handlebars-utils');
3+
// Detect ICU support
4+
const { hasFullICU } = require('./lib/icu-detect.js');
5+
6+
const factory = () => {
7+
return function(price, locale) {
8+
if (!utils.isObject(price) || !utils.isString(price.currency)
9+
|| isNaN(price.value) || !utils.isString(price.formatted)) {
10+
// Return empty string if this does not appear to be a price object
11+
return ''
12+
}
13+
14+
if (!utils.isString(locale) || locale.length < 2) {
15+
// Valid browser language strings are at least two characters
16+
// https://www.metamodpro.com/browser-language-codes
17+
// If provided locale is less than two characters (or not a string),
18+
// return the normal formatted price
19+
return price.formatted;
20+
}
21+
22+
// If the if full ICU is not installed, fall back to normal price
23+
if (!hasFullICU){
24+
return price.formatted;
25+
}
26+
27+
// Try to format the price to the provided locale,
28+
// but if anything goes wrong,
29+
// just return the usual price
30+
// Could happen if the full ICU is not installed,
31+
// or if an invalid locale is provided.
32+
try {
33+
return new Intl.NumberFormat(
34+
locale, { style: 'currency', currency: price.currency}
35+
).format(price.value);
36+
}
37+
catch (err){
38+
return price.formatted;
39+
}
40+
};
41+
};
42+
43+
module.exports = [{
44+
name: 'localizePrice',
45+
factory: factory,
46+
}];

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"license": "BSD-4-Clause",
88
"scripts": {
99
"linter": "eslint .",
10-
"test": "npm run linter && lab -v -t 98 --ignore WebAssembly,_time,format,SharedArrayBuffer,Atomics,BigUint64Array,BigInt64Array,BigInt,URL,URLSearchParams,TextEncoder,TextDecoder,queueMicrotask spec",
10+
"test": "cross-env NODE_ICU_DATA=node_modules/full-icu npm run linter && lab -v -t 98 --ignore WebAssembly,_time,format,SharedArrayBuffer,Atomics,BigUint64Array,BigInt64Array,BigInt,URL,URLSearchParams,TextEncoder,TextDecoder,queueMicrotask spec",
1111
"coverage": "lab -c -r console -o stdout -r html -o coverage.html spec"
1212
},
1313
"repository": {
@@ -34,7 +34,9 @@
3434
},
3535
"devDependencies": {
3636
"code": "~4.0.0",
37+
"cross-env": "^7.0.2",
3738
"eslint": "~4.10.0",
39+
"full-icu": "^1.3.1",
3840
"lab": "~13.0.4",
3941
"sinon": "~7.5.0"
4042
},

spec/helpers.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ describe('helper registration', () => {
4141
'lang',
4242
'langJson',
4343
'limit',
44+
'localizePrice',
4445
'money',
4546
'nl2br',
4647
'or',

spec/helpers/localizePrice.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
require('full-icu');
2+
3+
const Lab = require('lab'),
4+
lab = exports.lab = Lab.script(),
5+
describe = lab.experiment,
6+
it = lab.it,
7+
specHelpers = require('../spec-helpers'),
8+
testRunner = specHelpers.testRunner
9+
10+
describe('localizePrice helper', function() {
11+
const context = {
12+
"price": {
13+
"tax_label": "GST",
14+
"without_tax": {
15+
"currency": "USD",
16+
"formatted": "$123,456.78",
17+
"value": 123456.78
18+
}
19+
}
20+
};
21+
22+
const runTestCases = testRunner({context});
23+
24+
it('should return return correct prices across a number of locales', function(done) {
25+
runTestCases([
26+
{
27+
input: '{{localizePrice price.without_tax "en-US"}}',
28+
output: '$123,456.78',
29+
},
30+
{
31+
input: '{{localizePrice price.without_tax "ja-JP"}}',
32+
output: '$123,456.78',
33+
},
34+
{
35+
input: '{{localizePrice price.without_tax "de"}}',
36+
output: '123.456,78 $',
37+
},
38+
], done);
39+
});
40+
});

0 commit comments

Comments
 (0)