Skip to content

Commit 786caf6

Browse files
authored
fix(localization): correctly format number with escaped semicolon (T1275922) (DevExpress#29020)
1 parent 13f52e0 commit 786caf6

File tree

4 files changed

+49
-4
lines changed

4 files changed

+49
-4
lines changed

packages/devextreme/js/common/core/localization/intl/number.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ export default {
9090
return getFormatter(format)(value);
9191
}
9292

93-
return this.callBase.apply(this, arguments);
93+
const result = this.callBase.apply(this, arguments);
94+
95+
return result;
9496
},
9597
_getCurrencySymbolInfo: function(currency) {
9698
const formatter = getCurrencyFormatter(currency);

packages/devextreme/js/common/core/localization/ldml/number.js

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,36 @@ function getGroupSizes(formatString) {
1919
});
2020
}
2121

22+
function splitSignParts(format, separatorChar = ';', escapingChar = ESCAPING_CHAR) {
23+
const parts = [];
24+
let currentPart = '';
25+
let state = 'searchingSeparator';
26+
27+
for(let i = 0; i < format.length; i++) {
28+
const char = format[i];
29+
if(state === 'searchingSeparator' && char === escapingChar) {
30+
state = 'skippingSeparationInsideEscaping';
31+
} else if(state === 'skippingSeparationInsideEscaping' && char === escapingChar) {
32+
state = 'searchingSeparator';
33+
} else if(state === 'searchingSeparator' && char === separatorChar) {
34+
state = 'separating';
35+
parts.push(currentPart);
36+
currentPart = '';
37+
}
38+
39+
if(state !== 'separating') {
40+
currentPart += char;
41+
} else {
42+
state = 'searchingSeparator';
43+
}
44+
}
45+
parts.push(currentPart);
46+
47+
return parts;
48+
}
49+
2250
function getSignParts(format) {
23-
const signParts = format.split(';');
51+
const signParts = splitSignParts(format);
2452

2553
if(signParts.length === 1) {
2654
signParts.push('-' + signParts[0]);
@@ -38,7 +66,7 @@ function isPercentFormat(format) {
3866
}
3967

4068
function removeStubs(str) {
41-
return str.replace(/'.+'/g, '');
69+
return str.replace(/'[^']*'/g, '');
4270
}
4371

4472
function getNonRequiredDigitCount(floatFormat) {

packages/devextreme/js/common/core/localization/number.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,12 @@ const numberLocalization = dependencyInjector({
287287
if(!numberConfig) {
288288
const formatterConfig = this._getSeparators();
289289
formatterConfig.unlimitedIntegerDigits = format.unlimitedIntegerDigits;
290-
return this.convertDigits(getFormatter(format.type, formatterConfig)(value));
290+
291+
const formatter = getFormatter(format.type, formatterConfig)(value);
292+
293+
const result = this.convertDigits(formatter);
294+
295+
return result;
291296
}
292297

293298
return this._formatNumber(value, numberConfig, format);

packages/devextreme/testing/tests/DevExpress.localization/ldml.tests.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,16 @@ QUnit.module('number formatter', () => {
228228
assert.strictEqual(getNumberFormatter('#0.00 руб\'.\'')(15), '15.00 руб.', 'special chars was escaped');
229229
});
230230

231+
QUnit.test('escaped semicolon in format', function(assert) {
232+
const formatter = getNumberFormatter('\';\'plus \';\' 0;minus \';\' 0\';\'');
233+
234+
assert.strictEqual(formatter(-8), 'minus ; 8;', 'semicolons were escaped');
235+
assert.strictEqual(formatter(8), ';plus ; 8', 'semicolons were escaped');
236+
237+
// T1275922
238+
assert.strictEqual(getNumberFormatter('\';\'0')(8), ';8', 'semicolons were escaped');
239+
});
240+
231241
QUnit.test('percent formatting with leading zero', function(assert) {
232242
const formatter = getNumberFormatter('#0.#%;(#0.#%)');
233243

0 commit comments

Comments
 (0)