Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 5d5fd62

Browse files
committed
chore(i18n): fix parser for currency patterns without fraction digits
Previously, it was assumed that all currency pattern would have fraction digits. However, in [closure-library@b9155d5][1] the `agq_CM` locale was modified to have such a pattern (namely `#,##0\u00A4`). This commit modifies the parser implementation to account for pattern without a decimal point (and thus no fraction digits). [1]: google/closure-library@b9155d5#diff-02793124214ad0470ccea6f86b90d786R711
1 parent 7fbbacc commit 5d5fd62

File tree

2 files changed

+77
-7
lines changed

2 files changed

+77
-7
lines changed

i18n/spec/parserSpec.js

+49-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,43 @@
11
'use strict';
22

3-
var parsePattern = require('../src/parser.js').parsePattern;
3+
var parser = require('../src/parser');
4+
var ensureDecimalSep = parser.ensureDecimalSep;
5+
var parsePattern = parser.parsePattern;
6+
7+
describe('ensureDecimalSep', function() {
8+
it('should leave patterns with DECIMAL_SEP untouched', function() {
9+
[
10+
'#,##0.00',
11+
'$#,##0.00',
12+
'#,##0.00$',
13+
'$0.00',
14+
'0.00$',
15+
'0.0',
16+
'#,##0.',
17+
'0.'
18+
].forEach(function(pattern) {
19+
expect(ensureDecimalSep(pattern)).toBe(pattern);
20+
});
21+
});
22+
23+
it('should add a DECIMAL_SEP in patterns that don\'t have one (after the last ZERO)', function() {
24+
var patterns = {
25+
'#,##000': '#,##000.',
26+
'$#,#0#00': '$#,#0#00.',
27+
'#,##000$': '#,##000.$',
28+
'$000': '$000.',
29+
'000$': '000.$',
30+
'00': '00.',
31+
'#,##0': '#,##0.',
32+
'0': '0.'
33+
};
34+
35+
Object.keys(patterns).forEach(function(input) {
36+
var output = patterns[input];
37+
expect(ensureDecimalSep(input)).toBe(output);
38+
});
39+
});
40+
});
441

542
describe('parsePattern', function() {
643
function parseAndExpect(pattern, pp, np, ps, ns, mii, mif, maf, g, lg) {
@@ -28,6 +65,11 @@ describe('parsePattern', function() {
2865
'', '\u202A-', '', '\u202C', 1, 0, 3, 3, 3);
2966
parseAndExpect('#0.###;#0.###-', '', '', '', '-', 1, 0, 3, 0, 0);
3067

68+
// Even patterns without a DECIMAL_SEP
69+
parseAndExpect('#,##0', '', '-', '', '', 1, 0, 0, 3, 3);
70+
parseAndExpect('+#,##0', '+', '-+', '', '', 1, 0, 0, 3, 3);
71+
parseAndExpect('#,#0;+#,#0', '', '+', '', '', 1, 0, 0, 2, 2);
72+
parseAndExpect('#,##,##0+;(#,##,##0)', '', '(', '+', ')', 1, 0, 0, 2, 3);
3173
});
3274

3375
it('should parse CURRENCY patterns', function() {
@@ -51,5 +93,11 @@ describe('parsePattern', function() {
5193
parseAndExpect('\u00A4 #,##0.00;\u00A4 #,##0.00-',
5294
'\u00A4 ', '\u00A4 ', '', '-', 1, 2, 2, 3, 3);
5395
parseAndExpect('\u00A4 #,##,##0.00', '\u00A4 ', '-\u00A4 ', '', '', 1, 2, 2, 2, 3);
96+
97+
// Even patterns without a DECIMAL_SEP
98+
parseAndExpect('#,##0 \u00A4', '', '-', ' \u00A4', ' \u00A4', 1, 0, 0, 3, 3);
99+
parseAndExpect('\u00A4 #,##0', '\u00A4 ', '-\u00A4 ', '', '', 1, 0, 0, 3, 3);
100+
parseAndExpect('#,#0 \u00A4;+#,#0\u00A4', '', '+', ' \u00A4', '\u00A4', 1, 0, 0, 2, 2);
101+
parseAndExpect('\u00A4 #,##,##0;(\u00A4 #,##,##0)', '\u00A4 ', '(\u00A4 ', '', ')', 1, 0, 0, 2, 3);
54102
});
55103
});

i18n/src/parser.js

+28-6
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,26 @@
44
* A simple parser to parse a number format into a pattern object
55
*/
66

7+
exports.ensureDecimalSep = ensureDecimalSep;
78
exports.parsePattern = parsePattern;
89

9-
var PATTERN_SEP = ';',
10-
DECIMAL_SEP = '.',
11-
GROUP_SEP = ',',
12-
ZERO = '0',
13-
DIGIT = '#';
10+
var PATTERN_SEP = ';',
11+
DECIMAL_SEP = '.',
12+
GROUP_SEP = ',',
13+
DIGIT = '#',
14+
ZERO = '0',
15+
LAST_ZERO_RE = /^(.*0)(?!0)(.*)$/;
16+
17+
/**
18+
* Helper function for parser.
19+
* Ensures that `pattern` (e.g #,##0.###) contains a DECIMAL_SEP, which is necessary for further
20+
* parsing. If a pattern does not include one, it is added after the last ZERO (which is the last
21+
* thing before the `posSuf` - if any).
22+
*/
23+
function ensureDecimalSep(pattern) {
24+
return (pattern.indexOf(DECIMAL_SEP) !== -1)
25+
? pattern : pattern.replace(LAST_ZERO_RE, '$1' + DECIMAL_SEP + '$2');
26+
}
1427

1528
/**
1629
* main function for parser
@@ -33,7 +46,16 @@ function parsePattern(pattern) {
3346
positive = patternParts[0],
3447
negative = patternParts[1];
3548

36-
var positiveParts = positive.split(DECIMAL_SEP),
49+
// The parsing logic further below assumes that there will always be a DECIMAL_SEP in the pattern.
50+
// However, some locales (e.g. agq_CM) do not have one, thus we add one after the last ZERO
51+
// (which is the last thing before the `posSuf` - if any). Since there will be no ZEROs or DIGITs
52+
// after DECIMAL_SEP, `min/maxFrac` will remain 0 (which is accurate - no fraction digits) and
53+
// `posSuf` will be processed correctly.
54+
// For example `#,##0$` would be converted to `#,##0.$`, which would (correctly) result in:
55+
// `minFrac: 0`, `maxFrac: 0`, `posSuf: '$'`
56+
// Note: We shouldn't modify `positive` directly, because it is used to parse the negative part.)
57+
var positiveWithDecimalSep = ensureDecimalSep(positive),
58+
positiveParts = positiveWithDecimalSep.split(DECIMAL_SEP),
3759
integer = positiveParts[0],
3860
fraction = positiveParts[1];
3961

0 commit comments

Comments
 (0)