@ui5/webcomponents-localization 2.20.0 → 2.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/generated/assets/cldr/Unicode-Data-Files-LICENSE.txt +36 -24
  4. package/dist/generated/assets/cldr/ar.json +624 -354
  5. package/dist/generated/assets/cldr/ar_EG.json +624 -354
  6. package/dist/generated/assets/cldr/ar_SA.json +625 -355
  7. package/dist/generated/assets/cldr/bg.json +339 -215
  8. package/dist/generated/assets/cldr/ca.json +633 -509
  9. package/dist/generated/assets/cldr/cnr.json +960 -653
  10. package/dist/generated/assets/cldr/cs.json +343 -531
  11. package/dist/generated/assets/cldr/cy.json +684 -1050
  12. package/dist/generated/assets/cldr/da.json +354 -231
  13. package/dist/generated/assets/cldr/de.json +458 -330
  14. package/dist/generated/assets/cldr/de_AT.json +459 -331
  15. package/dist/generated/assets/cldr/de_CH.json +522 -386
  16. package/dist/generated/assets/cldr/el.json +317 -152
  17. package/dist/generated/assets/cldr/el_CY.json +317 -152
  18. package/dist/generated/assets/cldr/en.json +606 -253
  19. package/dist/generated/assets/cldr/en_AU.json +830 -376
  20. package/dist/generated/assets/cldr/en_GB.json +696 -251
  21. package/dist/generated/assets/cldr/en_HK.json +544 -203
  22. package/dist/generated/assets/cldr/en_IE.json +533 -192
  23. package/dist/generated/assets/cldr/en_IN.json +820 -296
  24. package/dist/generated/assets/cldr/en_NZ.json +534 -193
  25. package/dist/generated/assets/cldr/en_PG.json +509 -168
  26. package/dist/generated/assets/cldr/en_SG.json +526 -185
  27. package/dist/generated/assets/cldr/en_ZA.json +529 -186
  28. package/dist/generated/assets/cldr/es.json +355 -230
  29. package/dist/generated/assets/cldr/es_AR.json +444 -288
  30. package/dist/generated/assets/cldr/es_BO.json +419 -264
  31. package/dist/generated/assets/cldr/es_CL.json +455 -300
  32. package/dist/generated/assets/cldr/es_CO.json +412 -257
  33. package/dist/generated/assets/cldr/es_MX.json +515 -352
  34. package/dist/generated/assets/cldr/es_PE.json +397 -234
  35. package/dist/generated/assets/cldr/es_UY.json +424 -269
  36. package/dist/generated/assets/cldr/es_VE.json +398 -243
  37. package/dist/generated/assets/cldr/et.json +390 -233
  38. package/dist/generated/assets/cldr/fa.json +510 -271
  39. package/dist/generated/assets/cldr/fi.json +497 -251
  40. package/dist/generated/assets/cldr/fr.json +339 -106
  41. package/dist/generated/assets/cldr/fr_BE.json +341 -108
  42. package/dist/generated/assets/cldr/fr_CA.json +652 -397
  43. package/dist/generated/assets/cldr/fr_CH.json +342 -109
  44. package/dist/generated/assets/cldr/fr_LU.json +339 -106
  45. package/dist/generated/assets/cldr/he.json +726 -904
  46. package/dist/generated/assets/cldr/hi.json +529 -288
  47. package/dist/generated/assets/cldr/hr.json +392 -383
  48. package/dist/generated/assets/cldr/hu.json +459 -335
  49. package/dist/generated/assets/cldr/id.json +379 -128
  50. package/dist/generated/assets/cldr/it.json +420 -301
  51. package/dist/generated/assets/cldr/it_CH.json +426 -307
  52. package/dist/generated/assets/cldr/ja.json +332 -74
  53. package/dist/generated/assets/cldr/kk.json +540 -240
  54. package/dist/generated/assets/cldr/ko.json +371 -115
  55. package/dist/generated/assets/cldr/lt.json +428 -545
  56. package/dist/generated/assets/cldr/lv.json +415 -413
  57. package/dist/generated/assets/cldr/mk.json +691 -559
  58. package/dist/generated/assets/cldr/ms.json +395 -136
  59. package/dist/generated/assets/cldr/nb.json +355 -234
  60. package/dist/generated/assets/cldr/nl.json +357 -251
  61. package/dist/generated/assets/cldr/nl_BE.json +428 -322
  62. package/dist/generated/assets/cldr/pl.json +333 -459
  63. package/dist/generated/assets/cldr/pt.json +419 -298
  64. package/dist/generated/assets/cldr/pt_PT.json +362 -231
  65. package/dist/generated/assets/cldr/ro.json +369 -397
  66. package/dist/generated/assets/cldr/ru.json +599 -368
  67. package/dist/generated/assets/cldr/ru_UA.json +598 -367
  68. package/dist/generated/assets/cldr/sk.json +343 -517
  69. package/dist/generated/assets/cldr/sl.json +477 -640
  70. package/dist/generated/assets/cldr/sr.json +478 -531
  71. package/dist/generated/assets/cldr/sr_Latn.json +631 -684
  72. package/dist/generated/assets/cldr/sv.json +395 -258
  73. package/dist/generated/assets/cldr/th.json +452 -199
  74. package/dist/generated/assets/cldr/tr.json +391 -238
  75. package/dist/generated/assets/cldr/uk.json +696 -342
  76. package/dist/generated/assets/cldr/vi.json +836 -487
  77. package/dist/generated/assets/cldr/zh_CN.json +358 -104
  78. package/dist/generated/assets/cldr/zh_HK.json +366 -113
  79. package/dist/generated/assets/cldr/zh_SG.json +371 -118
  80. package/dist/generated/assets/cldr/zh_TW.json +480 -238
  81. package/dist/sap/base/Event.js +2 -2
  82. package/dist/sap/base/Eventing.js +4 -3
  83. package/dist/sap/base/assert.js +1 -1
  84. package/dist/sap/base/config/MemoryConfigurationProvider.js +1 -1
  85. package/dist/sap/base/future.js +12 -0
  86. package/dist/sap/base/i18n/Formatting.d.ts +5 -0
  87. package/dist/sap/base/i18n/Formatting.js +7 -0
  88. package/dist/sap/base/i18n/Formatting.js.map +1 -1
  89. package/dist/sap/base/i18n/LanguageTag.js +18 -12
  90. package/dist/sap/base/i18n/Localization.d.ts +3 -0
  91. package/dist/sap/base/i18n/Localization.js +5 -0
  92. package/dist/sap/base/i18n/Localization.js.map +1 -1
  93. package/dist/sap/base/i18n/ResourceBundle.js +20 -0
  94. package/dist/sap/base/i18n/date/CalendarType.js +9 -8
  95. package/dist/sap/base/i18n/date/CalendarWeekNumbering.js +9 -8
  96. package/dist/sap/base/i18n/date/TimezoneUtils.js +170 -18
  97. package/dist/sap/base/i18n/date/_EnumHelper.js +37 -0
  98. package/dist/sap/base/strings/camelize.js +1 -1
  99. package/dist/sap/base/strings/formatMessage.js +6 -1
  100. package/dist/sap/base/util/Properties.js +12 -0
  101. package/dist/sap/base/util/Version.js +19 -13
  102. package/dist/sap/base/util/_merge.js +1 -1
  103. package/dist/sap/base/util/array/uniqueSort.js +1 -1
  104. package/dist/sap/base/util/deepClone.js +2 -2
  105. package/dist/sap/base/util/deepEqual.js +1 -1
  106. package/dist/sap/base/util/deepExtend.js +60 -0
  107. package/dist/sap/base/util/extend.js +1 -1
  108. package/dist/sap/base/util/fetch.js +8 -0
  109. package/dist/sap/base/util/isEmptyObject.js +1 -1
  110. package/dist/sap/base/util/isPlainObject.js +1 -1
  111. package/dist/sap/base/util/merge.js +57 -0
  112. package/dist/sap/base/util/mixedFetch.js +3 -0
  113. package/dist/sap/base/util/now.js +7 -13
  114. package/dist/sap/base/util/uid.js +1 -1
  115. package/dist/sap/ui/Device.js +27 -0
  116. package/dist/sap/ui/Global.js +1 -0
  117. package/dist/sap/ui/VersionInfo.js +14 -0
  118. package/dist/sap/ui/base/DataType.js +63 -12
  119. package/dist/sap/ui/base/DesignTime.js +11 -0
  120. package/dist/sap/ui/base/EventProvider.js +21 -0
  121. package/dist/sap/ui/base/Metadata.js +45 -10
  122. package/dist/sap/ui/base/Object.js +2 -2
  123. package/dist/sap/ui/base/OwnStatics.js +6 -0
  124. package/dist/sap/ui/base/SyncPromise.js +98 -0
  125. package/dist/sap/ui/core/AnimationMode.js +42 -0
  126. package/dist/sap/ui/core/CalendarType.js +2 -2
  127. package/dist/sap/ui/core/ControlBehavior.js +11 -0
  128. package/dist/sap/ui/core/Lib.js +41 -0
  129. package/dist/sap/ui/core/Locale.js +9 -9
  130. package/dist/sap/ui/core/LocaleData.js +498 -333
  131. package/dist/sap/ui/core/Theming.js +25 -525
  132. package/dist/sap/ui/core/_UrlResolver.js +11 -0
  133. package/dist/sap/ui/core/date/Buddhist.js +2 -2
  134. package/dist/sap/ui/core/date/CalendarUtils.js +14 -12
  135. package/dist/sap/ui/core/date/CalendarWeekNumbering.js +2 -2
  136. package/dist/sap/ui/core/date/Gregorian.js +2 -2
  137. package/dist/sap/ui/core/date/Islamic.js +2 -2
  138. package/dist/sap/ui/core/date/Japanese.js +2 -2
  139. package/dist/sap/ui/core/date/Persian.js +2 -2
  140. package/dist/sap/ui/core/date/UI5Date.js +80 -32
  141. package/dist/sap/ui/core/date/UniversalDate.js +83 -101
  142. package/dist/sap/ui/core/format/DateFormat.js +70 -73
  143. package/dist/sap/ui/core/format/FormatUtils.js +30 -0
  144. package/dist/sap/ui/core/format/NumberFormat.js +917 -588
  145. package/dist/sap/ui/core/format/TimezoneUtil.js +80 -11
  146. package/dist/sap/ui/core/getCompatibilityVersion.js +7 -0
  147. package/dist/sap/ui/core/theming/ThemeHelper.js +14 -0
  148. package/dist/sap/ui/security/Security.js +12 -0
  149. package/dist/sap/ui/util/XMLHelper.js +11 -0
  150. package/dist/sap/ui/util/_URL.js +1 -0
  151. package/dist/ui5loader-autoconfig.js +1 -0
  152. package/package.json +5 -5
  153. package/used-modules.txt +5 -0
@@ -1,17 +1,19 @@
1
1
  /*!
2
2
  * OpenUI5
3
- * (c) Copyright 2009-2024 SAP SE or an SAP affiliate company.
3
+ * (c) Copyright 2026 SAP SE or an SAP affiliate company.
4
4
  * Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
5
5
  */
6
6
  // Provides class sap.ui.core.format.NumberFormat
7
+ import Formatting from "../../../base/i18n/Formatting.js";
8
+ import Localization from "../../../base/i18n/Localization.js";
7
9
  import BaseObject from "../../base/Object.js";
8
10
  import Locale from "../Locale.js";
9
11
  import LocaleData from "../LocaleData.js";
10
12
  import Supportability from "../Supportability.js";
13
+ import FormatUtils from "./FormatUtils.js";
11
14
  import Log from "../../../base/Log.js";
12
15
  import assert from "../../../base/assert.js";
13
16
  import extend from "../../../base/util/extend.js";
14
- import Configuration from "../Configuration.js";
15
17
  /**
16
18
  * Format classes
17
19
  *
@@ -38,17 +40,40 @@ var NumberFormat = BaseObject.extend("sap.ui.core.format.NumberFormat", /** @len
38
40
  throw new Error();
39
41
  }
40
42
  });
41
- var rAllWhiteSpaces = /\s/g,
42
- rDigit = /\d/,
43
- // Regex for checking if a number has leading zeros
44
- rLeadingZeros = /^(-?)0+(\d)/,
45
- // Not matching Sc (currency symbol) and Z (separator) characters
46
- // https://www.unicode.org/reports/tr44/#General_Category_Values
47
- rNotSAndNotZ = /[^\$\xA2-\xA5\u058F\u060B\u09F2\u09F3\u09FB\u0AF1\u0BF9\u0E3F\u17DB\u20A0-\u20BD\uA838\uFDFC\uFE69\uFF04\uFFE0\uFFE1\uFFE5\uFFE6\u0020\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]/,
48
- // Regex for matching the number placeholder in pattern
49
- rNumPlaceHolder = /0+(\.0+)?/,
50
- // Regex for checking that the given string only consists of '0' characters
51
- rOnlyZeros = /^0+$/;
43
+
44
+ // Regex for replacing the number part of a decimal or currency pattern
45
+ const rNumberPattern = /[0#.,]+/;
46
+ const rAllWhiteSpaces = /\s/g;
47
+ // Regex for checking whether the last character belongs to the Unicode General Category L (letter)
48
+ const rEndsWithLetter = /\p{L}$/u;
49
+ // Regex for checking whether the first character belongs to the Unicode General Category L (letter)
50
+ const rStartsWithLetter = /^\p{L}/u;
51
+ // splits a currency pattern into following matching groups:
52
+ // 0: complete match
53
+ // 1: the optional number pattern in front of the currency placeholder
54
+ // 2: the characters between the number pattern in front of the currency placeholder and the currency placeholder
55
+ // 3: the currency placeholder
56
+ // 4: the characters between the currency placeholder and the number pattern after the currency placeholder
57
+ // 5: the optional number pattern after the currency placeholder
58
+ const rSplitCurrencyPattern = /([0#.,]*)([^0#.,]*)(¤)([^0#.,]*)([0#.,]*)/;
59
+ // Regex for checking if a number has leading zeros
60
+ const rLeadingZeros = /^(-?)0+(\d)/;
61
+ // Regex for matching the number placeholder in pattern
62
+ const rNumPlaceHolder = /0+(\.0+)?/;
63
+ // Regex for checking that the given string only consists of '0' characters
64
+ const rOnlyZeros = /^0+$/;
65
+ // A regular expresssion that can be used to remove a leading "-" from a number representing zero,
66
+ // e.g. "-0", or "-0.00"; $1 contains the number without the leading "-"
67
+ const rRemoveMinusFromZero = /^-(0(?:.0+)?)$/;
68
+ // A regular expression that can be used to remove trailing zeros from a number
69
+ const rTrailingZeros = /0+$/;
70
+ // A regular expression that can be used to remove all RTL characters
71
+ // see: https://www.unicode.org/reports/tr44/#Bidi_Class_Values (Explicit Formatting Types)
72
+ const rAllRTLCharacters = /[\u061c\u200e\u200f\u202a\u202b\u202c]/g;
73
+ // A regular expression that can be used to remove the left-to-right mark character
74
+ const rLeftToRightMark = /\u200e/;
75
+ // Array of all available power of tens for short formats, max: 100 000 000 000 000
76
+ const aPowerOfTens = [10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000];
52
77
 
53
78
  /*
54
79
  * Is used to validate existing grouping separators.
@@ -71,8 +96,8 @@ var mNumberType = {
71
96
  };
72
97
 
73
98
  /**
74
- * Specifies a rounding behavior for numerical operations capable of discarding precision. Each rounding mode in this object indicates how the least
75
- * significant returned digits of rounded result is to be calculated.
99
+ * Specifies a rounding behavior for numerical operations capable of discarding precision. Each rounding mode in
100
+ * this object indicates how the least significant returned digits of rounded result are to be calculated.
76
101
  *
77
102
  * @public
78
103
  * @enum {string}
@@ -80,49 +105,61 @@ var mNumberType = {
80
105
  */
81
106
  var mRoundingMode = {
82
107
  /**
83
- * Rounding mode to round towards negative infinity
108
+ * Rounding mode to round towards negative infinity; examples of rounding results to one fractional digit: 0.51
109
+ * is rounded to 0.5, and -0.51 is rounded to -0.6.
84
110
  * @public
85
111
  * @type {string}
86
112
  */
87
113
  FLOOR: "FLOOR",
88
114
  /**
89
- * Rounding mode to round towards positive infinity
115
+ * Rounding mode to round towards positive infinity; examples of rounding results to one fractional digit: 0.51
116
+ * is rounded to 0.6, and -0.51 is rounded to -0.5.
90
117
  * @public
91
118
  * @type {string}
92
119
  */
93
120
  CEILING: "CEILING",
94
121
  /**
95
- * Rounding mode to round towards zero
122
+ * Rounding mode to round towards zero; examples of rounding results to one fractional digit: 0.59 is rounded to
123
+ * 0.5, and -0.59 is rounded to -0.5.
96
124
  * @public
97
125
  * @type {string}
98
126
  */
99
127
  TOWARDS_ZERO: "TOWARDS_ZERO",
100
128
  /**
101
- * Rounding mode to round away from zero
129
+ * Rounding mode to round away from zero; examples of rounding results to one fractional digit: 0.51 is rounded
130
+ * to 0.6, and -0.51 is rounded to -0.6.
102
131
  * @public
103
132
  * @type {string}
104
133
  */
105
134
  AWAY_FROM_ZERO: "AWAY_FROM_ZERO",
106
135
  /**
107
- * Rounding mode to round towards the nearest neighbor unless both neighbors are equidistant, in which case round towards negative infinity.
136
+ * Rounding mode to round towards the nearest neighbor, unless both neighbors are equidistant, in which case
137
+ * round towards negative infinity; examples of rounding results to one fractional digit: 0.54 or 0.46 are
138
+ * rounded to 0.5, -0.54 or -0.46 are rounded to -0.5, 0.55 is rounded to 0.5, and -0.55 is rounded to -0.6.
108
139
  * @public
109
140
  * @type {string}
110
141
  */
111
142
  HALF_FLOOR: "HALF_FLOOR",
112
143
  /**
113
- * Rounding mode to round towards the nearest neighbor unless both neighbors are equidistant, in which case round towards positive infinity.
144
+ * Rounding mode to round towards the nearest neighbor, unless both neighbors are equidistant, in which case
145
+ * round towards positive infinity; examples of rounding results to one fractional digit: 0.54 or 0.46 are
146
+ * rounded to 0.5, -0.54 or -0.46 are rounded to -0.5, 0.55 is rounded to 0.6, and -0.55 is rounded to -0.5.
114
147
  * @public
115
148
  * @type {string}
116
149
  */
117
150
  HALF_CEILING: "HALF_CEILING",
118
151
  /**
119
- * Rounding mode to round towards the nearest neighbor unless both neighbors are equidistant, in which case round towards zero.
152
+ * Rounding mode to round towards the nearest neighbor, unless both neighbors are equidistant, in which case
153
+ * round towards zero; examples of rounding results to one fractional digit: 0.54 or 0.46 are rounded to 0.5,
154
+ * -0.54 or -0.46 are rounded to -0.5, 0.55 is rounded to 0.5, and -0.55 is rounded to -0.5.
120
155
  * @public
121
156
  * @type {string}
122
157
  */
123
158
  HALF_TOWARDS_ZERO: "HALF_TOWARDS_ZERO",
124
159
  /**
125
- * Rounding mode to round towards the nearest neighbor unless both neighbors are equidistant, in which case round away from zero.
160
+ * Rounding mode to round towards the nearest neighbor unless, both neighbors are equidistant, in which case
161
+ * round away from zero; examples of rounding results to one fractional digit: 0.54 or 0.46 are rounded to 0.5,
162
+ * -0.54 or -0.46 are rounded to -0.5, 0.55 is rounded to 0.6, and -0.55 is rounded to -0.6.
126
163
  * @public
127
164
  * @type {string}
128
165
  */
@@ -130,7 +167,77 @@ var mRoundingMode = {
130
167
  };
131
168
 
132
169
  /**
133
- * Derives the maximal possible decimals from the given format option's <code>maxFractionDigits</code>
170
+ * Adds the summand given as a number to an decimal given as a string.
171
+ *
172
+ * @param {string} sDecimal A positive or negative decimal number as string
173
+ * @param {int} iSummand An integer between -9 and 9 to be added to the given decimal number
174
+ * @returns {string} The sum of the two numbers as a string
175
+ *
176
+ * @private
177
+ */
178
+ NumberFormat.add = function (sDecimal, iSummand) {
179
+ const aParts = sDecimal.split(".");
180
+ let sInteger = aParts[0];
181
+ const sFractionPart = aParts[1];
182
+ const bNegative = sInteger[0] === "-";
183
+ if (bNegative) {
184
+ sInteger = sInteger.slice(1);
185
+ iSummand = -iSummand;
186
+ }
187
+ const aDigits = sInteger.split("").map(Number);
188
+ const iLastIndex = aDigits.length - 1;
189
+ aDigits[iLastIndex] += iSummand;
190
+ for (let i = iLastIndex; i >= 0; i -= 1) {
191
+ if (aDigits[i] >= 10) {
192
+ aDigits[i] = aDigits[i] % 10;
193
+ if (i === 0) {
194
+ aDigits.unshift(1);
195
+ break;
196
+ }
197
+ aDigits[i - 1] += 1;
198
+ } else if (aDigits[i] < 0 && i > 0) {
199
+ aDigits[i] = 10 + aDigits[i];
200
+ aDigits[i - 1] -= 1;
201
+ if (i === 1 && aDigits[0] === 0) {
202
+ aDigits.shift();
203
+ break;
204
+ }
205
+ } else {
206
+ break;
207
+ }
208
+ }
209
+ if (bNegative) {
210
+ aDigits[0] = -aDigits[0];
211
+ }
212
+ let sResult = aDigits.join("");
213
+ if (!sFractionPart) {
214
+ return sResult;
215
+ }
216
+
217
+ // If sResult is 0, the sign may be lost and has to be restored, e.g. "-5.123" + 5 => -5 + 5 = 0 => "-0.123"
218
+ sResult = sResult === "0" && bNegative ? "-0" : sResult;
219
+ const sResultSign = sResult[0] === "-" ? "-" : "";
220
+ // If both signs are equal, the fraction part can simply be appended
221
+ if (bNegative === !!sResultSign) {
222
+ return sResult + "." + sFractionPart;
223
+ }
224
+
225
+ // If the signs are different, aDigits contains only one digit which is different from zero; to compute the
226
+ // result, the result sign has to be kept, the integer part is the absolute sResult reduced by one, and the
227
+ // fractional part is (1 - fractional part), e.g. "2.123" - 5 => 2 - 5 = -3 => sign = "-", integer part is
228
+ // |-3| - 1 = 2 and fractional part is 1 - 0.123 = 0.877 without the leading "0." => "-2.877"
229
+ const aFractionDigits = sFractionPart.split("").map(Number);
230
+ for (let i = aFractionDigits.length - 1; i >= 0; i -= 1) {
231
+ aFractionDigits[i] = 10 - aFractionDigits[i];
232
+ if (i > 0) {
233
+ aFractionDigits[i - 1] += 1;
234
+ }
235
+ }
236
+ return sResultSign + (Math.abs(aDigits[0]) - 1) + "." + aFractionDigits.join("");
237
+ };
238
+
239
+ /**
240
+ * Derives the maximum number of possible decimals from the given format option's <code>maxFractionDigits</code>
134
241
  * and <code>decimals</code> properties.
135
242
  *
136
243
  * If <code>decimals</code> and <code>maxFractionDigits</code> are >= 0, then the minimum of
@@ -148,7 +255,7 @@ var mRoundingMode = {
148
255
  * @private
149
256
  * @static
150
257
  */
151
- NumberFormat.getMaximalDecimals = function ({
258
+ NumberFormat.getMaximumDecimals = function ({
152
259
  decimals,
153
260
  maxFractionDigits
154
261
  }) {
@@ -157,25 +264,77 @@ NumberFormat.getMaximalDecimals = function ({
157
264
  }
158
265
  return decimals;
159
266
  };
160
- var mRoundingFunction = {};
161
- mRoundingFunction[mRoundingMode.FLOOR] = Math.floor;
162
- mRoundingFunction[mRoundingMode.CEILING] = Math.ceil;
163
- mRoundingFunction[mRoundingMode.TOWARDS_ZERO] = function (nValue) {
164
- return nValue > 0 ? Math.floor(nValue) : Math.ceil(nValue);
165
- };
166
- mRoundingFunction[mRoundingMode.AWAY_FROM_ZERO] = function (nValue) {
167
- return nValue > 0 ? Math.ceil(nValue) : Math.floor(nValue);
168
- };
169
- mRoundingFunction[mRoundingMode.HALF_TOWARDS_ZERO] = function (nValue) {
170
- return nValue > 0 ? Math.ceil(nValue - 0.5) : Math.floor(nValue + 0.5);
171
- };
172
- mRoundingFunction[mRoundingMode.HALF_AWAY_FROM_ZERO] = function (nValue) {
173
- return nValue > 0 ? Math.floor(nValue + 0.5) : Math.ceil(nValue - 0.5);
174
- };
175
- mRoundingFunction[mRoundingMode.HALF_FLOOR] = function (nValue) {
176
- return Math.ceil(nValue - 0.5);
267
+
268
+ /**
269
+ * Rounds the given number up to the smallest integer greater than or equal to the given number.
270
+ *
271
+ * @param {number|string} vNumber
272
+ * The number to be rounded up; it has at least one digit in front of the decimal point in case of type "string"
273
+ * @returns {number|string}
274
+ * The smallest integer greater than or equal to the given number; the returned type is the same as the type of
275
+ * the given number
276
+ */
277
+ function ceil(vNumber) {
278
+ if (typeof vNumber === "number") {
279
+ return Math.ceil(vNumber);
280
+ }
281
+ const [sIntegerPart, sFractionPart = "0"] = vNumber.split(".");
282
+ return rOnlyZeros.test(sFractionPart) || sIntegerPart[0] === "-" ? sIntegerPart : NumberFormat.add(sIntegerPart, 1);
283
+ }
284
+
285
+ /**
286
+ * Rounds the given number down to the largest integer less than or equal to the given number.
287
+ *
288
+ * @param {number|string} vNumber
289
+ * The number to be rounded down; it has at least one digit in front of the decimal point in case of type "string"
290
+ * @returns {number|string}
291
+ * The largest integer less than or equal to the given number; the returned type is the same as the type of the
292
+ * given number
293
+ */
294
+ function floor(vNumber) {
295
+ if (typeof vNumber === "number") {
296
+ return Math.floor(vNumber);
297
+ }
298
+ const [sIntegerPart, sFractionPart = "0"] = vNumber.split(".");
299
+ return rOnlyZeros.test(sFractionPart) || sIntegerPart[0] !== "-" ? sIntegerPart : NumberFormat.add(sIntegerPart, -1);
300
+ }
301
+
302
+ /**
303
+ * Adds 0.5 to or subtracts 0.5 from the given number.
304
+ *
305
+ * @param {number|string} vNumber
306
+ * The number to be increased or decreased by 0.5
307
+ * @param {boolean} bIncrease
308
+ * Whether to increase the number by 0.5; otherwise the number is decreased by 0.5
309
+ * @returns {number|string}
310
+ * The number increased or decreased by 0.5; the returned type is the same as the type of the given number
311
+ */
312
+ function increaseOrDecreaseByHalf(vNumber, bIncrease) {
313
+ if (typeof vNumber === "number") {
314
+ return bIncrease ? vNumber + 0.5 : vNumber - 0.5;
315
+ }
316
+ vNumber = NumberFormat._shiftDecimalPoint(vNumber, 1);
317
+ vNumber = NumberFormat.add(vNumber, bIncrease ? 5 : -5);
318
+ return NumberFormat._shiftDecimalPoint(vNumber, -1);
319
+ }
320
+ const mRoundingFunction = {
321
+ [mRoundingMode.FLOOR]: floor,
322
+ [mRoundingMode.CEILING]: ceil,
323
+ [mRoundingMode.TOWARDS_ZERO]: vNumber => vNumber > 0 ? floor(vNumber) : ceil(vNumber),
324
+ [mRoundingMode.AWAY_FROM_ZERO]: vNumber => vNumber > 0 ? ceil(vNumber) : floor(vNumber),
325
+ [mRoundingMode.HALF_TOWARDS_ZERO]: vNumber => {
326
+ const bPositive = vNumber > 0;
327
+ vNumber = increaseOrDecreaseByHalf(vNumber, !bPositive);
328
+ return bPositive ? ceil(vNumber) : floor(vNumber);
329
+ },
330
+ [mRoundingMode.HALF_AWAY_FROM_ZERO]: vNumber => {
331
+ const bPositive = vNumber > 0;
332
+ vNumber = increaseOrDecreaseByHalf(vNumber, bPositive);
333
+ return bPositive ? floor(vNumber) : ceil(vNumber);
334
+ },
335
+ [mRoundingMode.HALF_FLOOR]: vNumber => ceil(increaseOrDecreaseByHalf(vNumber, false)),
336
+ [mRoundingMode.HALF_CEILING]: vNumber => floor(increaseOrDecreaseByHalf(vNumber, true))
177
337
  };
178
- mRoundingFunction[mRoundingMode.HALF_CEILING] = Math.round;
179
338
  NumberFormat.RoundingMode = mRoundingMode;
180
339
 
181
340
  /*
@@ -322,6 +481,42 @@ NumberFormat.oDefaultUnitFormat = {
322
481
  showScale: true
323
482
  };
324
483
 
484
+ /**
485
+ * Checks the given format options if decimal padding is allowed and if the decimal padding value is supported.
486
+ *
487
+ * @param {object} [oFormatOptions]
488
+ * The format options to be checked
489
+ * @param {int} [oFormatOptions.decimalPadding]
490
+ * The format option decimal padding
491
+ * @param {boolean} [oFormatOptions.showMeasure]
492
+ * The format option show measure
493
+ * @param {string} [oFormatOptions.style]
494
+ * The format option style
495
+ * @param {boolean} [bDecimalPaddingSupported=true]
496
+ * Whether decimal padding is supported
497
+ * @param {boolean} [bShowMeasureMustBeFalse=false]
498
+ * Whether the format option <code>showMeasure</code> must be set to false
499
+ * @throws {Error} If decimal padding cannot be used or the given decimal padding value is not supported
500
+ *
501
+ * @private
502
+ * @static
503
+ */
504
+ NumberFormat.checkDecimalPadding = function (oFormatOptions, bDecimalPaddingSupported = true, bShowMeasureMustBeFalse = false) {
505
+ const iDecimalPadding = oFormatOptions?.decimalPadding;
506
+ if (!bDecimalPaddingSupported && iDecimalPadding !== undefined) {
507
+ throw new Error("Unsupported format option: 'decimalPadding' cannot be used with an integer or percent" + " instance of sap.ui.core.format.NumberFormat");
508
+ }
509
+ if (bShowMeasureMustBeFalse && iDecimalPadding && oFormatOptions?.showMeasure !== false) {
510
+ throw new Error("The format option 'decimalPadding' can only be used if the format option" + " 'showMeasure' is set to false");
511
+ }
512
+ if (iDecimalPadding < 1) {
513
+ throw new Error("The format option 'decimalPadding' must have a value greater than 0");
514
+ }
515
+ if (iDecimalPadding > 0 && (oFormatOptions?.style === "short" || oFormatOptions?.style === "long")) {
516
+ throw new Error("The format option 'decimalPadding' can only be used if the format option" + " 'style' is not set to 'short' or 'long'");
517
+ }
518
+ };
519
+
325
520
  /**
326
521
  * An alias for {@link #getFloatInstance}.
327
522
  *
@@ -338,9 +533,6 @@ NumberFormat.getInstance = function (oFormatOptions, oLocale) {
338
533
  /**
339
534
  * Get a float instance of the NumberFormat, which can be used for formatting.
340
535
  *
341
- * If no locale is given, the currently configured
342
- * {@link sap.ui.core.Configuration.FormatSettings#getFormatLocale formatLocale} will be used.
343
- *
344
536
  * <p>
345
537
  * This instance has HALF_AWAY_FROM_ZERO set as default rounding mode.
346
538
  * Please set the roundingMode property in oFormatOptions to change the
@@ -361,6 +553,16 @@ NumberFormat.getInstance = function (oFormatOptions, oLocale) {
361
553
  *
362
554
  * @param {object} [oFormatOptions] The option object, which supports the following parameters.
363
555
  * If no options are given, default values according to the type and locale settings are used.
556
+ * @param {int} [oFormatOptions.decimalPadding]
557
+ * The target length of places after the decimal separator; if the number has fewer decimal places than given in
558
+ * this option, it is padded with whitespaces at the end up to the target length. An additional whitespace
559
+ * character for the decimal separator is added for a number without any decimals.
560
+ * <b>Note:</b> This format option is only allowed if the following conditions apply:
561
+ * <ul>
562
+ * <li>It has a value greater than 0.</li>
563
+ * <li>The <code>oFormatOptions.style</code> format option is <b>not</b> set to <code>"short"</code> or
564
+ * <code>"long"</code>.</li>
565
+ * </ul>
364
566
  * @param {int} [oFormatOptions.decimals] defines the number of decimal digits
365
567
  * @param {string} [oFormatOptions.decimalSeparator] defines the character used as decimal separator.
366
568
  * Note: <code>decimalSeparator</code> must always be different from <code>groupingSeparator</code>.
@@ -373,11 +575,14 @@ NumberFormat.getInstance = function (oFormatOptions, oLocale) {
373
575
  * @param {int} [oFormatOptions.groupingBaseSize=3] defines the grouping base size in digits if
374
576
  * it is different from the grouping size (e.g. Indian grouping)
375
577
  * @param {boolean} [oFormatOptions.groupingEnabled=true] defines whether grouping is enabled
376
- * (grouping separators are shown)
578
+ * (grouping separators are shown).
579
+ * <b>Note:</b> Grouping is disabled if the <code>groupingSize</code> format option is set to
580
+ * a non-positive value.
377
581
  * @param {string} [oFormatOptions.groupingSeparator] defines the character used as grouping separator.
378
582
  * Note: <code>groupingSeparator</code> must always be different from <code>decimalSeparator</code>.
379
583
  * @param {int} [oFormatOptions.groupingSize=3] defines the grouping size in digits; the default
380
- * is <code>3</code>. It must be a positive number.
584
+ * is <code>3</code>.
585
+ * <b>Note:</b> If this format option is set to a non-positive value, grouping will be disabled entirely.
381
586
  * @param {int} [oFormatOptions.maxFractionDigits=99] defines the maximum number of decimal digits
382
587
  * @param {int} [oFormatOptions.maxIntegerDigits=99] defines the maximum number of non-decimal digits.
383
588
  * If the number exceeds this maximum, e.g. 1e+120, "?" characters are shown instead of digits.
@@ -390,19 +595,25 @@ NumberFormat.getInstance = function (oFormatOptions, oLocale) {
390
595
  * to "0.005".
391
596
  * @param {string} [oFormatOptions.pattern] CLDR number pattern which is used to format the number
392
597
  * @param {string} [oFormatOptions.plusSign] defines the used plus symbol
393
- * @param {int} [oFormatOptions.precision] defines the numerical precision; the number of decimals
394
- * is calculated dependent on the integer digits
598
+ * @param {int} [oFormatOptions.precision] The maximum number of digits in the formatted representation of a number;
599
+ * if the <code>precision</code> is less than the overall length of the number, its fractional part is truncated
600
+ * through rounding. As the <code>precision</code> only affects the rounding of a number, its integer part can
601
+ * retain more digits than defined by this parameter.
602
+ * <b>Example:</b> With a <code>precision</code> of 2, <code>234.567</code> is formatted to <code>235</code>.
603
+ * <b>Note:</b> The formatted output may differ depending on locale.
395
604
  * @param {boolean} [oFormatOptions.preserveDecimals=false] Whether {@link #format} preserves
396
605
  * decimal digits except trailing zeros in case there are more decimals than the
397
606
  * <code>maxFractionDigits</code> format option allows.
398
607
  * If decimals are not preserved, the formatted number is rounded to <code>maxFractionDigits</code>.
399
608
  * @param {sap.ui.core.format.NumberFormat.RoundingMode} [oFormatOptions.roundingMode=HALF_AWAY_FROM_ZERO]
400
- * specifies the rounding behavior for discarding the digits after the maximum fraction digits
401
- * defined by maxFractionDigits. Rounding will only be applied if the passed value is of type <code>number</code>.
609
+ * Specifies the rounding behavior for discarding the digits after the maximum fraction digits
610
+ * defined by <code>maxFractionDigits</code>.
402
611
  * This can be assigned
403
612
  * <ul>
404
613
  * <li>by value in {@link sap.ui.core.format.NumberFormat.RoundingMode RoundingMode},</li>
405
- * <li>via a function that is used for rounding the number and takes two parameters: the number itself, and the number of decimal digits that should be reserved.</li>
614
+ * <li>via a function that is used for rounding the number and takes two parameters: the number itself, and the
615
+ * number of decimal digits that should be reserved. <b>Using a function is deprecated since 1.121.0</b>;
616
+ * string based numbers are not rounded via this custom function.</li>
406
617
  * </ul>
407
618
  * @param {int} [oFormatOptions.shortDecimals] defines the number of decimal in the shortened format string. If this isn't specified, the 'decimals' options is used
408
619
  * @param {int} [oFormatOptions.shortLimit] only use short number formatting for values above this limit
@@ -416,24 +627,26 @@ NumberFormat.getInstance = function (oFormatOptions, oLocale) {
416
627
  * numbers are formatted into compact forms. When this option is set, the default value of the
417
628
  * 'precision' option is set to 2. This can be changed by setting either min/maxFractionDigits,
418
629
  * decimals, shortDecimals, or the 'precision' option itself.
419
- * @param {sap.ui.core.Locale} [oLocale] Locale to get the formatter for
630
+ * @param {sap.ui.core.Locale} [oLocale]
631
+ * The locale to get the formatter for; if no locale is given, a locale for the currently configured language is
632
+ * used; see {@link module:sap/base/i18n/Formatting.getLanguageTag Formatting.getLanguageTag}
420
633
  * @return {sap.ui.core.format.NumberFormat} float instance of the NumberFormat
634
+ * @throws {Error} If the <code>oFormatOptions.decimalPadding</code> is set but is not allowed
421
635
  * @static
422
636
  * @public
423
637
  */
424
638
  NumberFormat.getFloatInstance = function (oFormatOptions, oLocale) {
425
- var oFormat = this.createInstance(oFormatOptions, oLocale),
426
- oLocaleFormatOptions = this.getLocaleFormatOptions(oFormat.oLocaleData, mNumberType.FLOAT);
639
+ NumberFormat.checkDecimalPadding(oFormatOptions);
640
+ const oFormat = NumberFormat.createInstance(oFormatOptions, oLocale);
641
+ const oLocaleFormatOptions = oFormat.getLocaleFormatOptions(mNumberType.FLOAT);
427
642
  oFormat.oFormatOptions = extend({}, this.oDefaultFloatFormat, oLocaleFormatOptions, oFormat.oOriginalFormatOptions);
643
+ oFormat.checkGroupingFormatOptions();
428
644
  return oFormat;
429
645
  };
430
646
 
431
647
  /**
432
648
  * Get an integer instance of the NumberFormat, which can be used for formatting.
433
649
  *
434
- * If no locale is given, the currently configured
435
- * {@link sap.ui.core.Configuration.FormatSettings#getFormatLocale formatLocale} will be used.
436
- *
437
650
  * <p>
438
651
  * This instance has TOWARDS_ZERO set as default rounding mode.
439
652
  * Please set the roundingMode property in oFormatOptions to change the
@@ -453,6 +666,7 @@ NumberFormat.getFloatInstance = function (oFormatOptions, oLocale) {
453
666
  *
454
667
  * @param {object} [oFormatOptions] The option object, which supports the following parameters.
455
668
  * If no options are given, default values according to the type and locale settings are used.
669
+ * @param {int} [oFormatOptions.decimalPadding] Not supported.
456
670
  * @param {int} [oFormatOptions.decimals] defines the number of decimal digits
457
671
  * @param {string} [oFormatOptions.decimalSeparator] defines the character used as decimal separator.
458
672
  * Note: <code>decimalSeparator</code> must always be different from <code>groupingSeparator</code>.
@@ -465,11 +679,14 @@ NumberFormat.getFloatInstance = function (oFormatOptions, oLocale) {
465
679
  * @param {int} [oFormatOptions.groupingBaseSize=3] defines the grouping base size in digits if
466
680
  * it is different from the grouping size (e.g. Indian grouping)
467
681
  * @param {boolean} [oFormatOptions.groupingEnabled=false] defines whether grouping is enabled
468
- * (grouping separators are shown)
682
+ * (grouping separators are shown).
683
+ * <b>Note:</b> Grouping is disabled if the <code>groupingSize</code> format option is set to
684
+ * a non-positive value.
469
685
  * @param {string} [oFormatOptions.groupingSeparator] defines the character used as grouping separator.
470
686
  * Note: <code>groupingSeparator</code> must always be different from <code>decimalSeparator</code>.
471
687
  * @param {int} [oFormatOptions.groupingSize=3] defines the grouping size in digits; the default
472
- * is <code>3</code>. It must be a positive number.
688
+ * is <code>3</code>.
689
+ * <b>Note:</b> If this format option is set to a non-positive value, grouping will be disabled entirely.
473
690
  * @param {int} [oFormatOptions.maxFractionDigits=0] defines the maximum number of decimal digits
474
691
  * @param {int} [oFormatOptions.maxIntegerDigits=99] defines the maximum number of non-decimal digits.
475
692
  * If the number exceeds this maximum, e.g. 1e+120, "?" characters are shown instead of digits.
@@ -482,19 +699,28 @@ NumberFormat.getFloatInstance = function (oFormatOptions, oLocale) {
482
699
  * to "5000".
483
700
  * @param {string} [oFormatOptions.pattern] CLDR number pattern which is used to format the number
484
701
  * @param {string} [oFormatOptions.plusSign] defines the used plus symbol
485
- * @param {int} [oFormatOptions.precision] defines the numerical precision; the number of decimals
486
- * is calculated dependent on the integer digits
702
+ * @param {int} [oFormatOptions.precision] <b>Note:</b> Only considered if the number format leads to a
703
+ * representation with decimal places, e.g. if the option <code>style: "short"</code> is set.
704
+ * The maximum number of digits in the formatted representation of a number; if the <code>precision</code> is
705
+ * less than the overall length of the number, its fractional part is truncated through rounding.
706
+ * As the <code>precision</code> only affects the rounding of a number, its integer part can retain more digits
707
+ * than defined by this parameter.
708
+ * <b>Example:</b> With a <code>precision</code> of 2 and <code>style: "short"</code>,
709
+ * <code>234567</code> is formatted to <code>"235K"</code>.
710
+ * <b>Note:</b> The formatted output may differ depending on locale.
487
711
  * @param {boolean} [oFormatOptions.preserveDecimals=false] Whether {@link #format} preserves
488
712
  * decimal digits except trailing zeros in case there are more decimals than the
489
713
  * <code>maxFractionDigits</code> format option allows.
490
714
  * If decimals are not preserved, the formatted number is rounded to <code>maxFractionDigits</code>.
491
715
  * @param {sap.ui.core.format.NumberFormat.RoundingMode} [oFormatOptions.roundingMode=TOWARDS_ZERO]
492
- * specifies the rounding behavior for discarding the digits after the maximum fraction digits
493
- * defined by maxFractionDigits. Rounding will only be applied if the passed value is of type <code>number</code>.
716
+ * Specifies the rounding behavior for discarding the digits after the maximum fraction digits
717
+ * defined by <code>maxFractionDigits</code>.
494
718
  * This can be assigned
495
719
  * <ul>
496
720
  * <li>by value in {@link sap.ui.core.format.NumberFormat.RoundingMode RoundingMode},</li>
497
- * <li>via a function that is used for rounding the number and takes two parameters: the number itself, and the number of decimal digits that should be reserved.</li>
721
+ * <li>via a function that is used for rounding the number and takes two parameters: the number itself, and the
722
+ * number of decimal digits that should be reserved. <b>Using a function is deprecated since 1.121.0</b>;
723
+ * string based numbers are not rounded via this custom function.</li>
498
724
  * </ul>
499
725
  * @param {int} [oFormatOptions.shortDecimals] defines the number of decimal in the shortened format string. If this isn't specified, the 'decimals' options is used
500
726
  * @param {int} [oFormatOptions.shortLimit] only use short number formatting for values above this limit
@@ -508,24 +734,26 @@ NumberFormat.getFloatInstance = function (oFormatOptions, oLocale) {
508
734
  * numbers are formatted into compact forms. When this option is set, the default value of the
509
735
  * 'precision' option is set to 2. This can be changed by setting either min/maxFractionDigits,
510
736
  * decimals, shortDecimals, or the 'precision' option itself.
511
- * @param {sap.ui.core.Locale} [oLocale] Locale to get the formatter for
737
+ * @param {sap.ui.core.Locale} [oLocale]
738
+ * The locale to get the formatter for; if no locale is given, a locale for the currently configured language is
739
+ * used; see {@link module:sap/base/i18n/Formatting.getLanguageTag Formatting.getLanguageTag}
512
740
  * @return {sap.ui.core.format.NumberFormat} integer instance of the NumberFormat
741
+ * @throws {Error} If the <code>oFormatOptions.decimalPadding</code> format option is provided
513
742
  * @static
514
743
  * @public
515
744
  */
516
745
  NumberFormat.getIntegerInstance = function (oFormatOptions, oLocale) {
517
- var oFormat = this.createInstance(oFormatOptions, oLocale),
518
- oLocaleFormatOptions = this.getLocaleFormatOptions(oFormat.oLocaleData, mNumberType.INTEGER);
519
- oFormat.oFormatOptions = extend({}, this.oDefaultIntegerFormat, oLocaleFormatOptions, oFormat.oOriginalFormatOptions);
746
+ NumberFormat.checkDecimalPadding(oFormatOptions, false);
747
+ const oFormat = NumberFormat.createInstance(oFormatOptions, oLocale);
748
+ const oLocaleFormatOptions = oFormat.getLocaleFormatOptions(mNumberType.INTEGER);
749
+ oFormat.oFormatOptions = extend({}, NumberFormat.oDefaultIntegerFormat, oLocaleFormatOptions, oFormat.oOriginalFormatOptions);
750
+ oFormat.checkGroupingFormatOptions();
520
751
  return oFormat;
521
752
  };
522
753
 
523
754
  /**
524
755
  * Get a currency instance of the NumberFormat, which can be used for formatting.
525
756
  *
526
- * If no locale is given, the currently configured
527
- * {@link sap.ui.core.Configuration.FormatSettings#getFormatLocale formatLocale} will be used.
528
- *
529
757
  * <p>
530
758
  * This instance has HALF_AWAY_FROM_ZERO set as default rounding mode.
531
759
  * Please set the roundingMode property in oFormatOptions to change the
@@ -550,7 +778,9 @@ NumberFormat.getIntegerInstance = function (oFormatOptions, oLocale) {
550
778
  *
551
779
  * As an alternative to using a fixed <code>symbol</code> for your custom currencies, you can also provide an ISO-Code.
552
780
  * The provided ISO-Code will be used to look up the currency symbol in the global configuration,
553
- * either defined in the CLDR or custom defined on the Format Settings (see {@link sap.ui.core.Configuration.FormatSettings#setCustomCurrencies}, {@link sap.ui.core.Configuration.FormatSettings#addCustomCurrencies}).
781
+ * either defined in the CLDR or custom defined on the Format Settings (see
782
+ * {@link module:sap/base/i18n/Formatting.setCustomCurrencies Formatting.setCustomCurrencies},
783
+ * {@link module:sap/base/i18n/Formatting.addCustomCurrencies Formatting.addCustomCurrencies}).
554
784
  *
555
785
  * If no symbol is given at all, the custom currency key is used for formatting.
556
786
  *
@@ -587,7 +817,19 @@ NumberFormat.getIntegerInstance = function (oFormatOptions, oLocale) {
587
817
  * If custom currencies are defined on the instance, no other currencies can be formatted and parsed by this instance.
588
818
  * Globally available custom currencies can be added via the global configuration.
589
819
  * See the above examples.
590
- * See also {@link sap.ui.core.Configuration.FormatSettings#setCustomCurrencies} and {@link sap.ui.core.Configuration.FormatSettings#addCustomCurrencies}.
820
+ * See also {@link module:sap/base/i18n/Formatting.setCustomCurrencies Formatting.setCustomCurrencies} and
821
+ * {@link module:sap/base/i18n/Formatting.addCustomCurrencies Formatting.addCustomCurrencies}.
822
+ * @param {int} [oFormatOptions.decimalPadding]
823
+ * The target length of places after the decimal separator; if the number has fewer decimal places than given in
824
+ * this option, it is padded with whitespaces at the end up to the target length. An additional whitespace
825
+ * character for the decimal separator is added for a number without any decimals.
826
+ * <b>Note:</b> This format option is only allowed if the following conditions apply:
827
+ * <ul>
828
+ * <li>It has a value greater than 0.</li>
829
+ * <li>The <code>FormatOptions.showMeasure</code> format option is set to <code>false</code>.</li>
830
+ * <li>The <code>oFormatOptions.style</code> format option is <b>not</b> set to <code>"short"</code> or
831
+ * <code>"long"</code>.</li>
832
+ * </ul>
591
833
  * @param {int} [oFormatOptions.decimals] defines the number of decimal digits
592
834
  * @param {string} [oFormatOptions.decimalSeparator] defines the character used as decimal separator.
593
835
  * Note: <code>decimalSeparator</code> must always be different from <code>groupingSeparator</code>.
@@ -600,15 +842,20 @@ NumberFormat.getIntegerInstance = function (oFormatOptions, oLocale) {
600
842
  * @param {int} [oFormatOptions.groupingBaseSize=3] defines the grouping base size in digits if
601
843
  * it is different from the grouping size (e.g. Indian grouping)
602
844
  * @param {boolean} [oFormatOptions.groupingEnabled=true] defines whether grouping is enabled
603
- * (grouping separators are shown)
845
+ * (grouping separators are shown).
846
+ * <b>Note:</b> Grouping is disabled if the <code>groupingSize</code> format option is set to
847
+ * a non-positive value.
604
848
  * @param {string} [oFormatOptions.groupingSeparator] defines the character used as grouping separator.
605
849
  * Note: <code>groupingSeparator</code> must always be different from <code>decimalSeparator</code>.
606
850
  * @param {int} [oFormatOptions.groupingSize=3] defines the grouping size in digits; the default
607
- * is <code>3</code>. It must be a positive number.
851
+ * is <code>3</code>.
852
+ * <b>Note:</b> If this format option is set to a non-positive value, grouping will be disabled entirely.
608
853
  * @param {int} [oFormatOptions.maxFractionDigits=99] defines the maximum number of decimal digits
609
854
  * @param {int} [oFormatOptions.maxIntegerDigits=99] defines the maximum number of non-decimal digits.
610
855
  * If the number exceeds this maximum, e.g. 1e+120, "?" characters are shown instead of digits.
611
- * @param {int} [oFormatOptions.minFractionDigits=0] defines the minimal number of decimal digits
856
+ * @param {int} [oFormatOptions.minFractionDigits=0] Deprecated as of 1.130; this format option does not have
857
+ * an effect on currency formats since decimals can always be determined, either through the given format options,
858
+ * custom currencies or the CLDR
612
859
  * @param {int} [oFormatOptions.minIntegerDigits=1] defines the minimal number of non-decimal digits
613
860
  * @param {string} [oFormatOptions.minusSign] defines the used minus symbol
614
861
  * @param {boolean} [oFormatOptions.parseAsString=false] since 1.28.2 defines whether to output
@@ -622,12 +869,14 @@ NumberFormat.getIntegerInstance = function (oFormatOptions, oLocale) {
622
869
  * <code>maxFractionDigits</code> format option allows.
623
870
  * If decimals are not preserved, the formatted number is rounded to <code>maxFractionDigits</code>.
624
871
  * @param {sap.ui.core.format.NumberFormat.RoundingMode} [oFormatOptions.roundingMode=HALF_AWAY_FROM_ZERO]
625
- * specifies the rounding behavior for discarding the digits after the maximum fraction digits
626
- * defined by maxFractionDigits. Rounding will only be applied if the passed value is of type <code>number</code>.
872
+ * Specifies the rounding behavior for discarding the digits after the maximum fraction digits
873
+ * defined by <code>maxFractionDigits</code>.
627
874
  * This can be assigned
628
875
  * <ul>
629
876
  * <li>by value in {@link sap.ui.core.format.NumberFormat.RoundingMode RoundingMode},</li>
630
- * <li>via a function that is used for rounding the number and takes two parameters: the number itself, and the number of decimal digits that should be reserved.</li>
877
+ * <li>via a function that is used for rounding the number and takes two parameters: the number itself, and the
878
+ * number of decimal digits that should be reserved. <b>Using a function is deprecated since 1.121.0</b>;
879
+ * string based numbers are not rounded via this custom function.</li>
631
880
  * </ul>
632
881
  * @param {int} [oFormatOptions.shortDecimals] defines the number of decimal in the shortened format string. If this isn't specified, the 'decimals' options is used
633
882
  * @param {int} [oFormatOptions.shortLimit] only use short number formatting for values above this limit
@@ -647,54 +896,48 @@ NumberFormat.getIntegerInstance = function (oFormatOptions, oLocale) {
647
896
  * @param {boolean} [oFormatOptions.strictGroupingValidation=false] whether the positions of grouping separators are validated. Space characters used as grouping separators are not validated.
648
897
  * @param {string} [oFormatOptions.style=standard] defines the style of format. Valid values are
649
898
  * 'short, 'long' or 'standard' (based on the CLDR decimalFormat). When set to 'short' or 'long',
650
- * numbers are formatted into compact forms. When this option is set, the default value of the
899
+ * numbers are formatted into the 'short' form only. When this option is set, the default value of the
651
900
  * 'precision' option is set to 2. This can be changed by setting either min/maxFractionDigits,
652
901
  * decimals, shortDecimals, or the 'precision' option itself.
653
902
  * @param {boolean} [oFormatOptions.trailingCurrencyCode] overrides the global configuration
654
- * value {@link sap.ui.core.Configuration.FormatSettings#getTrailingCurrencyCode}, which has a
655
- * default value of <code>true</>.
903
+ * value {@link module:sap/base/i18n/Formatting.getTrailingCurrencyCode Formatting.getTrailingCurrencyCode},
904
+ * which has a default value of <code>true</>.
656
905
  * This is ignored if <code>oFormatOptions.currencyCode</code> is set to <code>false</code>,
657
906
  * or if <code>oFormatOptions.pattern</code> is supplied.
658
- * @param {sap.ui.core.Locale} [oLocale] Locale to get the formatter for
907
+ * @param {sap.ui.core.Locale} [oLocale]
908
+ * The locale to get the formatter for; if no locale is given, a locale for the currently configured language is
909
+ * used; see {@link module:sap/base/i18n/Formatting.getLanguageTag Formatting.getLanguageTag}
659
910
  * @return {sap.ui.core.format.NumberFormat} currency instance of the NumberFormat
911
+ * @throws {Error} If the <code>oFormatOptions.decimalPadding</code> is set but is not allowed
660
912
  * @static
661
913
  * @public
662
914
  */
663
915
  NumberFormat.getCurrencyInstance = function (oFormatOptions, oLocale) {
664
- var oFormat = this.createInstance(oFormatOptions, oLocale);
665
- var sContext = oFormat.oOriginalFormatOptions && oFormat.oOriginalFormatOptions.currencyContext;
666
-
667
- // currency code trailing
668
- var bShowTrailingCurrencyCode = showTrailingCurrencyCode(oFormat.oOriginalFormatOptions);
916
+ NumberFormat.checkDecimalPadding(oFormatOptions, true, true);
917
+ const oFormat = NumberFormat.createInstance(oFormatOptions, oLocale);
918
+ const oLocaleFormatOptions = oFormat.getLocaleFormatOptions(mNumberType.CURRENCY);
919
+ oFormat.oFormatOptions = extend({}, NumberFormat.oDefaultCurrencyFormat, oLocaleFormatOptions, oFormat.oOriginalFormatOptions);
669
920
 
670
- // prepend "sap-" to pattern params to load (context and short)
671
- if (bShowTrailingCurrencyCode) {
672
- sContext = sContext || this.oDefaultCurrencyFormat.style;
673
- sContext = "sap-" + sContext;
674
- }
675
- var oLocaleFormatOptions = this.getLocaleFormatOptions(oFormat.oLocaleData, mNumberType.CURRENCY, sContext);
676
- oFormat.oFormatOptions = extend({}, this.oDefaultCurrencyFormat, oLocaleFormatOptions, oFormat.oOriginalFormatOptions);
677
-
678
- // Trailing currency code option
679
- //
680
- // The format option "trailingCurrencyCode" is influenced by other options, such as pattern, currencyCode, global config
921
+ // The format option "trailingCurrencyCode" is influenced by other options, such as pattern, currencyCode,
922
+ // global config
681
923
  // Therefore set it manually without modifying the original oFormatOptions.
682
924
  // E.g. the "pattern" option would overwrite this option, even if the "trailingCurrencyCode" option is set
683
925
  // oFormatOptions.pattern = "###"
684
926
  // oFormatOptions.trailingCurrencyCode = true
685
927
  // ->
686
928
  // oFormatOptions.trailingCurrencyCode = false
687
- oFormat.oFormatOptions.trailingCurrencyCode = bShowTrailingCurrencyCode;
929
+ oFormat.oFormatOptions.trailingCurrencyCode = oFormat.showTrailingCurrencyCode();
688
930
  oFormat._defineCustomCurrencySymbols();
931
+ if (oFormat.oFormatOptions.style === "long") {
932
+ oFormat.oFormatOptions.style = "short";
933
+ }
934
+ oFormat.checkGroupingFormatOptions();
689
935
  return oFormat;
690
936
  };
691
937
 
692
938
  /**
693
939
  * Get a unit instance of the NumberFormat, which can be used for formatting units.
694
940
  *
695
- * If no locale is given, the currently configured
696
- * {@link sap.ui.core.Configuration.FormatSettings#getFormatLocale formatLocale} will be used.
697
- *
698
941
  * <p>
699
942
  * This instance has HALF_AWAY_FROM_ZERO set as default rounding mode.
700
943
  * Please set the roundingMode property in oFormatOptions to change the
@@ -713,6 +956,17 @@ NumberFormat.getCurrencyInstance = function (oFormatOptions, oLocale) {
713
956
  * "decimals": 2,
714
957
  * "precision": 4
715
958
  * }}
959
+ * @param {int} [oFormatOptions.decimalPadding]
960
+ * The target length of places after the decimal separator; if the number has fewer decimal places than given in
961
+ * this option, it is padded with whitespaces at the end up to the target length. An additional whitespace
962
+ * character for the decimal separator is added for a number without any decimals.
963
+ * <b>Note:</b> This format option is only allowed if the following conditions apply:
964
+ * <ul>
965
+ * <li>It has a value greater than 0.</li>
966
+ * <li>The <code>FormatOptions.showMeasure</code> format option is set to <code>false</code>.</li>
967
+ * <li>The <code>oFormatOptions.style</code> format option is <b>not</b> set to <code>"short"</code> or
968
+ * <code>"long"</code>.</li>
969
+ * </ul>
716
970
  * @param {int} [oFormatOptions.decimals] defines the number of decimal digits
717
971
  * @param {string} [oFormatOptions.decimalSeparator] defines the character used as decimal separator.
718
972
  * Note: <code>decimalSeparator</code> must always be different from <code>groupingSeparator</code>.
@@ -725,11 +979,14 @@ NumberFormat.getCurrencyInstance = function (oFormatOptions, oLocale) {
725
979
  * @param {int} [oFormatOptions.groupingBaseSize=3] defines the grouping base size in digits if
726
980
  * it is different from the grouping size (e.g. Indian grouping)
727
981
  * @param {boolean} [oFormatOptions.groupingEnabled=true] defines whether grouping is enabled
728
- * (grouping separators are shown)
982
+ * (grouping separators are shown).
983
+ * <b>Note:</b> Grouping is disabled if the <code>groupingSize</code> format option is set to
984
+ * a non-positive value.
729
985
  * @param {string} [oFormatOptions.groupingSeparator] defines the character used as grouping separator.
730
986
  * Note: <code>groupingSeparator</code> must always be different from <code>decimalSeparator</code>.
731
987
  * @param {int} [oFormatOptions.groupingSize=3] defines the grouping size in digits; the default
732
- * is <code>3</code>. It must be a positive number.
988
+ * is <code>3</code>.
989
+ * <b>Note:</b> If this format option is set to a non-positive value, grouping will be disabled entirely.
733
990
  * @param {int} [oFormatOptions.maxFractionDigits=99] defines the maximum number of decimal digits
734
991
  * @param {int} [oFormatOptions.maxIntegerDigits=99] defines the maximum number of non-decimal digits.
735
992
  * If the number exceeds this maximum, e.g. 1e+120, "?" characters are shown instead of digits.
@@ -742,19 +999,26 @@ NumberFormat.getCurrencyInstance = function (oFormatOptions, oLocale) {
742
999
  * to "0.005".
743
1000
  * @param {string} [oFormatOptions.pattern] CLDR number pattern which is used to format the number
744
1001
  * @param {string} [oFormatOptions.plusSign] defines the used plus symbol
745
- * @param {int} [oFormatOptions.precision] defines the numerical precision; the number of decimals
746
- * is calculated dependent on the integer digits
1002
+ * @param {int} [oFormatOptions.precision] The maximum number of digits in the formatted representation of a number;
1003
+ * if the <code>precision</code> is less than the overall length of the number, its fractional part is truncated
1004
+ * through rounding. As the <code>precision</code> only affects the rounding of a number, its integer part can
1005
+ * retain more digits than defined by this parameter.
1006
+ * <b>Example:</b> With a <code>precision</code> of 2, the parameters <code>"234.567", "mass-kilogram"</code> are
1007
+ * formatted to <code>"235 kg"</code>.
1008
+ * <b>Note:</b> The formatted output may differ depending on locale.
747
1009
  * @param {boolean} [oFormatOptions.preserveDecimals=false] Whether {@link #format} preserves
748
1010
  * decimal digits except trailing zeros in case there are more decimals than the
749
1011
  * <code>maxFractionDigits</code> format option allows.
750
1012
  * If decimals are not preserved, the formatted number is rounded to <code>maxFractionDigits</code>.
751
1013
  * @param {sap.ui.core.format.NumberFormat.RoundingMode} [oFormatOptions.roundingMode=HALF_AWAY_FROM_ZERO]
752
- * specifies the rounding behavior for discarding the digits after the maximum fraction digits
753
- * defined by maxFractionDigits. Rounding will only be applied if the passed value is of type <code>number</code>.
1014
+ * Specifies the rounding behavior for discarding the digits after the maximum fraction digits
1015
+ * defined by <code>maxFractionDigits</code>.
754
1016
  * This can be assigned
755
1017
  * <ul>
756
1018
  * <li>by value in {@link sap.ui.core.format.NumberFormat.RoundingMode RoundingMode},</li>
757
- * <li>via a function that is used for rounding the number and takes two parameters: the number itself, and the number of decimal digits that should be reserved.</li>
1019
+ * <li>via a function that is used for rounding the number and takes two parameters: the number itself, and the
1020
+ * number of decimal digits that should be reserved. <b>Using a function is deprecated since 1.121.0</b>;
1021
+ * string based numbers are not rounded via this custom function.</li>
758
1022
  * </ul>
759
1023
  * @param {int} [oFormatOptions.shortDecimals] defines the number of decimals in the shortened
760
1024
  * format string. If this option isn't specified, the 'decimals' option is used instead.
@@ -783,24 +1047,26 @@ NumberFormat.getCurrencyInstance = function (oFormatOptions, oLocale) {
783
1047
  * numbers are formatted into compact forms. When this option is set, the default value of the
784
1048
  * 'precision' option is set to 2. This can be changed by setting either min/maxFractionDigits,
785
1049
  * decimals, shortDecimals, or the 'precision' option itself.
786
- * @param {sap.ui.core.Locale} [oLocale] Locale to get the formatter for
1050
+ * @param {sap.ui.core.Locale} [oLocale]
1051
+ * The locale to get the formatter for; if no locale is given, a locale for the currently configured language is
1052
+ * used; see {@link module:sap/base/i18n/Formatting.getLanguageTag Formatting.getLanguageTag}
787
1053
  * @return {sap.ui.core.format.NumberFormat} unit instance of the NumberFormat
1054
+ * @throws {Error} If the <code>oFormatOptions.decimalPadding</code> is set but is not allowed
788
1055
  * @static
789
1056
  * @public
790
1057
  */
791
1058
  NumberFormat.getUnitInstance = function (oFormatOptions, oLocale) {
792
- var oFormat = this.createInstance(oFormatOptions, oLocale),
793
- oLocaleFormatOptions = this.getLocaleFormatOptions(oFormat.oLocaleData, mNumberType.UNIT);
794
- oFormat.oFormatOptions = extend({}, this.oDefaultUnitFormat, oLocaleFormatOptions, oFormat.oOriginalFormatOptions);
1059
+ NumberFormat.checkDecimalPadding(oFormatOptions, true, true);
1060
+ const oFormat = NumberFormat.createInstance(oFormatOptions, oLocale);
1061
+ const oLocaleFormatOptions = oFormat.getLocaleFormatOptions(mNumberType.UNIT);
1062
+ oFormat.oFormatOptions = extend({}, NumberFormat.oDefaultUnitFormat, oLocaleFormatOptions, oFormat.oOriginalFormatOptions);
1063
+ oFormat.checkGroupingFormatOptions();
795
1064
  return oFormat;
796
1065
  };
797
1066
 
798
1067
  /**
799
1068
  * Get a percent instance of the NumberFormat, which can be used for formatting.
800
1069
  *
801
- * If no locale is given, the currently configured
802
- * {@link sap.ui.core.Configuration.FormatSettings#getFormatLocale formatLocale} will be used.
803
- *
804
1070
  * <p>
805
1071
  * This instance has HALF_AWAY_FROM_ZERO set as default rounding mode.
806
1072
  * Please set the roundingMode property in oFormatOptions to change the
@@ -809,6 +1075,7 @@ NumberFormat.getUnitInstance = function (oFormatOptions, oLocale) {
809
1075
  *
810
1076
  * @param {object} [oFormatOptions] The option object, which supports the following parameters.
811
1077
  * If no options are given, default values according to the type and locale settings are used.
1078
+ * @param {int} [oFormatOptions.decimalPadding] Not supported.
812
1079
  * @param {int} [oFormatOptions.decimals] defines the number of decimal digits
813
1080
  * @param {string} [oFormatOptions.decimalSeparator] defines the character used as decimal separator.
814
1081
  * Note: <code>decimalSeparator</code> must always be different from <code>groupingSeparator</code>.
@@ -821,11 +1088,14 @@ NumberFormat.getUnitInstance = function (oFormatOptions, oLocale) {
821
1088
  * @param {int} [oFormatOptions.groupingBaseSize=3] defines the grouping base size in digits if
822
1089
  * it is different from the grouping size (e.g. Indian grouping)
823
1090
  * @param {boolean} [oFormatOptions.groupingEnabled=true] defines whether grouping is enabled
824
- * (grouping separators are shown)
1091
+ * (grouping separators are shown).
1092
+ * <b>Note:</b> Grouping is disabled if the <code>groupingSize</code> format option is set to
1093
+ * a non-positive value.
825
1094
  * @param {string} [oFormatOptions.groupingSeparator] defines the character used as grouping separator.
826
1095
  * Note: <code>groupingSeparator</code> must always be different from <code>decimalSeparator</code>.
827
1096
  * @param {int} [oFormatOptions.groupingSize=3] defines the grouping size in digits; the default
828
- * is <code>3</code>. It must be a positive number.
1097
+ * is <code>3</code>.
1098
+ * <b>Note:</b> If this format option is set to a non-positive value, grouping will be disabled entirely.
829
1099
  * @param {int} [oFormatOptions.maxFractionDigits=99] defines the maximum number of decimal digits
830
1100
  * @param {int} [oFormatOptions.maxIntegerDigits=99] defines the maximum number of non-decimal digits.
831
1101
  * If the number exceeds this maximum, e.g. 1e+120, "?" characters are shown instead of digits.
@@ -839,19 +1109,26 @@ NumberFormat.getUnitInstance = function (oFormatOptions, oLocale) {
839
1109
  * @param {string} [oFormatOptions.pattern] CLDR number pattern which is used to format the number
840
1110
  * @param {string} [oFormatOptions.percentSign] defines the used percent symbol
841
1111
  * @param {string} [oFormatOptions.plusSign] defines the used plus symbol
842
- * @param {int} [oFormatOptions.precision] defines the numerical precision; the number of decimals
843
- * is calculated dependent on the integer digits
1112
+ * @param {int} [oFormatOptions.precision] The maximum number of digits in the formatted representation of a number;
1113
+ * if the <code>precision</code> is less than the overall length of the number, its fractional part is truncated
1114
+ * through rounding. As the <code>precision</code> only affects the rounding of a number, its integer part
1115
+ * can retain more digits than defined by this parameter.
1116
+ * <b>Example:</b> With a <code>precision</code> of 2, <code>234.567</code> is formatted to
1117
+ * <code>"23,457%"</code>.
1118
+ * <b>Note:</b> The formatted output may differ depending on locale.
844
1119
  * @param {boolean} [oFormatOptions.preserveDecimals=false] Whether {@link #format} preserves
845
1120
  * decimal digits except trailing zeros in case there are more decimals than the
846
1121
  * <code>maxFractionDigits</code> format option allows.
847
1122
  * If decimals are not preserved, the formatted number is rounded to <code>maxFractionDigits</code>.
848
1123
  * @param {sap.ui.core.format.NumberFormat.RoundingMode} [oFormatOptions.roundingMode=HALF_AWAY_FROM_ZERO]
849
- * specifies the rounding behavior for discarding the digits after the maximum fraction digits
850
- * defined by maxFractionDigits. Rounding will only be applied if the passed value is of type <code>number</code>.
1124
+ * Specifies the rounding behavior for discarding the digits after the maximum fraction digits
1125
+ * defined by <code>maxFractionDigits</code>.
851
1126
  * This can be assigned
852
1127
  * <ul>
853
1128
  * <li>by value in {@link sap.ui.core.format.NumberFormat.RoundingMode RoundingMode},</li>
854
- * <li>via a function that is used for rounding the number and takes two parameters: the number itself, and the number of decimal digits that should be reserved.</li>
1129
+ * <li>via a function that is used for rounding the number and takes two parameters: the number itself, and the
1130
+ * number of decimal digits that should be reserved. <b>Using a function is deprecated since 1.121.0</b>;
1131
+ * string based numbers are not rounded via this custom function.</li>
855
1132
  * </ul>
856
1133
  * @param {int} [oFormatOptions.shortDecimals] defines the number of decimal in the shortened format string. If this isn't specified, the 'decimals' options is used
857
1134
  * @param {int} [oFormatOptions.shortLimit] only use short number formatting for values above this limit
@@ -865,15 +1142,20 @@ NumberFormat.getUnitInstance = function (oFormatOptions, oLocale) {
865
1142
  * numbers are formatted into compact forms. When this option is set, the default value of the
866
1143
  * 'precision' option is set to 2. This can be changed by setting either min/maxFractionDigits,
867
1144
  * decimals, shortDecimals, or the 'precision' option itself.
868
- * @param {sap.ui.core.Locale} [oLocale] Locale to get the formatter for
1145
+ * @param {sap.ui.core.Locale} [oLocale]
1146
+ * The locale to get the formatter for; if no locale is given, a locale for the currently configured language is
1147
+ * used; see {@link module:sap/base/i18n/Formatting.getLanguageTag Formatting.getLanguageTag}
869
1148
  * @return {sap.ui.core.format.NumberFormat} percentage instance of the NumberFormat
1149
+ * @throws {Error} If the <code>oFormatOptions.decimalPadding</code> format option is provided
870
1150
  * @static
871
1151
  * @public
872
1152
  */
873
1153
  NumberFormat.getPercentInstance = function (oFormatOptions, oLocale) {
874
- var oFormat = this.createInstance(oFormatOptions, oLocale),
875
- oLocaleFormatOptions = this.getLocaleFormatOptions(oFormat.oLocaleData, mNumberType.PERCENT);
876
- oFormat.oFormatOptions = extend({}, this.oDefaultPercentFormat, oLocaleFormatOptions, oFormat.oOriginalFormatOptions);
1154
+ NumberFormat.checkDecimalPadding(oFormatOptions, false);
1155
+ const oFormat = NumberFormat.createInstance(oFormatOptions, oLocale);
1156
+ const oLocaleFormatOptions = oFormat.getLocaleFormatOptions(mNumberType.PERCENT);
1157
+ oFormat.oFormatOptions = extend({}, NumberFormat.oDefaultPercentFormat, oLocaleFormatOptions, oFormat.oOriginalFormatOptions);
1158
+ oFormat.checkGroupingFormatOptions();
877
1159
  return oFormat;
878
1160
  };
879
1161
 
@@ -893,7 +1175,7 @@ NumberFormat.createInstance = function (oFormatOptions, oLocale) {
893
1175
  oFormatOptions = undefined;
894
1176
  }
895
1177
  if (!oLocale) {
896
- oLocale = Configuration.getFormatSettings().getFormatLocale();
1178
+ oLocale = new Locale(Formatting.getLanguageTag());
897
1179
  }
898
1180
  oFormat.oLocale = oLocale;
899
1181
  oFormat.oLocaleData = LocaleData.getInstance(oLocale);
@@ -939,30 +1221,38 @@ NumberFormat.createInstance = function (oFormatOptions, oLocale) {
939
1221
  * @ui5-restricted sap.ui.model.odata.type
940
1222
  */
941
1223
  NumberFormat.getDefaultUnitPattern = function (sShortName) {
942
- return "{0} " + sShortName;
1224
+ return "{0}\u00a0" + sShortName;
943
1225
  };
944
1226
 
945
1227
  /**
946
- * Get locale dependent default format options.
1228
+ * Gets the default locale-dependent format options for the given number format type.
947
1229
  *
948
- * @static
1230
+ * @param {"integer"|"float"|"currency"|"unit"|"percent"} sType
1231
+ * The number format type
1232
+ * @returns {object} The default locale-dependent format options for the given number format type
1233
+ *
1234
+ * @private
949
1235
  */
950
- NumberFormat.getLocaleFormatOptions = function (oLocaleData, iType, sContext) {
951
- var oLocaleFormatOptions, sNumberPattern;
952
- switch (iType) {
1236
+ NumberFormat.prototype.getLocaleFormatOptions = function (sType) {
1237
+ const oLocaleData = this.oLocaleData;
1238
+ let sNumberPattern;
1239
+ let sContext;
1240
+ switch (sType) {
953
1241
  case mNumberType.PERCENT:
954
1242
  sNumberPattern = oLocaleData.getPercentPattern();
955
1243
  break;
956
1244
  case mNumberType.CURRENCY:
1245
+ sContext = this.oOriginalFormatOptions?.currencyContext || NumberFormat.oDefaultCurrencyFormat.style;
1246
+ // prepend "sap-" to pattern params to load (context and short)
1247
+ if (this.showTrailingCurrencyCode()) {
1248
+ sContext = "sap-" + sContext;
1249
+ }
957
1250
  sNumberPattern = oLocaleData.getCurrencyPattern(sContext);
958
1251
  break;
959
- case mNumberType.UNIT:
960
- sNumberPattern = oLocaleData.getDecimalPattern();
961
- break;
962
1252
  default:
963
1253
  sNumberPattern = oLocaleData.getDecimalPattern();
964
1254
  }
965
- oLocaleFormatOptions = this.parseNumberPattern(sNumberPattern);
1255
+ const oLocaleFormatOptions = NumberFormat.parseNumberPattern(sNumberPattern);
966
1256
  oLocaleFormatOptions.plusSign = oLocaleData.getNumberSymbol("plusSign");
967
1257
  oLocaleFormatOptions.minusSign = oLocaleData.getNumberSymbol("minusSign");
968
1258
  oLocaleFormatOptions.decimalSeparator = oLocaleData.getNumberSymbol("decimal");
@@ -972,14 +1262,7 @@ NumberFormat.getLocaleFormatOptions = function (oLocaleData, iType, sContext) {
972
1262
 
973
1263
  // Some options need to be overridden to stay compatible with the formatting defaults
974
1264
  // before pattern parsing was added to the NumberFormat
975
- switch (iType) {
976
- case mNumberType.UNIT:
977
- case mNumberType.FLOAT:
978
- case mNumberType.PERCENT:
979
- // Unlimited fraction digits for float and percent values
980
- oLocaleFormatOptions.minFractionDigits = 0;
981
- oLocaleFormatOptions.maxFractionDigits = 99;
982
- break;
1265
+ switch (sType) {
983
1266
  case mNumberType.INTEGER:
984
1267
  // No fraction digits and no grouping for integer values
985
1268
  oLocaleFormatOptions.minFractionDigits = 0;
@@ -991,6 +1274,11 @@ NumberFormat.getLocaleFormatOptions = function (oLocaleData, iType, sContext) {
991
1274
  oLocaleFormatOptions.minFractionDigits = undefined;
992
1275
  oLocaleFormatOptions.maxFractionDigits = undefined;
993
1276
  break;
1277
+ default:
1278
+ // cases: mNumberType.UNIT, mNumberType.FLOAT and mNumberType.PERCENT
1279
+ // Unlimited fraction digits
1280
+ oLocaleFormatOptions.minFractionDigits = 0;
1281
+ oLocaleFormatOptions.maxFractionDigits = 99;
994
1282
  }
995
1283
  return oLocaleFormatOptions;
996
1284
  };
@@ -1143,7 +1431,7 @@ NumberFormat.prototype._defineCustomCurrencySymbols = function () {
1143
1431
  * @returns {string} the number with stripped trailing zero decimals, e.g. "1.230"
1144
1432
  */
1145
1433
  function stripTrailingZeroDecimals(sNumber, minDecimalsPreserved) {
1146
- if (sNumber.indexOf(".") >= 0 && !isScientificNotation(sNumber) && sNumber.endsWith("0")) {
1434
+ if (sNumber.indexOf(".") >= 0 && sNumber.endsWith("0")) {
1147
1435
  var iFractionDigitsLength = sNumber.length - sNumber.lastIndexOf(".") - 1;
1148
1436
  var iFractionsToRemove = iFractionDigitsLength - minDecimalsPreserved;
1149
1437
  if (iFractionsToRemove > 0) {
@@ -1193,9 +1481,13 @@ function applyGrouping(sIntegerPart, oOptions) {
1193
1481
  /**
1194
1482
  * Format a number according to the given format options.
1195
1483
  *
1196
- * @param {number|array} vValue the number to format or an array which contains the number to format and the sMeasure parameter
1197
- * @param {string} [sMeasure] an optional unit which has an impact on formatting currencies and units
1198
- * @return {string} the formatted output value
1484
+ * @param {number|string|array} vValue
1485
+ * The number to format as a number or a string, such as <code>1234.45</code> or <code>"-1234.45"</code>, or an
1486
+ * array which contains both the number to format as a number or a string and the <code>sMeasure</code> parameter
1487
+ * @param {string} [sMeasure]
1488
+ * An optional unit which has an impact on formatting currencies and units
1489
+ * @returns {string}
1490
+ * The formatted value
1199
1491
  * @public
1200
1492
  */
1201
1493
  NumberFormat.prototype.format = function (vValue, sMeasure) {
@@ -1214,18 +1506,12 @@ NumberFormat.prototype.format = function (vValue, sMeasure) {
1214
1506
  oOptions = Object.assign({}, this.oFormatOptions),
1215
1507
  oOrigOptions = this.oOriginalFormatOptions,
1216
1508
  bIndianCurrency = oOptions.type === mNumberType.CURRENCY && sMeasure === "INR" && this.oLocale.getLanguage() === "en" && this.oLocale.getRegion() === "IN",
1217
- aPatternParts,
1218
1509
  oShortFormat,
1219
1510
  nShortRefNumber,
1220
1511
  sPluralCategory,
1221
1512
  mUnitPatterns,
1222
1513
  sLookupMeasure,
1223
1514
  bValueIsNullOrUndefined = vValue === undefined || vValue === null;
1224
- if (oOptions.groupingEnabled && oOptions.groupingSize <= 0) {
1225
- // invalid grouping size specified
1226
- Log.error("Grouping requires the 'groupingSize' format option to be a positive number, but it is '" + oOptions.groupingSize + "' instead.");
1227
- return "";
1228
- }
1229
1515
 
1230
1516
  // emptyString is only relevant for the number part (vValue)
1231
1517
  if (oOptions.showNumber && (vValue === oOptions.emptyString || isNaN(vValue) && isNaN(oOptions.emptyString))) {
@@ -1277,13 +1563,16 @@ NumberFormat.prototype.format = function (vValue, sMeasure) {
1277
1563
  if (!mUnitPatterns && !oOptions.showNumber) {
1278
1564
  return this._addOriginInfo(sMeasure);
1279
1565
  }
1280
-
1566
+ }
1567
+ if (oOptions.type === mNumberType.UNIT) {
1281
1568
  // either take the decimals/precision on the custom units or fallback to the given format-options
1282
1569
  oOptions.decimals = mUnitPatterns && typeof mUnitPatterns.decimals === "number" && mUnitPatterns.decimals >= 0 ? mUnitPatterns.decimals : oOptions.decimals;
1283
- oOptions.decimals = NumberFormat.getMaximalDecimals(oOptions);
1570
+ oOptions.decimals = NumberFormat.getMaximumDecimals(oOptions);
1284
1571
  oOptions.precision = mUnitPatterns && typeof mUnitPatterns.precision === "number" && mUnitPatterns.precision >= 0 ? mUnitPatterns.precision : oOptions.precision;
1285
1572
  }
1286
- if (oOptions.type == mNumberType.CURRENCY) {
1573
+ let sCurrencySymbolOrCode;
1574
+ if (oOptions.type === mNumberType.CURRENCY) {
1575
+ sCurrencySymbolOrCode = this.getCurrencySymbolOrCode(sMeasure, oOptions.currencyCode);
1287
1576
  // Make sure the "trailingCurrencyCode" mode is only used on currency codes:
1288
1577
  // The "customCurrencies" format option takes precedence over CLDR and global configuration. If the given measure isn't found
1289
1578
  // there, we already return an empty string in the check above (look for error log 'Currency "xy" is unknown').
@@ -1292,34 +1581,23 @@ NumberFormat.prototype.format = function (vValue, sMeasure) {
1292
1581
  // it shouldn't be formatted with the "trailingCurrencyCode" pattern.
1293
1582
  if (sMeasure && oOptions.trailingCurrencyCode) {
1294
1583
  if (!this.mKnownCurrencyCodes[sMeasure] && !/(^[A-Z]{3}$)/.test(sMeasure)) {
1295
- oOptions.trailingCurrencyCode = false;
1296
1584
  // Revert to non-"sap-" prefixed (trailing-currency-code) pattern. Also see code in getCurrencyInstance()
1297
- oOptions.pattern = this.oLocaleData.getCurrencyPattern(oOptions.currencyContext);
1585
+ oOptions.trailingCurrencyCode = false;
1298
1586
  }
1299
1587
  }
1300
1588
  if (!oOptions.showNumber) {
1301
1589
  // if the number should not be shown, return the sMeasure part standalone, without anything number specific
1302
- if (!oOptions.currencyCode) {
1303
- var sSymbol;
1304
- // custom currencies provided
1305
- if (oOptions.customCurrencies && typeof oOptions.customCurrencies === "object") {
1306
- // the custom currency symbol map was preprocessed on instance creation
1307
- sSymbol = this.mKnownCurrencySymbols[sMeasure];
1308
- } else {
1309
- sSymbol = this.oLocaleData.getCurrencySymbol(sMeasure);
1310
- }
1311
- if (sSymbol && sSymbol !== sMeasure) {
1312
- sMeasure = sSymbol;
1313
- }
1314
- }
1315
- return sMeasure;
1590
+ return sCurrencySymbolOrCode;
1316
1591
  }
1317
- // if decimals are given on a custom currency, they have precedence over the decimals defined on the format options
1318
- if (oOptions.customCurrencies && oOptions.customCurrencies[sMeasure]) {
1319
- // we either take the custom decimals or use decimals defined in the format-options
1320
- // we check for undefined here, since 0 is an accepted value
1321
- oOptions.decimals = oOptions.customCurrencies[sMeasure].decimals !== undefined ? oOptions.customCurrencies[sMeasure].decimals : oOptions.decimals;
1322
- oOptions.decimals = NumberFormat.getMaximalDecimals(oOptions);
1592
+ if (oOptions.style === "long" || oOptions.style === "short") {
1593
+ oOptions.maxFractionDigits ??= 0;
1594
+ } else {
1595
+ if (oOptions.customCurrencies?.[sMeasure]?.decimals !== undefined) {
1596
+ oOptions.decimals = oOptions.customCurrencies[sMeasure].decimals;
1597
+ } else {
1598
+ oOptions.decimals ??= this.oLocaleData.getCurrencyDigits(sMeasure);
1599
+ }
1600
+ oOptions.decimals = NumberFormat.getMaximumDecimals(oOptions);
1323
1601
  }
1324
1602
  }
1325
1603
 
@@ -1330,7 +1608,7 @@ NumberFormat.prototype.format = function (vValue, sMeasure) {
1330
1608
  }
1331
1609
  if (oOptions.shortLimit === undefined || Math.abs(vValue) >= oOptions.shortLimit) {
1332
1610
  nShortRefNumber = oOptions.shortRefNumber === undefined ? vValue : oOptions.shortRefNumber;
1333
- oShortFormat = getShortenedFormat(nShortRefNumber, oOptions, this.oLocaleData, bIndianCurrency);
1611
+ oShortFormat = this.getShortenedFormat(nShortRefNumber, oOptions, bIndianCurrency);
1334
1612
  if (oShortFormat && oShortFormat.formatString != "0") {
1335
1613
  vValue = vValue / oShortFormat.magnitude;
1336
1614
  // If shortDecimals is defined, override the fractionDigits
@@ -1373,28 +1651,12 @@ NumberFormat.prototype.format = function (vValue, sMeasure) {
1373
1651
  vValue = NumberFormat._shiftDecimalPoint(vValue, 2);
1374
1652
  }
1375
1653
 
1376
- //handle measure
1377
- if (oOptions.type == mNumberType.CURRENCY) {
1378
- var iDigits = this.oLocaleData.getCurrencyDigits(sMeasure);
1379
-
1380
- // decimals might be undefined, yet 0 is accepted of course
1381
- if (oOptions.customCurrencies && oOptions.customCurrencies[sMeasure] && oOptions.customCurrencies[sMeasure].decimals !== undefined) {
1382
- iDigits = oOptions.customCurrencies[sMeasure].decimals;
1383
- }
1384
- if (oOptions.maxFractionDigits === undefined) {
1385
- oOptions.maxFractionDigits = iDigits;
1386
- }
1387
- if (oOptions.minFractionDigits === undefined) {
1388
- oOptions.minFractionDigits = iDigits;
1389
- }
1390
- }
1391
-
1392
1654
  // Rounding the value with oOptions.maxFractionDigits and oOptions.roundingMode.
1393
1655
  //
1394
1656
  // If the number of fraction digits are equal or less than oOptions.maxFractionDigits, the
1395
1657
  // number isn't changed. After this operation, the number of fraction digits is
1396
1658
  // equal or less than oOptions.maxFractionDigits.
1397
- if (typeof vValue === "number" && !oOptions.preserveDecimals) {
1659
+ if ((typeof vValue === "number" || typeof vValue === "string" && typeof oOptions.roundingMode !== "function") && !oOptions.preserveDecimals) {
1398
1660
  vValue = rounding(vValue, oOptions.maxFractionDigits, oOptions.roundingMode);
1399
1661
  }
1400
1662
 
@@ -1402,6 +1664,12 @@ NumberFormat.prototype.format = function (vValue, sMeasure) {
1402
1664
  if (vValue == 0) {
1403
1665
  bNegative = false;
1404
1666
  }
1667
+ if (!bValueIsNullOrUndefined) {
1668
+ sNumber = LocaleData.convertToDecimal(vValue);
1669
+ }
1670
+ if (sNumber === "NaN") {
1671
+ return sNumber;
1672
+ }
1405
1673
 
1406
1674
  // strip of trailing zeros in decimals
1407
1675
  // "1000.00" -> "1000" (maxFractionDigits: 0)
@@ -1411,14 +1679,8 @@ NumberFormat.prototype.format = function (vValue, sMeasure) {
1411
1679
  // These zeros are cut off until maxFractionDigits is reached to be backward compatible.
1412
1680
  // If more trailing decimal zeros are required the option maxFractionDigits can be increased.
1413
1681
  // Note: default maxFractionDigits for Unit and Float is 99.
1414
- if (oOptions.preserveDecimals && (typeof vValue === "string" || vValue instanceof String)) {
1415
- vValue = stripTrailingZeroDecimals(vValue, oOptions.maxFractionDigits);
1416
- }
1417
- if (!bValueIsNullOrUndefined) {
1418
- sNumber = LocaleData.convertToDecimal(vValue);
1419
- }
1420
- if (sNumber == "NaN") {
1421
- return sNumber;
1682
+ if (oOptions.preserveDecimals) {
1683
+ sNumber = stripTrailingZeroDecimals(sNumber, oOptions.maxFractionDigits);
1422
1684
  }
1423
1685
 
1424
1686
  // if number is negative remove minus
@@ -1485,7 +1747,10 @@ NumberFormat.prototype.format = function (vValue, sMeasure) {
1485
1747
  } else {
1486
1748
  sGroupedIntegerPart = sIntegerPart;
1487
1749
  }
1488
-
1750
+ const iDecimalPadding = oOptions.decimalPadding || 0;
1751
+ if (iDecimalPadding) {
1752
+ oOptions.minusSign = oOptions.minusSign.replace(rLeftToRightMark, "");
1753
+ }
1489
1754
  // combine
1490
1755
  if (bNegative) {
1491
1756
  sResult = oOptions.minusSign;
@@ -1494,81 +1759,29 @@ NumberFormat.prototype.format = function (vValue, sMeasure) {
1494
1759
  if (sFractionPart) {
1495
1760
  sResult += oOptions.decimalSeparator + sFractionPart;
1496
1761
  }
1497
- if (oShortFormat && oShortFormat.formatString && oOptions.showScale && oOptions.type !== mNumberType.CURRENCY) {
1762
+ const bUseCompactPattern = oShortFormat && oShortFormat.formatString && oOptions.showScale;
1763
+ let sCompactPattern;
1764
+ if (bUseCompactPattern) {
1498
1765
  // Get correct format string based on actual decimal/fraction digits
1499
- // the plural category of a compact number is determined for the reduced short number without compact
1500
- // notation, e.g. "1.2M" must check "1.2" (see CLDR "decimalFormat-short" and "decimalFormat-long")
1766
+ // the plural category of a compact number/currency is determined for the reduced short number without
1767
+ // compact notation, e.g. "1.2M" must check "1.2"
1768
+ // (see CLDR "decimalFormat-short" and "decimalFormat-long" or "currencyFormat-short")
1501
1769
  sPluralCategory = this._getPluralCategory(sIntegerPart, sFractionPart);
1502
- oShortFormat.formatString = this.oLocaleData.getDecimalFormat(oOptions.style, oShortFormat.key, sPluralCategory);
1503
- //inject formatted shortValue in the formatString
1504
- sResult = oShortFormat.formatString.replace(oShortFormat.valueSubString, sResult);
1505
- //formatString may contain '.' (quoted to differentiate them decimal separator)
1506
- //which must be replaced with .
1507
- sResult = sResult.replace(/'.'/g, ".");
1770
+ sCompactPattern = this.getCompactPattern(oOptions.type, oOptions.style, oShortFormat.key, sPluralCategory, oOptions.trailingCurrencyCode, bIndianCurrency, sMeasure && oOptions.showMeasure, sCurrencySymbolOrCode, bNegative);
1771
+ if (oOptions.type !== mNumberType.CURRENCY) {
1772
+ // inject formatted shortValue in the formatString
1773
+ sResult = sCompactPattern.replace(oShortFormat.valueSubString, sResult);
1774
+ }
1508
1775
  }
1509
1776
  if (oOptions.type === mNumberType.CURRENCY) {
1510
- sPattern = oOptions.pattern;
1511
- if (oShortFormat && oShortFormat.formatString && oOptions.showScale) {
1512
- var sStyle;
1513
-
1514
- // Currency formatting has only short style (no long)
1515
- if (oOptions.trailingCurrencyCode) {
1516
- sStyle = "sap-short";
1517
- } else {
1518
- sStyle = "short";
1519
- }
1520
-
1521
- // Get correct format string based on actual decimal/fraction digits
1522
- // the plural category of a compact currency is determined for the reduced short number without compact
1523
- // notation, e.g. "1.2M" must check "1.2" (see CLDR "currencyFormat-short")
1524
- sPluralCategory = this._getPluralCategory(sIntegerPart, sFractionPart);
1525
- if (bIndianCurrency) {
1526
- sPattern = getIndianCurrencyFormat(sStyle, oShortFormat.key, sPluralCategory);
1527
- } else {
1528
- sPattern = this.oLocaleData.getCurrencyFormat(sStyle, oShortFormat.key, sPluralCategory);
1529
- }
1530
- //formatString may contain '.' (quoted to differentiate them decimal separator)
1531
- //which must be replaced with .
1532
- sPattern = sPattern.replace(/'.'/g, ".");
1533
- }
1534
-
1535
- // The currency pattern is defined in some locale, for example in "ko", as: ¤#,##0.00;(¤#,##0.00)
1536
- // where the pattern after ';' should be used for negative numbers.
1537
- // Therefore it's needed to check whether the pattern contains ';' and use the later part for
1538
- // negative values
1539
- aPatternParts = sPattern.split(";");
1540
- if (aPatternParts.length === 2) {
1541
- sPattern = bNegative ? aPatternParts[1] : aPatternParts[0];
1542
- if (bNegative) {
1543
- sResult = sResult.substring(oOptions.minusSign.length);
1544
- }
1545
- }
1546
-
1547
- // check if we need to render a symbol instead of a currency-code
1548
- if (!oOptions.currencyCode) {
1549
- var sSymbol;
1550
- // custom currencies provided
1551
- if (oOptions.customCurrencies && typeof oOptions.customCurrencies === "object") {
1552
- // the custom currency symbol map was preprocessed on instance creation
1553
- sSymbol = this.mKnownCurrencySymbols[sMeasure];
1554
- } else {
1555
- sSymbol = this.oLocaleData.getCurrencySymbol(sMeasure);
1556
- }
1557
- if (sSymbol && sSymbol !== sMeasure) {
1558
- sMeasure = sSymbol;
1559
- }
1560
- }
1561
- sResult = this._composeCurrencyResult(sPattern, sResult, sMeasure, {
1562
- showMeasure: oOptions.showMeasure,
1563
- negative: bNegative,
1564
- minusSign: oOptions.minusSign
1565
- });
1777
+ sPattern = bUseCompactPattern ? sCompactPattern : this.getCurrencyPattern(oOptions.currencyContext, oOptions.trailingCurrencyCode, sMeasure && oOptions.showMeasure, sCurrencySymbolOrCode, bNegative);
1778
+ sResult = NumberFormat._composeCurrencyResult(sPattern, sResult, sCurrencySymbolOrCode, oOptions.minusSign, bNegative);
1566
1779
  }
1567
1780
 
1568
1781
  // format percent values:
1569
1782
  if (oOptions.type === mNumberType.PERCENT) {
1570
1783
  sPattern = oOptions.pattern;
1571
- sResult = sPattern.replace(/[0#.,]+/, sResult);
1784
+ sResult = sPattern.replace(rNumberPattern, sResult);
1572
1785
  sResult = sResult.replace(/%/, oOptions.percentSign);
1573
1786
  }
1574
1787
  if (oOptions.showMeasure && sMeasure && oOptions.type === mNumberType.UNIT) {
@@ -1589,9 +1802,46 @@ NumberFormat.prototype.format = function (vValue, sMeasure) {
1589
1802
  }
1590
1803
  sResult = sPattern.replace("{0}", sResult);
1591
1804
  }
1805
+ let iDecimalPaddingLength = iDecimalPadding - sFractionPart.length;
1806
+ if (iDecimalPaddingLength > 0) {
1807
+ const bNegativeAccounting = sResult[sResult.length - 1] === ")";
1808
+ const sCharPunctuationSpace = "\u2008";
1809
+ if (sFractionPart) {
1810
+ if (bNegativeAccounting) {
1811
+ // the ")" and the CHAR_PUNCTUATION_SPACE u2008 have a combined width close to the width of a
1812
+ // CHAR_FIGURE_SPACE u2007
1813
+ sResult += sCharPunctuationSpace;
1814
+ iDecimalPaddingLength = iDecimalPaddingLength - 1;
1815
+ }
1816
+ } else if (!bNegativeAccounting) {
1817
+ // only add CHAR_PUNCTUATION_SPACE u2008 if there is no ")" at the end
1818
+ sResult += sCharPunctuationSpace;
1819
+ }
1820
+ sResult += "\u2007".repeat(iDecimalPaddingLength); // CHAR_FIGURE_SPACE u2007
1821
+ }
1592
1822
  return this._addOriginInfo(sResult);
1593
1823
  };
1594
1824
 
1825
+ /**
1826
+ * Gets the currency symbol or the currency code for the given currency code depending on the given format options.
1827
+ *
1828
+ * @param {string} sCurrencyCode
1829
+ * The currency code
1830
+ * @param {boolean} bCurrencyCode
1831
+ * Whether to show the currency code instead of the currency symbol, see {@link Numberformat.getCurrencyInstance}
1832
+ * @returns {string}
1833
+ * The currency symbol or the currency code
1834
+ *
1835
+ * @private
1836
+ */
1837
+ NumberFormat.prototype.getCurrencySymbolOrCode = function (sCurrencyCode, bCurrencyCode) {
1838
+ if (bCurrencyCode) {
1839
+ return sCurrencyCode;
1840
+ }
1841
+ // the custom currency symbol map was preprocessed on instance creation
1842
+ return (typeof this.oFormatOptions.customCurrencies === "object" ? this.mKnownCurrencySymbols[sCurrencyCode] : this.oLocaleData.getCurrencySymbol(sCurrencyCode)) || sCurrencyCode;
1843
+ };
1844
+
1595
1845
  /**
1596
1846
  * Gets the plural category for the given number information. With a given <code>oShortFormat</code>
1597
1847
  * the category is determined based on the compact notation.
@@ -1629,54 +1879,46 @@ NumberFormat.prototype._addOriginInfo = function (sResult) {
1629
1879
  }
1630
1880
  return sResult;
1631
1881
  };
1632
- NumberFormat.prototype._composeCurrencyResult = function (sPattern, sFormattedNumber, sMeasure, oOptions) {
1633
- var sMinusSign = oOptions.minusSign;
1634
- sPattern = sPattern.replace(/[0#.,]+/, sFormattedNumber);
1635
- if (oOptions.showMeasure && sMeasure) {
1636
- var sPlaceHolder = "\u00a4",
1637
- mRegex = {
1638
- "[:digit:]": rDigit,
1639
- "[[:^S:]&[:^Z:]]": rNotSAndNotZ
1640
- },
1641
- iMeasureStart = sPattern.indexOf(sPlaceHolder),
1642
- // determine whether the number is before the measure or after it by comparing the position of measure placeholder with half of the length of the pattern string
1643
- sPosition = iMeasureStart < sPattern.length / 2 ? "after" : "before",
1644
- oSpacingSetting = this.oLocaleData.getCurrencySpacing(sPosition),
1645
- sCurrencyChar = sPosition === "after" ? sMeasure.charAt(sMeasure.length - 1) : sMeasure.charAt(0),
1646
- sNumberChar,
1647
- rCurrencyChar = mRegex[oSpacingSetting.currencyMatch],
1648
- rNumberChar = mRegex[oSpacingSetting.surroundingMatch],
1649
- iInsertPos;
1650
- sPattern = sPattern.replace(sPlaceHolder, sMeasure);
1651
- sNumberChar = sPosition === "after" ? sPattern.charAt(iMeasureStart + sMeasure.length) : sPattern.charAt(iMeasureStart - 1);
1652
- if (rCurrencyChar && rCurrencyChar.test(sCurrencyChar) && rNumberChar && rNumberChar.test(sNumberChar)) {
1653
- // when both checks are valid, insert the defined space
1654
-
1655
- if (sPosition === "after") {
1656
- iInsertPos = iMeasureStart + sMeasure.length;
1657
- } else {
1658
- iInsertPos = iMeasureStart;
1659
- }
1660
1882
 
1661
- // insert the space char between the measure and the number
1662
- sPattern = sPattern.slice(0, iInsertPos) + oSpacingSetting.insertBetween + sPattern.slice(iInsertPos);
1663
- } else if (oOptions.negative && sPosition === "after") {
1664
- // when no space is inserted between measure and number
1665
- // and when the number is negative and the measure is shown before the number
1666
- // a zero-width non-breakable space ("\ufeff") is inserted before the minus sign
1667
- // in order to prevent the formatted currency number from being wrapped after the
1668
- // minus sign when the space isn't enough for displaying the currency number within
1669
- // one line
1670
- sMinusSign = "\ufeff" + oOptions.minusSign;
1883
+ /**
1884
+ * Replaces the amount, measure, and minus sign parts in the given pattern with the given values and returns the
1885
+ * result.
1886
+ *
1887
+ * @param {string} sPattern
1888
+ * The currency pattern, e.g. "¤#,##0.00;(¤#,##0.00)", "¤#,##0.00;¤-#,##0.00", "#,##0.00", or 000K"
1889
+ * @param {string} sAmount
1890
+ * The formatted amount, e.g. "1,234.56"
1891
+ * @param {string} sMeasure
1892
+ * The currency symbol or code
1893
+ * @param {string} sMinusSign
1894
+ * The locale specific minus sign
1895
+ * @param {boolean} bNegative
1896
+ * Whether the amount is negative
1897
+ * @returns {string}
1898
+ * The resulting string after replacing the amount, measure, and minus sign parts in the given pattern with the
1899
+ * given values
1900
+ *
1901
+ * @private
1902
+ */
1903
+ NumberFormat._composeCurrencyResult = function (sPattern, sAmount, sMeasure, sMinusSign, bNegative) {
1904
+ const aPatternParts = sPattern.split(";");
1905
+ if (aPatternParts.length === 2) {
1906
+ sPattern = aPatternParts[bNegative ? 1 : 0];
1907
+ if (bNegative) {
1908
+ sAmount = sAmount.slice(sMinusSign.length);
1671
1909
  }
1672
- } else {
1673
- // If measure is not shown, also remove whitespace next to the measure symbol
1674
- sPattern = sPattern.replace(/\s*\u00a4\s*/, "");
1675
1910
  }
1676
- if (oOptions.negative) {
1677
- sPattern = sPattern.replace(/-/, sMinusSign);
1911
+ let sResult = sPattern.replace("-", sMinusSign).replace(rNumberPattern, sAmount).replace("\u00a4", sMeasure);
1912
+ if (bNegative) {
1913
+ // when no space is inserted between measure and number
1914
+ // and when the number is negative and the measure is shown before the number
1915
+ // a zero-width non-breakable space ("\ufeff") is inserted before the minus sign
1916
+ // in order to prevent the formatted currency number from being wrapped after the
1917
+ // minus sign when the space isn't enough for displaying the currency number within
1918
+ // one line
1919
+ sResult = sResult.replace(sMeasure + sMinusSign, sMeasure + "\ufeff" + sMinusSign);
1678
1920
  }
1679
- return sPattern;
1921
+ return sResult;
1680
1922
  };
1681
1923
 
1682
1924
  /**
@@ -1701,7 +1943,7 @@ NumberFormat.prototype.parse = function (sValue) {
1701
1943
  sPlusMinusSigns = quote(sPlusSigns + sMinusSigns),
1702
1944
  sGroupingSeparator = quote(oOptions.groupingSeparator),
1703
1945
  sDecimalSeparator = quote(oOptions.decimalSeparator),
1704
- sRegExpFloat = "^\\s*([" + sPlusMinusSigns + "]?(?:[0-9" + sGroupingSeparator + "]+|[0-9" + sGroupingSeparator + "]*" + sDecimalSeparator + "[0-9]*)(?:[eE][+-][0-9]+)?)\\s*$",
1946
+ sRegExpFloat = "^\\s*([" + sPlusMinusSigns + "]?(?:[0-9" + sGroupingSeparator + "]+|[0-9" + sGroupingSeparator + "]*" + sDecimalSeparator + "[0-9]*)(?:[eE][+-]?[0-9]+)?)\\s*$",
1705
1947
  sRegExpInt = "^\\s*([" + sPlusMinusSigns + "]?[0-9" + sGroupingSeparator + "]+)\\s*$",
1706
1948
  oGroupingRegExp = new RegExp(sGroupingSeparator, "g"),
1707
1949
  oDecimalRegExp = new RegExp(sDecimalSeparator, "g"),
@@ -1714,8 +1956,13 @@ NumberFormat.prototype.parse = function (sValue) {
1714
1956
  vResult = 0,
1715
1957
  oShort,
1716
1958
  vEmptyParseValue;
1959
+ if (typeof sValue !== "string" && !(sValue instanceof String)) {
1960
+ return null;
1961
+ }
1962
+ sValue = FormatUtils.normalize(sValue).trim();
1717
1963
  if (sValue === "") {
1718
- if (!oOptions.showNumber) {
1964
+ const bUnitOrCurrency = oOptions.type === mNumberType.CURRENCY || oOptions.type === mNumberType.UNIT;
1965
+ if (!oOptions.showNumber && !bUnitOrCurrency) {
1719
1966
  return null;
1720
1967
  }
1721
1968
  vEmptyParseValue = oOptions.emptyString;
@@ -1724,15 +1971,15 @@ NumberFormat.prototype.parse = function (sValue) {
1724
1971
  if (oOptions.parseAsString && (oOptions.emptyString === 0 || isNaN(oOptions.emptyString))) {
1725
1972
  vEmptyParseValue = oOptions.emptyString + "";
1726
1973
  }
1727
- if (oOptions.type === mNumberType.CURRENCY || oOptions.type === mNumberType.UNIT) {
1974
+ if (bUnitOrCurrency) {
1975
+ if (!oOptions.showNumber) {
1976
+ return [undefined, vEmptyParseValue];
1977
+ }
1728
1978
  return [vEmptyParseValue, undefined];
1729
1979
  } else {
1730
1980
  return vEmptyParseValue;
1731
1981
  }
1732
1982
  }
1733
- if (typeof sValue !== "string" && !(sValue instanceof String)) {
1734
- return null;
1735
- }
1736
1983
  if (oOptions.groupingSeparator === oOptions.decimalSeparator) {
1737
1984
  Log.error("The grouping and decimal separator both have the same value '" + oOptions.groupingSeparator + "'. " + "They must be different from each other such that values can be parsed correctly.");
1738
1985
  }
@@ -1839,13 +2086,10 @@ NumberFormat.prototype.parse = function (sValue) {
1839
2086
  }
1840
2087
  }
1841
2088
 
1842
- // remove the RTL special characters before the string is matched with the regex
1843
- sValue = sValue.replace(/[\u202a\u200e\u202c\u202b\u200f]/g, "");
1844
-
1845
2089
  // remove all white spaces because when grouping separator is a non-breaking space (russian and french for example)
1846
2090
  // user will not input it this way. Also white spaces or grouping separator can be ignored by determining the value
1847
2091
  sValue = sValue.replace(rAllWhiteSpaces, "");
1848
- oShort = getNumberFromShortened(sValue, this.oLocaleData, bIndianCurrency);
2092
+ oShort = this.getNumberFromShortened(sValue, bIndianCurrency);
1849
2093
  if (oShort) {
1850
2094
  sValue = oShort.number;
1851
2095
  }
@@ -1961,7 +2205,7 @@ NumberFormat.prototype.getScale = function () {
1961
2205
  if (this.oFormatOptions.style !== "short" && this.oFormatOptions.style !== "long" || this.oFormatOptions.shortRefNumber === undefined) {
1962
2206
  return;
1963
2207
  }
1964
- var oShortFormat = getShortenedFormat(this.oFormatOptions.shortRefNumber, this.oFormatOptions, this.oLocaleData),
2208
+ var oShortFormat = this.getShortenedFormat(this.oFormatOptions.shortRefNumber, this.oFormatOptions),
1965
2209
  sScale;
1966
2210
  if (oShortFormat && oShortFormat.formatString) {
1967
2211
  // remove the placeholder of number
@@ -1974,10 +2218,23 @@ NumberFormat.prototype.getScale = function () {
1974
2218
  }
1975
2219
  }
1976
2220
  };
1977
- NumberFormat._shiftDecimalPoint = function (vValue, iStep) {
1978
- if (typeof iStep !== "number") {
1979
- return NaN;
1980
- }
2221
+
2222
+ /**
2223
+ * Moves the decimal seperator of the given number by the given steps to the right or left.
2224
+ *
2225
+ * @param {number|string} vValue
2226
+ * The number
2227
+ * @param {int} iStep
2228
+ * The number of decimal places to shift the "."; positive values shift to the right, negative values shift to the
2229
+ * left
2230
+ * @param {boolean} bNormalize
2231
+ * Whether the result is normalized if <code>vValue</code> is of type "string"; that means whether trailing zeros
2232
+ * are removed and whether scientific notation is resolved to a decimal string without exponent
2233
+ * @returns {number|string|null}
2234
+ * The number with shifted decimal point; or <code>null</code> if the given value is neither of type "number", nor
2235
+ * of type "string"
2236
+ */
2237
+ NumberFormat._shiftDecimalPoint = function (vValue, iStep, bNormalize) {
1981
2238
  var sMinus = "";
1982
2239
  var aExpParts = vValue.toString().toLowerCase().split("e");
1983
2240
  if (typeof vValue === "number") {
@@ -1988,7 +2245,7 @@ NumberFormat._shiftDecimalPoint = function (vValue, iStep) {
1988
2245
  iStep = aExpParts[1] ? +aExpParts[1] + iStep : iStep;
1989
2246
  return +(aExpParts[0] + "e" + iStep);
1990
2247
  } else if (typeof vValue === "string") {
1991
- if (parseFloat(vValue) === 0 && iStep >= 0) {
2248
+ if (!bNormalize && parseFloat(vValue) === 0 && iStep >= 0) {
1992
2249
  // input "00000" should become "0"
1993
2250
  // input "000.000" should become "0.000" to keep precision of decimals
1994
2251
  // input "1e-1337" should remain "1e-1337" in order to keep the precision
@@ -1999,7 +2256,7 @@ NumberFormat._shiftDecimalPoint = function (vValue, iStep) {
1999
2256
  // The minus sign will be added to the final result again.
2000
2257
  var sFirstChar = aExpParts[0].charAt(0);
2001
2258
  sMinus = sFirstChar === "-" ? sFirstChar : "";
2002
- if (sMinus) {
2259
+ if (sMinus || sFirstChar === "+") {
2003
2260
  aExpParts[0] = aExpParts[0].slice(1);
2004
2261
  }
2005
2262
  vValue = aExpParts[0];
@@ -2031,20 +2288,174 @@ NumberFormat._shiftDecimalPoint = function (vValue, iStep) {
2031
2288
  vValue = vValue.replace(".", "");
2032
2289
  sInt = vValue.substring(0, iAfterMovePos);
2033
2290
  sDecimal = vValue.substring(iAfterMovePos);
2034
-
2035
2291
  // remove unnecessary leading zeros
2036
2292
  sInt = sInt.replace(rLeadingZeros, "$1$2");
2293
+ if (bNormalize) {
2294
+ sDecimal = sDecimal.replace(rTrailingZeros, "");
2295
+ }
2037
2296
  return sMinus + sInt + (sDecimal ? "." + sDecimal : "");
2038
2297
  } else {
2039
2298
  // can't shift decimal point in this case
2040
2299
  return null;
2041
2300
  }
2042
2301
  };
2043
- function getShortenedFormat(fValue, oOptions, oLocaleData, bIndianCurrency) {
2302
+
2303
+ /**
2304
+ * Checks whether there is a letter next to the number using the given currency pattern.
2305
+ *
2306
+ * @param {string} sPattern
2307
+ * The currency pattern, e.g. "¤#,##0.00;(¤#,##0.00)" or "¤ 000K"
2308
+ * @param {string} sCurrency
2309
+ * The currency code or the currency symbol to check, e.g. "USD" or "$"
2310
+ * @param {boolean} bNegative
2311
+ * Whether the value to be formatted is negative
2312
+ * @returns {boolean}
2313
+ * Whether there is a letter next to the number for the given pattern and currency
2314
+ *
2315
+ * @private
2316
+ */
2317
+ NumberFormat.isAlphaNextToNumber = function (sPattern, sCurrency, bNegative) {
2318
+ if (!sPattern || !sCurrency) {
2319
+ return false;
2320
+ }
2321
+ const aPatterns = sPattern.split(";");
2322
+ sPattern = (aPatterns[bNegative ? 1 : 0] || aPatterns[0]).replace(rAllRTLCharacters, "");
2323
+ const aMatches = rSplitCurrencyPattern.exec(sPattern);
2324
+
2325
+ // number in front of the currency placeholder
2326
+ if (aMatches[1]) {
2327
+ return rStartsWithLetter.test(sCurrency) && !aMatches[2];
2328
+ }
2329
+
2330
+ // currency placeholder in front of the number
2331
+ return !aMatches[4] // no characters between the placeholder and the number
2332
+ && rEndsWithLetter.test(sCurrency) // currency ends with a letter
2333
+ // if there is no separate negative pattern and the value is negative, there is a minus sign between
2334
+ // the currency and the number, so there is no need for the alphaNextToNumber pattern
2335
+ && (!bNegative || aPatterns.length !== 1);
2336
+ };
2337
+
2338
+ /**
2339
+ * Gets the compact decimal or currency pattern for the given power of ten and plural category.
2340
+ *
2341
+ * @param {"integer"|"float"|"currency"|"unit"|"percent"} sType
2342
+ * The number format type
2343
+ * @param {"long"|"short"} sStyle
2344
+ * The style of the compact format
2345
+ * @param {string} sPowerOfTen
2346
+ * The power of ten
2347
+ * @param {"few"|"many"|"one"|"other"|"two"|"zero"} sPluralCategory
2348
+ * The plural category
2349
+ * @param {boolean} [bTrailingCurrencyCode]
2350
+ * Whether the currency code is formatted after the amount; only relevant if type "currency" is used
2351
+ * @param {boolean} [bIndianCurrency]
2352
+ * Whether to use the Indian currency format; only relevant if type "currency" is used
2353
+ * @param {boolean} [bShowMeasure]
2354
+ * Whether to show the measure
2355
+ * @param {string} [sCurrency]
2356
+ * The currency code or symbol
2357
+ * @param {boolean} [bNegative]
2358
+ * Whether the number is negative
2359
+ * @returns {string|undefined}
2360
+ * The compact decimal or currency pattern for the given power of ten and plural category; or
2361
+ * <code>undefined</code> if there is no pattern for the given parameters
2362
+ *
2363
+ * @private
2364
+ */
2365
+ NumberFormat.prototype.getCompactPattern = function (sType, sStyle, sPowerOfTen, sPluralCategory, bTrailingCurrencyCode, bIndianCurrency, bShowMeasure, sCurrency, bNegative) {
2366
+ let sPattern;
2367
+ if (sType === mNumberType.CURRENCY) {
2368
+ if (bTrailingCurrencyCode) {
2369
+ sStyle = "sap-short";
2370
+ }
2371
+ if (bIndianCurrency) {
2372
+ sStyle += "-indian";
2373
+ }
2374
+ if (bShowMeasure) {
2375
+ // Use currency specific format because for some languages there is a difference between the
2376
+ // decimalFormat and the currencyFormat
2377
+ sPattern = this.oLocaleData.getCompactCurrencyPattern(sStyle, sPowerOfTen, sPluralCategory);
2378
+ if (NumberFormat.isAlphaNextToNumber(sPattern, sCurrency, bNegative)) {
2379
+ sPattern = this.oLocaleData.getCompactCurrencyPattern(sStyle, sPowerOfTen, sPluralCategory, "alphaNextToNumber") || sPattern;
2380
+ }
2381
+ } else {
2382
+ sPattern = this.oLocaleData.getCompactCurrencyPattern(sStyle, sPowerOfTen, sPluralCategory, "noCurrency");
2383
+ if (!sPattern) {
2384
+ if (sStyle.startsWith("sap-")) {
2385
+ sStyle = sStyle.slice(4);
2386
+ }
2387
+ sPattern = this.oLocaleData.getCompactDecimalPattern(sStyle, sPowerOfTen, sPluralCategory);
2388
+ }
2389
+ }
2390
+ } else {
2391
+ sPattern = this.oLocaleData.getCompactDecimalPattern(sStyle, sPowerOfTen, sPluralCategory);
2392
+ }
2393
+
2394
+ // pattern may contain a single quoted dot ('.') to differentiate them from decimal separator; replace it
2395
+ // with an unquoted dot (.)
2396
+ sPattern = sPattern?.replace(/'.'/g, ".");
2397
+ return sPattern;
2398
+ };
2399
+
2400
+ /**
2401
+ * Gets the locale specific currency pattern for the given parameters.
2402
+ *
2403
+ * @param {"accounting"|"standard"} sContext The context of the currency pattern
2404
+ * @param {boolean} [bShowTrailingCurrencyCode] Whether the currency code shall be shown after the amount
2405
+ * @param {boolean} [bShowMeasure] Whether to include the measure (currency code or currency symbol) in the pattern
2406
+ * @param {string} [sCurrency] The currency code or symbol to use
2407
+ * @param {boolean} [bNegative] Whether the current value is negative
2408
+ * @returns {string} The currency pattern
2409
+ *
2410
+ * @private
2411
+ */
2412
+ NumberFormat.prototype.getCurrencyPattern = function (sContext, bShowTrailingCurrencyCode, bShowMeasure, sCurrency, bNegative) {
2413
+ if (bShowTrailingCurrencyCode) {
2414
+ sContext = "sap-" + sContext;
2415
+ }
2416
+ let sPattern = this.oLocaleData.getCurrencyPattern(sContext, bShowMeasure ? undefined : "noCurrency");
2417
+ if (bShowMeasure && NumberFormat.isAlphaNextToNumber(sPattern, sCurrency, bNegative)) {
2418
+ sPattern = this.oLocaleData.getCurrencyPattern(sContext, "alphaNextToNumber") || sPattern;
2419
+ }
2420
+ return sPattern;
2421
+ };
2422
+
2423
+ /**
2424
+ * Gets the compact decimal or currency format for the given value and parameters.
2425
+ *
2426
+ * @param {number|string} vValue
2427
+ * The value for which the shortened format is determined
2428
+ * @param {object} oOptions
2429
+ * The options used for getting the compact pattern
2430
+ * @param {int} [oOptions.precision = 2]
2431
+ * The maximum number of digits in the formatted representation of the number
2432
+ * @param {"long"|"short"} oOptions.style
2433
+ * The style of the compact format
2434
+ * @param {boolean} [oOptions.trailingCurrencyCode]
2435
+ * Whether the currency code is formatted after the amount; only relevant if type "currency" is used
2436
+ * @param {"integer"|"float"|"currency"|"unit"|"percent"} oOptions.type
2437
+ * The number format type
2438
+ * @param {boolean} [bIndianCurrency]
2439
+ * Whether to use the Indian currency format; only relevant if type "currency" is used
2440
+ *
2441
+ * @returns {object|undefined}
2442
+ * The compact decimal or currency format for the given value; or <code>undefined</code> if neither the "short"
2443
+ * or the "long" style is used, or if there is no compact format for the given parameters; the returned object
2444
+ * contains the following properties:
2445
+ * <ul>
2446
+ * <li><code>decimals</code>: The number of decimals used in the compact format pattern</li>
2447
+ * <li><code>formatString</code>: The compact format pattern to use</li>
2448
+ * <li><code>key</code>: The power of ten matching the given value</li>
2449
+ * <li><code>magnitude</code>: The divisor to get the compact number to show from the given value</li>
2450
+ * <li><code>valueSubString</code>: The number part of the format pattern</li>
2451
+ * </ul>
2452
+ *
2453
+ * @private
2454
+ */
2455
+ NumberFormat.prototype.getShortenedFormat = function (vValue, oOptions, bIndianCurrency) {
2044
2456
  var oShortFormat,
2045
2457
  iKey,
2046
2458
  sKey,
2047
- sCldrFormat,
2048
2459
  sStyle = oOptions.style,
2049
2460
  iPrecision = oOptions.precision !== undefined ? oOptions.precision : 2;
2050
2461
  if (sStyle != "short" && sStyle != "long") {
@@ -2052,7 +2463,7 @@ function getShortenedFormat(fValue, oOptions, oLocaleData, bIndianCurrency) {
2052
2463
  }
2053
2464
  for (var i = 0; i < 15; i++) {
2054
2465
  iKey = Math.pow(10, i);
2055
- if (rounding(Math.abs(fValue) / iKey, iPrecision - 1) < 10) {
2466
+ if (rounding(Math.abs(vValue) / iKey, iPrecision - 1) < 10) {
2056
2467
  break;
2057
2468
  }
2058
2469
  }
@@ -2060,19 +2471,7 @@ function getShortenedFormat(fValue, oOptions, oLocaleData, bIndianCurrency) {
2060
2471
 
2061
2472
  // Use "other" format to find the right magnitude, the actual format will be retrieved later
2062
2473
  // after the value has been calculated
2063
- if (oOptions.type === mNumberType.CURRENCY) {
2064
- if (oOptions.trailingCurrencyCode) {
2065
- sStyle = "sap-short";
2066
- }
2067
- if (bIndianCurrency) {
2068
- sCldrFormat = getIndianCurrencyFormat(sStyle, sKey, "other", true);
2069
- } else {
2070
- // Use currency specific format because for some languages there is a difference between the decimalFormat and the currencyFormat
2071
- sCldrFormat = oLocaleData.getCurrencyFormat(sStyle, sKey, "other");
2072
- }
2073
- } else {
2074
- sCldrFormat = oLocaleData.getDecimalFormat(sStyle, sKey, "other");
2075
- }
2474
+ const sCldrFormat = this.getCompactPattern(oOptions.type, oOptions.style, sKey, "other", oOptions.trailingCurrencyCode, bIndianCurrency, oOptions.showMeasure);
2076
2475
  if (!sCldrFormat || sCldrFormat == "0") {
2077
2476
  //no format or special "0" format => number doesn't need to be shortened
2078
2477
  return undefined;
@@ -2103,218 +2502,123 @@ function getShortenedFormat(fValue, oOptions, oLocaleData, bIndianCurrency) {
2103
2502
  }
2104
2503
  }
2105
2504
  return oShortFormat;
2106
- }
2107
- function getNumberFromShortened(sValue, oLocaleData, bIndianCurrency) {
2108
- var sNumber,
2109
- iFactor = 1,
2110
- iKey = 10,
2111
- aPluralCategories = oLocaleData.getPluralCategories(),
2112
- sCldrFormat,
2113
- bestResult = {
2114
- number: undefined,
2115
- factor: iFactor
2116
- },
2117
- fnGetFactor = function (sPlural, iKey, sStyle, bIndian) {
2118
- if (bIndian) {
2119
- sCldrFormat = getIndianCurrencyFormat(sStyle, iKey.toString(), sPlural, true);
2120
- } else {
2121
- sCldrFormat = oLocaleData.getDecimalFormat(sStyle, iKey.toString(), sPlural);
2122
- }
2123
- if (sCldrFormat) {
2124
- // Note: CLDR uses a non-breaking space in the format string
2125
- // remove right-to-left mark u+200f character
2126
- sCldrFormat = sCldrFormat.replace(/[\s\u00a0\u200F]/g, "");
2127
- //formatString may contain '.' (quoted to differentiate them decimal separator)
2128
- //which must be replaced with .
2129
- sCldrFormat = sCldrFormat.replace(/'.'/g, ".");
2130
- var match = sCldrFormat.match(rNumPlaceHolder);
2131
- if (match) {
2132
- // determine unit -> may be on the beginning e.g. for he
2133
- var sValueSubString = match[0];
2134
- var sUnit = sCldrFormat.replace(sValueSubString, "");
2135
- if (!sUnit) {
2136
- // If there's no scale defined in the pattern, skip the pattern
2137
- return;
2138
- }
2139
- var iIndex = sValue.indexOf(sUnit);
2140
- if (iIndex >= 0) {
2141
- // parse the number part like every other number and then use the factor to get the real number
2142
- sNumber = sValue.replace(sUnit, "");
2143
- // remove right-to-left mark u+200f character
2144
- sNumber = sNumber.replace(/\u200F/g, "");
2145
- iFactor = iKey;
2146
- // spanish numbers e.g. for MRD in format for "one" is "00 MRD" therefore factor needs to be adjusted
2147
- // german numbers e.g. for Mrd. in format for "one" is "0 Mrd." therefore number does not need to be adjusted
2148
- // "0" => magnitude = key
2149
- // "00" => magnitude = key / 10
2150
- // "000" => magnitude = key / 100
2151
- iFactor *= Math.pow(10, 1 - sValueSubString.length);
2152
-
2153
- // if best result has no number yet or the new number is smaller that the current one set the new number as best result
2154
- if (bestResult.number === undefined || sNumber.length < bestResult.number.length) {
2155
- bestResult.number = sNumber;
2156
- bestResult.factor = iFactor;
2157
- }
2158
- }
2159
- }
2160
- }
2161
- };
2162
- // iterate over all formats. Max: 100 000 000 000 000
2163
- // find best result as format can have multiple matches:
2164
- // * value can be contained one in another (de-DE): "Million" and "Millionen"
2165
- // * end with each other (es-ES): "mil millones" and "millones"
2166
- ["long", "short"].forEach(function (sStyle) {
2167
- iKey = 10;
2168
- while (iKey < 1e15) {
2169
- for (var i = 0; i < aPluralCategories.length; i++) {
2170
- var sPluralCategory = aPluralCategories[i];
2171
- fnGetFactor(sPluralCategory, iKey, sStyle);
2172
- }
2173
- iKey = iKey * 10;
2174
- }
2175
- });
2176
-
2177
- // For india currencies try lakhs/crores
2178
- if (bIndianCurrency && !sNumber) {
2179
- iKey = 10;
2180
- while (iKey < 1e15) {
2181
- for (var i = 0; i < aPluralCategories.length; i++) {
2182
- var sPluralCategory = aPluralCategories[i];
2183
- fnGetFactor(sPluralCategory, iKey, "short", true);
2505
+ };
2506
+
2507
+ /**
2508
+ * Returns an object with two properties. The first is the number substring of the given <code>sValue</code>.
2509
+ * The second property is the factor with which the determined number must be multiplied to resolve
2510
+ * the short format.
2511
+ * If the number of the given <code>sValue</code> is larger than 100 000 000 000 000 no short format will be found
2512
+ * and <code>undefined</code> is returned.
2513
+ *
2514
+ * @example
2515
+ * Formatted value to be parsed: "123K"
2516
+ * Chosen short format: "100000-other": "000K",
2517
+ * Number Substring: "123"
2518
+ * Factor: 1000
2519
+ * Parsed number: 123 * 1000 = 123000
2520
+ *
2521
+ * @param {string} sValue The value for which the short format shall be determined
2522
+ * @param {boolean} bIndianCurrency Whether the the value has to be treated as Indian currency
2523
+ *
2524
+ * @returns {{number: string, factor: number}|undefined}
2525
+ * An object containing the number substring of the given <code>sValue</code>, e.g. <code>"123"</code> and
2526
+ * the factor with which the determined number must be multiplied to resolve the short format;
2527
+ * <code>undefined</code> if no short format is found for the given <code>sValue</code>
2528
+ *
2529
+ * @private
2530
+ */
2531
+ NumberFormat.prototype.getNumberFromShortened = function (sValue, bIndianCurrency) {
2532
+ const aPluralCategories = this.oLocaleData.getPluralCategories();
2533
+ const oBestResult = {
2534
+ number: undefined,
2535
+ factor: 1
2536
+ };
2537
+ const oIndianBestResult = {
2538
+ number: undefined,
2539
+ factor: 1
2540
+ };
2541
+ for (const iPowerOfTen of aPowerOfTens) {
2542
+ for (const sPluralCategory of aPluralCategories) {
2543
+ if (bIndianCurrency) {
2544
+ this.updateBestResult(oIndianBestResult, sPluralCategory, iPowerOfTen, "short-indian", sValue);
2184
2545
  }
2185
- iKey = iKey * 10;
2546
+ this.updateBestResult(oBestResult, sPluralCategory, iPowerOfTen, "long", sValue);
2547
+ this.updateBestResult(oBestResult, sPluralCategory, iPowerOfTen, "short", sValue);
2186
2548
  }
2187
2549
  }
2188
- if (!sNumber) {
2189
- return;
2550
+ if (oIndianBestResult.number) {
2551
+ return oIndianBestResult;
2552
+ } else if (oBestResult.number) {
2553
+ return oBestResult;
2190
2554
  }
2191
- return bestResult;
2192
- }
2555
+ return undefined;
2556
+ };
2193
2557
 
2194
2558
  /**
2195
- * Based on the format options and the global config, determine whether to display a trailing currency code
2196
- * @param oFormatOptions
2197
- * @returns {boolean}
2559
+ * Updates the number and factor in the given reference object based on the plural category,
2560
+ * power of ten, and style with the best match if one can be found for a given value.
2561
+ *
2562
+ * @param {{number: string, factor: number}} oBestResult
2563
+ * A reference in which the number and factor determined by this function will be stored
2564
+ * @param {"zero"|"one"|"two"|"few"|"many"} sPluralCategory
2565
+ * The plural category
2566
+ * @param {int} iPowerOfTen
2567
+ * The power of ten, max: 100 000 000 000 000
2568
+ * @param {"long"|"short"|"short-indian"} sStyle
2569
+ * The style
2570
+ * @param {string} sValue
2571
+ * The value for which the number and factor has to be determined
2572
+ *
2573
+ * @private
2198
2574
  */
2199
- function showTrailingCurrencyCode(oFormatOptions) {
2200
- var bShowTrailingCurrencyCodes = Configuration.getFormatSettings().getTrailingCurrencyCode();
2201
- if (oFormatOptions) {
2202
- // overwritten by instance configuration
2203
- if (oFormatOptions.trailingCurrencyCode !== undefined) {
2204
- bShowTrailingCurrencyCodes = oFormatOptions.trailingCurrencyCode;
2205
- }
2206
-
2207
- // is false when custom pattern is used
2208
- if (oFormatOptions.pattern) {
2209
- bShowTrailingCurrencyCodes = false;
2210
- }
2211
-
2212
- // is false when currencyCode is not used
2213
- if (oFormatOptions.currencyCode === false) {
2214
- bShowTrailingCurrencyCodes = false;
2575
+ NumberFormat.prototype.updateBestResult = function (oBestResult, sPluralCategory, iPowerOfTen, sStyle, sValue) {
2576
+ let sCldrFormat = this.oLocaleData.getCompactDecimalPattern(sStyle, iPowerOfTen.toString(), sPluralCategory);
2577
+ if (sCldrFormat) {
2578
+ // Note: CLDR uses a non-breaking space and right-to-left mark u+200f in the format string
2579
+ sCldrFormat = FormatUtils.normalize(sCldrFormat, true);
2580
+ //formatString may contain '.' (quoted to differentiate them from decimal separator)
2581
+ //which must be replaced with .
2582
+ sCldrFormat = sCldrFormat.replace(/'.'/g, ".");
2583
+ const aMatch = sCldrFormat.match(rNumPlaceHolder);
2584
+ if (aMatch) {
2585
+ // determine unit -> may be on the beginning e.g. for he
2586
+ const sValueSubString = aMatch[0];
2587
+ const sScalingFactor = sCldrFormat.replace(sValueSubString, "");
2588
+ if (sScalingFactor && sValue.includes(sScalingFactor)) {
2589
+ // parse the number part like every other number and then use the factor to get the real number
2590
+ const sNumber = sValue.replace(sScalingFactor, "");
2591
+ // spanish numbers e.g. for MRD in format for "one" is "00 MRD" therefore factor needs to be
2592
+ // adjusted
2593
+ // german numbers e.g. for Mrd. in format for "one" is "0 Mrd." therefore number does not need to
2594
+ // be adjusted
2595
+ // "0" => magnitude = key
2596
+ // "00" => magnitude = key / 10
2597
+ // "000" => magnitude = key / 100
2598
+ const iFactor = iPowerOfTen * Math.pow(10, 1 - sValueSubString.length);
2599
+ if (oBestResult.number === undefined || sNumber.length < oBestResult.number.length) {
2600
+ oBestResult.number = sNumber;
2601
+ oBestResult.factor = iFactor;
2602
+ }
2603
+ }
2215
2604
  }
2216
2605
  }
2217
- return bShowTrailingCurrencyCodes;
2218
- }
2219
- function getIndianCurrencyFormat(sStyle, sKey, sPlural, bDecimal) {
2220
- var sFormat,
2221
- oCurrencyFormats = {
2222
- "short": {
2223
- "1000-one": "\xa40000",
2224
- "1000-other": "\xa40000",
2225
- "10000-one": "\xa400000",
2226
- "10000-other": "\xa400000",
2227
- "100000-one": "\xa40 Lk",
2228
- "100000-other": "\xa40 Lk",
2229
- "1000000-one": "\xa400 Lk",
2230
- "1000000-other": "\xa400 Lk",
2231
- "10000000-one": "\xa40 Cr",
2232
- "10000000-other": "\xa40 Cr",
2233
- "100000000-one": "\xa400 Cr",
2234
- "100000000-other": "\xa400 Cr",
2235
- "1000000000-one": "\xa4000 Cr",
2236
- "1000000000-other": "\xa4000 Cr",
2237
- "10000000000-one": "\xa40000 Cr",
2238
- "10000000000-other": "\xa40000 Cr",
2239
- "100000000000-one": "\xa400000 Cr",
2240
- "100000000000-other": "\xa400000 Cr",
2241
- "1000000000000-one": "\xa40 Lk Cr",
2242
- "1000000000000-other": "\xa40 Lk Cr",
2243
- "10000000000000-one": "\xa400 Lk Cr",
2244
- "10000000000000-other": "\xa400 Lk Cr",
2245
- "100000000000000-one": "\xa40 Cr Cr",
2246
- "100000000000000-other": "\xa40 Cr Cr"
2247
- },
2248
- "sap-short": {
2249
- "1000-one": "0000\xa0\xa4",
2250
- "1000-other": "0000\xa0\xa4",
2251
- "10000-one": "00000\xa0\xa4",
2252
- "10000-other": "00000\xa0\xa4",
2253
- "100000-one": "0 Lk\xa0\xa4",
2254
- "100000-other": "0 Lk\xa0\xa4",
2255
- "1000000-one": "00 Lk\xa0\xa4",
2256
- "1000000-other": "00 Lk\xa0\xa4",
2257
- "10000000-one": "0 Cr\xa0\xa4",
2258
- "10000000-other": "0 Cr\xa0\xa4",
2259
- "100000000-one": "00 Cr\xa0\xa4",
2260
- "100000000-other": "00 Cr\xa0\xa4",
2261
- "1000000000-one": "000 Cr\xa0\xa4",
2262
- "1000000000-other": "000 Cr\xa0\xa4",
2263
- "10000000000-one": "0000 Cr\xa0\xa4",
2264
- "10000000000-other": "0000 Cr\xa0\xa4",
2265
- "100000000000-one": "00000 Cr\xa0\xa4",
2266
- "100000000000-other": "00000 Cr\xa0\xa4",
2267
- "1000000000000-one": "0 Lk Cr\xa0\xa4",
2268
- "1000000000000-other": "0 Lk Cr\xa0\xa4",
2269
- "10000000000000-one": "00 Lk Cr\xa0\xa4",
2270
- "10000000000000-other": "00 Lk Cr\xa0\xa4",
2271
- "100000000000000-one": "0 Cr Cr\xa0\xa4",
2272
- "100000000000000-other": "0 Cr Cr\xa0\xa4"
2273
- }
2274
- },
2275
- oDecimalFormats = {
2276
- "short": {
2277
- "1000-one": "0000",
2278
- "1000-other": "0000",
2279
- "10000-one": "00000",
2280
- "10000-other": "00000",
2281
- "100000-one": "0 Lk",
2282
- "100000-other": "0 Lk",
2283
- "1000000-one": "00 Lk",
2284
- "1000000-other": "00 Lk",
2285
- "10000000-one": "0 Cr",
2286
- "10000000-other": "0 Cr",
2287
- "100000000-one": "00 Cr",
2288
- "100000000-other": "00 Cr",
2289
- "1000000000-one": "000 Cr",
2290
- "1000000000-other": "000 Cr",
2291
- "10000000000-one": "0000 Cr",
2292
- "10000000000-other": "0000 Cr",
2293
- "100000000000-one": "00000 Cr",
2294
- "100000000000-other": "00000 Cr",
2295
- "1000000000000-one": "0 Lk Cr",
2296
- "1000000000000-other": "0 Lk Cr",
2297
- "10000000000000-one": "00 Lk Cr",
2298
- "10000000000000-other": "00 Lk Cr",
2299
- "100000000000000-one": "0 Cr Cr",
2300
- "100000000000000-other": "0 Cr Cr"
2301
- }
2302
- };
2303
- // decimal format for short and sap-short is the same
2304
- oDecimalFormats["sap-short"] = oDecimalFormats["short"];
2606
+ };
2305
2607
 
2306
- // use the appropriate format (either decimal or currency)
2307
- var oTargetFormat = bDecimal ? oDecimalFormats : oCurrencyFormats;
2308
- var oStyledFormat = oTargetFormat[sStyle];
2309
- if (!oStyledFormat) {
2310
- oStyledFormat = oTargetFormat["short"];
2311
- }
2312
- if (sPlural !== "one") {
2313
- sPlural = "other";
2314
- }
2315
- sFormat = oStyledFormat[sKey + "-" + sPlural];
2316
- return sFormat;
2317
- }
2608
+ /**
2609
+ * Whether to show the currency code at the end based on the original format options and the global configuration.
2610
+ *
2611
+ * @returns {boolean} Whether to show trailing currency code
2612
+ */
2613
+ NumberFormat.prototype.showTrailingCurrencyCode = function () {
2614
+ const oFormatOptions = this.oOriginalFormatOptions;
2615
+ // use default currency mode if custom pattern is given or currency code shall not be shown
2616
+ if (oFormatOptions?.pattern || oFormatOptions?.currencyCode === false) {
2617
+ return false;
2618
+ }
2619
+ return oFormatOptions?.trailingCurrencyCode !== undefined ? oFormatOptions.trailingCurrencyCode // overwritten by instance configuration
2620
+ : Formatting.getTrailingCurrencyCode();
2621
+ };
2318
2622
 
2319
2623
  /**
2320
2624
  * Checks if grouping is performed correctly (decimal separator is not confused with grouping separator).
@@ -2446,6 +2750,18 @@ NumberFormat.prototype._checkGrouping = function (sValueWithGrouping, oOptions,
2446
2750
  return true;
2447
2751
  };
2448
2752
 
2753
+ /**
2754
+ * Checks whether grouping will be enabled for this instance.
2755
+ *
2756
+ * @private
2757
+ */
2758
+ NumberFormat.prototype.checkGroupingFormatOptions = function () {
2759
+ if (this.oFormatOptions.groupingEnabled && this.oFormatOptions.groupingSize <= 0) {
2760
+ Log.warning("Grouping is disabled due to non-positive groupingSize set to '" + this.oFormatOptions.groupingSize + "'.");
2761
+ this.oFormatOptions.groupingEnabled = false;
2762
+ }
2763
+ };
2764
+
2449
2765
  /**
2450
2766
  * Whether or not the given value is in scientific notation
2451
2767
  *
@@ -2487,50 +2803,63 @@ function getInteger(sValue) {
2487
2803
  }
2488
2804
  return iInt;
2489
2805
  }
2490
- function rounding(fValue, iMaxFractionDigits, sRoundingMode) {
2491
- if (typeof fValue !== "number") {
2492
- return NaN;
2493
- }
2494
- sRoundingMode = sRoundingMode || NumberFormat.RoundingMode.HALF_AWAY_FROM_ZERO;
2806
+
2807
+ /**
2808
+ * Rounds the given value by the given number of fraction digits based on the given rounding mode.
2809
+ *
2810
+ * @param {number|string} vValue
2811
+ * The number to be rounded, may be a string or a number; has to be of type number if a custom rounding function
2812
+ * is used
2813
+ * @param {int|string} iMaxFractionDigits
2814
+ * The maximum number of fraction digits
2815
+ * @param {sap.ui.core.format.NumberFormat.RoundingMode|function(number,int):number} vRoundingMode
2816
+ * The rounding mode or a custom function for rounding which is called with the number and the number of decimal
2817
+ * digits that should be reserved; <b>using a function is deprecated since 1.121.0</b>; string based numbers are
2818
+ * not rounded via this custom function.
2819
+ * @returns {number|string}
2820
+ * The rounded value; the returned type is the same as the type of the given <code>vValue</code>
2821
+ */
2822
+ function rounding(vValue, iMaxFractionDigits, vRoundingMode) {
2823
+ vRoundingMode = vRoundingMode || NumberFormat.RoundingMode.HALF_AWAY_FROM_ZERO;
2495
2824
  iMaxFractionDigits = parseInt(iMaxFractionDigits);
2496
2825
 
2497
2826
  // only round if it is required (number of fraction digits is bigger than the maxFractionDigits option)
2498
- var sValue = "" + fValue;
2827
+ var sValue = "" + vValue;
2499
2828
  if (!isScientificNotation(sValue)) {
2500
2829
  var iIndexOfPoint = sValue.indexOf(".");
2501
2830
  if (iIndexOfPoint < 0) {
2502
- return fValue;
2831
+ return vValue;
2503
2832
  }
2504
2833
  if (sValue.substring(iIndexOfPoint + 1).length <= iMaxFractionDigits) {
2505
- return fValue;
2834
+ if (typeof vValue === "string") {
2835
+ vValue = NumberFormat._shiftDecimalPoint(vValue, 0, true);
2836
+ }
2837
+ return vValue;
2506
2838
  }
2507
2839
  }
2508
- if (typeof sRoundingMode === "function") {
2840
+ if (typeof vRoundingMode === "function") {
2509
2841
  // Support custom function for rounding the number
2510
- fValue = sRoundingMode(fValue, iMaxFractionDigits);
2842
+ vValue = vRoundingMode(vValue, iMaxFractionDigits);
2511
2843
  } else {
2512
2844
  // The NumberFormat.RoundingMode had all values in lower case before and later changed all values to upper case
2513
2845
  // to match the key according to the UI5 guideline for defining enum. Therefore it's needed to support both
2514
2846
  // lower and upper cases. Here checks whether the value has only lower case letters and converts it all to upper
2515
2847
  // case if so.
2516
- if (sRoundingMode.match(/^[a-z_]+$/)) {
2517
- sRoundingMode = sRoundingMode.toUpperCase();
2518
- }
2519
- if (!iMaxFractionDigits) {
2520
- return mRoundingFunction[sRoundingMode](fValue);
2848
+ if (vRoundingMode.match(/^[a-z_]+$/)) {
2849
+ vRoundingMode = vRoundingMode.toUpperCase();
2521
2850
  }
2522
2851
 
2523
- // First move the decimal point towards right by maxFactionDigits
2524
- // Then using the rounding function to round the first digit after decimal point
2525
- // In the end, move the decimal point back to the original position
2526
- //
2527
- // For example rounding 1.005 by maxFractionDigits 2
2528
- // 1. Move the decimal point to right by 2 digits, result 100.5
2529
- // 2. Using the round function, for example, Math.round(100.5) = 101
2530
- // 3. Move the decimal point back by 2 digits, result 1.01
2531
- fValue = NumberFormat._shiftDecimalPoint(mRoundingFunction[sRoundingMode](NumberFormat._shiftDecimalPoint(fValue, iMaxFractionDigits)), -iMaxFractionDigits);
2532
- }
2533
- return fValue;
2852
+ // 1. Move the decimal point to right by maxFactionDigits; e.g. 1.005 with maxFractionDigits 2 => 100.5
2853
+ vValue = NumberFormat._shiftDecimalPoint(vValue, iMaxFractionDigits, true);
2854
+ // 2. Use the rounding function to round the first digit after decimal point; e.g. ceil(100.5) => 101
2855
+ vValue = mRoundingFunction[vRoundingMode](vValue);
2856
+ // 3. Finally move the decimal point back to the original position; e.g. by 2 digits => 1.01
2857
+ vValue = NumberFormat._shiftDecimalPoint(vValue, -iMaxFractionDigits, true);
2858
+ if (typeof vValue === "string") {
2859
+ vValue = vValue.replace(rRemoveMinusFromZero, "$1");
2860
+ }
2861
+ }
2862
+ return vValue;
2534
2863
  }
2535
2864
  function quote(sRegex) {
2536
2865
  return sRegex.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
@@ -2588,7 +2917,7 @@ function parseNumberAndUnit(mUnitPatterns, sValue, bShowNumber, sLanguageTag) {
2588
2917
  if (!sKey.startsWith("unitPattern")) {
2589
2918
  continue;
2590
2919
  }
2591
- sUnitPattern = mUnitPatterns[sUnitCode][sKey];
2920
+ sUnitPattern = FormatUtils.normalize(mUnitPatterns[sUnitCode][sKey]);
2592
2921
 
2593
2922
  // IMPORTANT:
2594
2923
  // To increase performance we are using native string operations instead of regex,
@@ -2711,7 +3040,7 @@ function findLongestMatch(sValue, mCollection, bCaseInsensitive) {
2711
3040
  if (!sCurSymbol) {
2712
3041
  continue;
2713
3042
  }
2714
- sCurSymbol = sCurSymbol.replace(rAllWhiteSpaces, "\u0020");
3043
+ sCurSymbol = FormatUtils.normalize(sCurSymbol);
2715
3044
  if (sValue.indexOf(sCurSymbol) >= 0 && sSymbol.length <= sCurSymbol.length) {
2716
3045
  sCode = sCurCode;
2717
3046
  bDuplicate = false;
@@ -2719,7 +3048,7 @@ function findLongestMatch(sValue, mCollection, bCaseInsensitive) {
2719
3048
  sSymbol = sCurSymbol;
2720
3049
  sRecognizedCurrency = sCurSymbol;
2721
3050
  } else if (bCaseInsensitive) {
2722
- sLanguageTag = Configuration.getLanguageTag();
3051
+ sLanguageTag = Localization.getLanguageTag().toString();
2723
3052
  sCurSymbolToUpperCase = sCurSymbol.toLocaleUpperCase(sLanguageTag);
2724
3053
  iIndex = sValue.toLocaleUpperCase(sLanguageTag).indexOf(sCurSymbolToUpperCase);
2725
3054
  if (iIndex >= 0) {
@@ -2768,7 +3097,7 @@ function findLongestMatch(sValue, mCollection, bCaseInsensitive) {
2768
3097
  */
2769
3098
  function parseNumberAndCurrency(oConfig) {
2770
3099
  var aIsoMatches,
2771
- sValue = oConfig.value.replace(rAllWhiteSpaces, "\u0020");
3100
+ sValue = oConfig.value;
2772
3101
 
2773
3102
  // Search for known symbols (longest match)
2774
3103
  // no distinction between default and custom currencies
@@ -2782,7 +3111,7 @@ function parseNumberAndCurrency(oConfig) {
2782
3111
  if (!oMatch.code && !oConfig.customCurrenciesAvailable) {
2783
3112
  // Match 3-letter iso code
2784
3113
  aIsoMatches = sValue.match(/(^[A-Z]{3}|[A-Z]{3}$)/i);
2785
- oMatch.code = aIsoMatches && aIsoMatches[0].toLocaleUpperCase(Configuration.getLanguageTag());
3114
+ oMatch.code = aIsoMatches && aIsoMatches[0].toLocaleUpperCase(Localization.getLanguageTag().toString());
2786
3115
  oMatch.recognizedCurrency = aIsoMatches && aIsoMatches[0];
2787
3116
  }
2788
3117
  }