n2words 1.24.0 → 3.0.0

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 (280) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +183 -156
  3. package/dist/languages/am-Latn.js +3 -0
  4. package/dist/languages/am-Latn.js.map +1 -0
  5. package/dist/languages/am.js +3 -0
  6. package/dist/languages/am.js.map +1 -0
  7. package/dist/languages/ar.js +3 -2
  8. package/dist/languages/ar.js.map +1 -1
  9. package/dist/languages/az.js +3 -2
  10. package/dist/languages/az.js.map +1 -1
  11. package/dist/languages/bn.js +3 -2
  12. package/dist/languages/bn.js.map +1 -1
  13. package/dist/languages/cs.js +3 -2
  14. package/dist/languages/cs.js.map +1 -1
  15. package/dist/languages/da.js +3 -2
  16. package/dist/languages/da.js.map +1 -1
  17. package/dist/languages/de.js +3 -2
  18. package/dist/languages/de.js.map +1 -1
  19. package/dist/languages/el.js +3 -2
  20. package/dist/languages/el.js.map +1 -1
  21. package/dist/languages/en.js +3 -2
  22. package/dist/languages/en.js.map +1 -1
  23. package/dist/languages/es.js +3 -2
  24. package/dist/languages/es.js.map +1 -1
  25. package/dist/languages/fa.js +3 -2
  26. package/dist/languages/fa.js.map +1 -1
  27. package/dist/languages/fi.js +3 -0
  28. package/dist/languages/fi.js.map +1 -0
  29. package/dist/languages/fil.js +3 -2
  30. package/dist/languages/fil.js.map +1 -1
  31. package/dist/languages/fr-BE.js +3 -2
  32. package/dist/languages/fr-BE.js.map +1 -1
  33. package/dist/languages/fr.js +3 -2
  34. package/dist/languages/fr.js.map +1 -1
  35. package/dist/languages/gu.js +3 -2
  36. package/dist/languages/gu.js.map +1 -1
  37. package/dist/languages/ha.js +3 -0
  38. package/dist/languages/ha.js.map +1 -0
  39. package/dist/languages/hbo.js +3 -0
  40. package/dist/languages/hbo.js.map +1 -0
  41. package/dist/languages/he.js +3 -2
  42. package/dist/languages/he.js.map +1 -1
  43. package/dist/languages/hi.js +3 -2
  44. package/dist/languages/hi.js.map +1 -1
  45. package/dist/languages/hr.js +3 -2
  46. package/dist/languages/hr.js.map +1 -1
  47. package/dist/languages/hu.js +3 -2
  48. package/dist/languages/hu.js.map +1 -1
  49. package/dist/languages/id.js +3 -2
  50. package/dist/languages/id.js.map +1 -1
  51. package/dist/languages/it.js +3 -2
  52. package/dist/languages/it.js.map +1 -1
  53. package/dist/languages/ja.js +3 -2
  54. package/dist/languages/ja.js.map +1 -1
  55. package/dist/languages/kn.js +3 -2
  56. package/dist/languages/kn.js.map +1 -1
  57. package/dist/languages/ko.js +3 -2
  58. package/dist/languages/ko.js.map +1 -1
  59. package/dist/languages/lt.js +3 -2
  60. package/dist/languages/lt.js.map +1 -1
  61. package/dist/languages/lv.js +3 -2
  62. package/dist/languages/lv.js.map +1 -1
  63. package/dist/languages/mr.js +3 -2
  64. package/dist/languages/mr.js.map +1 -1
  65. package/dist/languages/ms.js +3 -2
  66. package/dist/languages/ms.js.map +1 -1
  67. package/dist/languages/nb.js +3 -2
  68. package/dist/languages/nb.js.map +1 -1
  69. package/dist/languages/nl.js +3 -2
  70. package/dist/languages/nl.js.map +1 -1
  71. package/dist/languages/pa.js +3 -0
  72. package/dist/languages/pa.js.map +1 -0
  73. package/dist/languages/pl.js +3 -2
  74. package/dist/languages/pl.js.map +1 -1
  75. package/dist/languages/pt.js +3 -2
  76. package/dist/languages/pt.js.map +1 -1
  77. package/dist/languages/ro.js +3 -2
  78. package/dist/languages/ro.js.map +1 -1
  79. package/dist/languages/ru.js +3 -2
  80. package/dist/languages/ru.js.map +1 -1
  81. package/dist/languages/sr-Cyrl.js +3 -0
  82. package/dist/languages/sr-Cyrl.js.map +1 -0
  83. package/dist/languages/sr-Latn.js +3 -2
  84. package/dist/languages/sr-Latn.js.map +1 -1
  85. package/dist/languages/sv.js +3 -2
  86. package/dist/languages/sv.js.map +1 -1
  87. package/dist/languages/sw.js +3 -2
  88. package/dist/languages/sw.js.map +1 -1
  89. package/dist/languages/ta.js +3 -2
  90. package/dist/languages/ta.js.map +1 -1
  91. package/dist/languages/te.js +3 -2
  92. package/dist/languages/te.js.map +1 -1
  93. package/dist/languages/th.js +3 -2
  94. package/dist/languages/th.js.map +1 -1
  95. package/dist/languages/tr.js +3 -2
  96. package/dist/languages/tr.js.map +1 -1
  97. package/dist/languages/uk.js +3 -2
  98. package/dist/languages/uk.js.map +1 -1
  99. package/dist/languages/ur.js +3 -2
  100. package/dist/languages/ur.js.map +1 -1
  101. package/dist/languages/vi.js +3 -2
  102. package/dist/languages/vi.js.map +1 -1
  103. package/dist/languages/zh-Hans.js +3 -2
  104. package/dist/languages/zh-Hans.js.map +1 -1
  105. package/dist/languages/zh-Hant.js +3 -0
  106. package/dist/languages/zh-Hant.js.map +1 -0
  107. package/dist/n2words.js +3 -2
  108. package/dist/n2words.js.map +1 -1
  109. package/lib/languages/am-Latn.d.ts +7 -0
  110. package/lib/languages/am-Latn.js +164 -0
  111. package/lib/languages/am.d.ts +7 -0
  112. package/lib/languages/am.js +164 -0
  113. package/lib/languages/ar.d.ts +17 -0
  114. package/lib/languages/ar.js +171 -209
  115. package/lib/languages/az.d.ts +7 -0
  116. package/lib/languages/az.js +167 -49
  117. package/lib/languages/bn.d.ts +7 -0
  118. package/lib/languages/bn.js +142 -123
  119. package/lib/languages/cs.d.ts +18 -0
  120. package/lib/languages/cs.js +303 -176
  121. package/lib/languages/da.d.ts +14 -0
  122. package/lib/languages/da.js +267 -139
  123. package/lib/languages/de.d.ts +17 -0
  124. package/lib/languages/de.js +310 -113
  125. package/lib/languages/el.d.ts +14 -0
  126. package/lib/languages/el.js +225 -98
  127. package/lib/languages/en.d.ts +17 -0
  128. package/lib/languages/en.js +235 -102
  129. package/lib/languages/es.d.ts +21 -0
  130. package/lib/languages/es.js +307 -125
  131. package/lib/languages/fa.d.ts +7 -0
  132. package/lib/languages/fa.js +115 -108
  133. package/lib/languages/fi.d.ts +14 -0
  134. package/lib/languages/fi.js +245 -0
  135. package/lib/languages/fil.d.ts +7 -0
  136. package/lib/languages/fil.js +199 -139
  137. package/lib/languages/fr-BE.d.ts +11 -0
  138. package/lib/languages/fr-BE.js +287 -48
  139. package/lib/languages/fr.d.ts +21 -0
  140. package/lib/languages/fr.js +343 -119
  141. package/lib/languages/gu.d.ts +7 -0
  142. package/lib/languages/gu.js +125 -144
  143. package/lib/languages/ha.d.ts +7 -0
  144. package/lib/languages/ha.js +230 -0
  145. package/lib/languages/hbo.d.ts +13 -0
  146. package/lib/languages/hbo.js +300 -0
  147. package/lib/languages/he.d.ts +13 -0
  148. package/lib/languages/he.js +230 -283
  149. package/lib/languages/hi.d.ts +7 -0
  150. package/lib/languages/hi.js +142 -123
  151. package/lib/languages/hr.d.ts +11 -0
  152. package/lib/languages/hr.js +190 -129
  153. package/lib/languages/hu.d.ts +7 -0
  154. package/lib/languages/hu.js +194 -133
  155. package/lib/languages/id.d.ts +7 -0
  156. package/lib/languages/id.js +167 -140
  157. package/lib/languages/it.d.ts +19 -0
  158. package/lib/languages/it.js +337 -108
  159. package/lib/languages/ja.d.ts +17 -0
  160. package/lib/languages/ja.js +224 -155
  161. package/lib/languages/kn.d.ts +7 -0
  162. package/lib/languages/kn.js +128 -62
  163. package/lib/languages/ko.d.ts +14 -0
  164. package/lib/languages/ko.js +250 -70
  165. package/lib/languages/lt.d.ts +18 -0
  166. package/lib/languages/lt.js +287 -148
  167. package/lib/languages/lv.d.ts +18 -0
  168. package/lib/languages/lv.js +291 -123
  169. package/lib/languages/mr.d.ts +7 -0
  170. package/lib/languages/mr.js +125 -144
  171. package/lib/languages/ms.d.ts +7 -0
  172. package/lib/languages/ms.js +171 -112
  173. package/lib/languages/nb.d.ts +14 -0
  174. package/lib/languages/nb.js +275 -100
  175. package/lib/languages/nl.d.ts +26 -0
  176. package/lib/languages/nl.js +307 -174
  177. package/lib/languages/pa.d.ts +7 -0
  178. package/lib/languages/pa.js +163 -0
  179. package/lib/languages/pl.d.ts +22 -0
  180. package/lib/languages/pl.js +299 -158
  181. package/lib/languages/pt.d.ts +17 -0
  182. package/lib/languages/pt.js +279 -120
  183. package/lib/languages/ro.d.ts +18 -0
  184. package/lib/languages/ro.js +214 -337
  185. package/lib/languages/ru.d.ts +11 -0
  186. package/lib/languages/ru.js +219 -95
  187. package/lib/languages/sr-Cyrl.d.ts +11 -0
  188. package/lib/languages/sr-Cyrl.js +215 -0
  189. package/lib/languages/sr-Latn.d.ts +11 -0
  190. package/lib/languages/sr-Latn.js +190 -132
  191. package/lib/languages/sv.d.ts +14 -0
  192. package/lib/languages/sv.js +280 -103
  193. package/lib/languages/sw.d.ts +7 -0
  194. package/lib/languages/sw.js +135 -103
  195. package/lib/languages/ta.d.ts +7 -0
  196. package/lib/languages/ta.js +133 -205
  197. package/lib/languages/te.d.ts +7 -0
  198. package/lib/languages/te.js +148 -213
  199. package/lib/languages/th.d.ts +7 -0
  200. package/lib/languages/th.js +139 -101
  201. package/lib/languages/tr.d.ts +18 -0
  202. package/lib/languages/tr.js +246 -66
  203. package/lib/languages/uk.d.ts +11 -0
  204. package/lib/languages/uk.js +197 -101
  205. package/lib/languages/ur.d.ts +7 -0
  206. package/lib/languages/ur.js +160 -123
  207. package/lib/languages/vi.d.ts +17 -0
  208. package/lib/languages/vi.js +287 -164
  209. package/lib/languages/zh-Hans.d.ts +11 -0
  210. package/lib/languages/zh-Hans.js +159 -142
  211. package/lib/languages/zh-Hant.d.ts +11 -0
  212. package/lib/languages/zh-Hant.js +202 -0
  213. package/lib/n2words.d.ts +53 -0
  214. package/lib/n2words.js +91 -227
  215. package/lib/utils/is-plain-object.d.ts +13 -0
  216. package/lib/utils/is-plain-object.js +17 -0
  217. package/lib/utils/parse-numeric.d.ts +17 -0
  218. package/lib/utils/parse-numeric.js +108 -0
  219. package/lib/utils/validate-options.d.ts +8 -0
  220. package/lib/utils/validate-options.js +16 -0
  221. package/package.json +118 -67
  222. package/dist/languages/pa-Guru.js +0 -2
  223. package/dist/languages/pa-Guru.js.map +0 -1
  224. package/lib/classes/abstract-language.js +0 -261
  225. package/lib/classes/greedy-scale-language.js +0 -195
  226. package/lib/classes/slavic-language.js +0 -251
  227. package/lib/classes/south-asian-language.js +0 -161
  228. package/lib/classes/turkic-language.js +0 -63
  229. package/lib/languages/pa-Guru.js +0 -126
  230. package/typings/classes/abstract-language.d.ts +0 -144
  231. package/typings/classes/greedy-scale-language.d.ts +0 -148
  232. package/typings/classes/slavic-language.d.ts +0 -145
  233. package/typings/classes/south-asian-language.d.ts +0 -101
  234. package/typings/classes/turkic-language.d.ts +0 -42
  235. package/typings/languages/ar.d.ts +0 -93
  236. package/typings/languages/az.d.ts +0 -25
  237. package/typings/languages/bn.d.ts +0 -1
  238. package/typings/languages/cs.d.ts +0 -120
  239. package/typings/languages/da.d.ts +0 -53
  240. package/typings/languages/de.d.ts +0 -26
  241. package/typings/languages/el.d.ts +0 -11
  242. package/typings/languages/en.d.ts +0 -30
  243. package/typings/languages/es.d.ts +0 -43
  244. package/typings/languages/fa.d.ts +0 -81
  245. package/typings/languages/fil.d.ts +0 -12
  246. package/typings/languages/fr-BE.d.ts +0 -41
  247. package/typings/languages/fr.d.ts +0 -43
  248. package/typings/languages/gu.d.ts +0 -12
  249. package/typings/languages/he.d.ts +0 -197
  250. package/typings/languages/hi.d.ts +0 -1
  251. package/typings/languages/hr.d.ts +0 -110
  252. package/typings/languages/hu.d.ts +0 -37
  253. package/typings/languages/id.d.ts +0 -69
  254. package/typings/languages/it.d.ts +0 -51
  255. package/typings/languages/ja.d.ts +0 -58
  256. package/typings/languages/kn.d.ts +0 -11
  257. package/typings/languages/ko.d.ts +0 -25
  258. package/typings/languages/lt.d.ts +0 -110
  259. package/typings/languages/lv.d.ts +0 -99
  260. package/typings/languages/mr.d.ts +0 -12
  261. package/typings/languages/ms.d.ts +0 -37
  262. package/typings/languages/nb.d.ts +0 -27
  263. package/typings/languages/nl.d.ts +0 -65
  264. package/typings/languages/pa-Guru.d.ts +0 -1
  265. package/typings/languages/pl.d.ts +0 -116
  266. package/typings/languages/pt.d.ts +0 -39
  267. package/typings/languages/ro.d.ts +0 -229
  268. package/typings/languages/ru.d.ts +0 -108
  269. package/typings/languages/sr-Latn.d.ts +0 -98
  270. package/typings/languages/sv.d.ts +0 -30
  271. package/typings/languages/sw.d.ts +0 -1
  272. package/typings/languages/ta.d.ts +0 -1
  273. package/typings/languages/te.d.ts +0 -1
  274. package/typings/languages/th.d.ts +0 -1
  275. package/typings/languages/tr.d.ts +0 -46
  276. package/typings/languages/uk.d.ts +0 -117
  277. package/typings/languages/ur.d.ts +0 -1
  278. package/typings/languages/vi.d.ts +0 -116
  279. package/typings/languages/zh-Hans.d.ts +0 -57
  280. package/typings/n2words.d.ts +0 -177
@@ -1,165 +1,182 @@
1
- import GreedyScaleLanguage from '../classes/greedy-scale-language.js'
2
-
3
1
  /**
4
- * @typedef {Object} ChineseOptions
5
- * @property {boolean} [formal=true] Use formal/financial numerals (壹贰叁) vs. common numerals (一二三).
2
+ * Simplified Chinese language converter - Functional Implementation
3
+ *
4
+ * Self-contained converter for Simplified Chinese.
5
+ *
6
+ * Key features:
7
+ * - Myriad-based (万, 亿) grouping - 4 digits
8
+ * - Formal (financial) vs common numerals
9
+ * - Zero insertion for skipped positions
10
+ * - No word separators (concatenated format)
6
11
  */
7
12
 
13
+ import { parseNumericValue } from '../utils/parse-numeric.js'
14
+ import { validateOptions } from '../utils/validate-options.js'
15
+
16
+ // ============================================================================
17
+ // Vocabulary
18
+ // ============================================================================
19
+
20
+ // Common (everyday) numerals
21
+ const ONES_COMMON = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']
22
+ const TEN_COMMON = '十'
23
+ const HUNDRED_COMMON = '百'
24
+ const THOUSAND_COMMON = '千'
25
+
26
+ // Formal (financial) numerals - harder to alter/forge
27
+ const ONES_FORMAL = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
28
+ const TEN_FORMAL = '拾'
29
+ const HUNDRED_FORMAL = '佰'
30
+ const THOUSAND_FORMAL = '仟'
31
+
32
+ // Scale words
33
+ const WAN_WORD = '万' // 10,000
34
+ const YI_WORD = '亿' // 100,000,000
35
+
36
+ const ZERO = '零'
37
+ const NEGATIVE = '负'
38
+ const DECIMAL_SEP = '点'
39
+
40
+ // ============================================================================
41
+ // Conversion Functions
42
+ // ============================================================================
43
+
8
44
  /**
9
- * Chinese language converter.
10
- *
11
- * Features:
12
- * - Concatenated number-word format (no spaces)
13
- * - Implicit zero insertion for positional values
14
- * - Decimal digits pronounced individually
15
- * - Supports both formal (financial) and common (everyday) styles
45
+ * Convert number below 万 (10,000) to words using direct string concatenation.
16
46
  */
17
- export class Chinese extends GreedyScaleLanguage {
18
- negativeWord = ''
19
- decimalSeparatorWord = '点'
20
- zeroWord = '零'
21
- wordSeparator = ''
22
-
23
- /**
24
- * Initializes the Chinese converter.
25
- *
26
- * @param {ChineseOptions} [options={}] Configuration options.
27
- */
28
- constructor ({ formal = true } = {}) {
29
- super()
30
-
31
- this.formal = formal
32
-
33
- if (this.formal) {
34
- this.scaleWordPairs = [
35
- [1_000_000_000_000n, '万'],
36
- [100_000_000n, '亿'],
37
- [10_000n, '万'],
38
- [1000n, '仟'],
39
- [100n, '佰'],
40
- [10n, '拾'],
41
- [9n, '玖'],
42
- [8n, '捌'],
43
- [7n, '柒'],
44
- [6n, '陆'],
45
- [5n, '伍'],
46
- [4n, '肆'],
47
- [3n, '叁'],
48
- [2n, '贰'],
49
- [1n, '壹'],
50
- [0n, '零']
51
- ]
52
- } else {
53
- this.scaleWordPairs = [
54
- [1_000_000_000_000n, '万'],
55
- [100_000_000n, '亿'],
56
- [10_000n, '万'],
57
- [1000n, '千'],
58
- [100n, '百'],
59
- [10n, '十'],
60
- [9n, '九'],
61
- [8n, '八'],
62
- [7n, '七'],
63
- [6n, '六'],
64
- [5n, '五'],
65
- [4n, '四'],
66
- [3n, '三'],
67
- [2n, '二'],
68
- [1n, '一'],
69
- [0n, '零']
70
- ]
71
- }
72
- }
47
+ function convertBelowWan (value, ones, ten, hundred, thousand) {
48
+ if (value === 0n) return ''
73
49
 
74
- /**
75
- * Merges two adjacent word-number pairs according to Chinese grammar rules.
76
- *
77
- * Chinese-specific rules:
78
- * - Omits '一' (or '壹' in formal style) before single digits (< 10)
79
- * - Concatenates without space
80
- * - Inserts '零' (zero) when positional values skip magnitude levels
81
- * - Multiplies when right > left, adds otherwise
82
- *
83
- * @param {Object} leftPair The left operand as `{ word: number }`.
84
- * @param {Object} rightPair The right operand as `{ word: number }`.
85
- * @returns {Object} Merged pair with combined word and resulting number.
86
- */
87
- mergeScales (leftPair, rightPair) {
88
- const leftWord = Object.keys(leftPair)[0]
89
- const leftNumber = Object.values(leftPair)[0]
90
- const rightWord = Object.keys(rightPair)[0]
91
- const rightNumber = Object.values(rightPair)[0]
92
-
93
- // Implicit one: omit 1 before single digits (< 10)
94
- if (leftNumber === 1n && rightNumber < 10n) {
95
- return rightPair
96
- }
50
+ let result = ''
51
+ let needsZero = false
97
52
 
98
- // Multiply when right > left (scale words like 千, 万, 亿)
99
- if (rightNumber > leftNumber) {
100
- return { [`${leftWord}${rightWord}`]: leftNumber * rightNumber }
101
- }
53
+ // Thousands ()
54
+ const thousandsVal = value / 1000n
55
+ const thousandsRemainder = value % 1000n
56
+ if (thousandsVal > 0n) {
57
+ result = ones[Number(thousandsVal)] + thousand
58
+ needsZero = thousandsRemainder > 0n && thousandsRemainder < 100n
59
+ }
102
60
 
103
- // Insert "零" (zero) when position skip levels (e.g., 1003 = 千零三)
104
- // zeroDigit() checks if gap exists between left and right magnitude
105
- if (this.zeroDigit(leftNumber) > this.digit(rightNumber)) {
106
- return { [`${leftWord}${this.zeroWord}${rightWord}`]: leftNumber + rightNumber }
107
- }
61
+ // Hundreds ()
62
+ const hundredsVal = thousandsRemainder / 100n
63
+ const hundredsRemainder = thousandsRemainder % 100n
64
+ if (hundredsVal > 0n) {
65
+ if (needsZero) result += ZERO
66
+ result += ones[Number(hundredsVal)] + hundred
67
+ needsZero = hundredsRemainder > 0n && hundredsRemainder < 10n
68
+ } else if (thousandsVal > 0n && hundredsRemainder > 0n) {
69
+ needsZero = true
70
+ }
108
71
 
109
- // Default: concatenate without zero insertion
110
- return { [`${leftWord}${rightWord}`]: leftNumber + rightNumber }
72
+ // Tens (十)
73
+ const tensVal = hundredsRemainder / 10n
74
+ const onesVal = hundredsRemainder % 10n
75
+ if (tensVal > 0n) {
76
+ if (needsZero) result += ZERO
77
+ result += ones[Number(tensVal)] + ten
78
+ needsZero = false
79
+ } else if ((hundredsVal > 0n || thousandsVal > 0n) && onesVal > 0n) {
80
+ needsZero = true
111
81
  }
112
82
 
113
- /**
114
- * Get the number of digits in a number.
115
- *
116
- * @param {bigint|number} number_ The number to count digits for.
117
- * @returns {number} The count of digits.
118
- */
119
- digit (number_) {
120
- return number_.toString().length
83
+ // Ones
84
+ if (onesVal > 0n) {
85
+ if (needsZero) result += ZERO
86
+ result += ones[Number(onesVal)]
121
87
  }
122
88
 
123
- /**
124
- * Count the number of zeros in a number.
125
- *
126
- * @param {bigint|number} number_ The number to count zeros in.
127
- * @returns {number} The count of zero digits.
128
- */
129
- zeroDigit (number_) {
130
- return [...number_.toString()].filter(c => c === '0').length
89
+ return result
90
+ }
91
+
92
+ /**
93
+ * Convert number below 亿 (100 million) to words.
94
+ */
95
+ function convertBelowYi (value, ones, ten, hundred, thousand) {
96
+ if (value === 0n) return ''
97
+
98
+ if (value >= 10_000n) {
99
+ const wanValue = value / 10_000n
100
+ const wanRemainder = value % 10_000n
101
+
102
+ let result = convertBelowWan(wanValue, ones, ten, hundred, thousand) + WAN_WORD
103
+
104
+ if (wanRemainder > 0n) {
105
+ const needsZero = (wanValue % 10n === 0n) || (wanRemainder < 1000n)
106
+ if (needsZero) result += ZERO
107
+ result += convertBelowWan(wanRemainder, ones, ten, hundred, thousand)
108
+ }
109
+
110
+ return result
131
111
  }
132
112
 
133
- /**
134
- * Convert decimal digits to words by reading each digit individually.
135
- * Overrides the default grouped behavior from AbstractLanguage.
136
- *
137
- * @param {string} decimalString The decimal digits as a string.
138
- * @returns {Array<string>} Array of individual digit words.
139
- */
140
- decimalDigitsToWords (decimalString) {
141
- const words = []
142
- for (let i = 0; i < decimalString.length; i++) {
143
- const digitValue = BigInt(decimalString[i])
144
- words.push(this.convertWholePart(digitValue))
113
+ return convertBelowWan(value, ones, ten, hundred, thousand)
114
+ }
115
+
116
+ function integerToWords (n, formal = true) {
117
+ if (n === 0n) return ZERO
118
+
119
+ const ones = formal ? ONES_FORMAL : ONES_COMMON
120
+ const ten = formal ? TEN_FORMAL : TEN_COMMON
121
+ const hundred = formal ? HUNDRED_FORMAL : HUNDRED_COMMON
122
+ const thousand = formal ? THOUSAND_FORMAL : THOUSAND_COMMON
123
+
124
+ // Handle numbers >= 亿 (100 million)
125
+ if (n >= 100_000_000n) {
126
+ const yiValue = n / 100_000_000n
127
+ const yiRemainder = n % 100_000_000n
128
+
129
+ let result = convertBelowYi(yiValue, ones, ten, hundred, thousand) + YI_WORD
130
+
131
+ if (yiRemainder > 0n) {
132
+ if (yiRemainder < 10_000_000n) result += ZERO
133
+ result += convertBelowYi(yiRemainder, ones, ten, hundred, thousand)
145
134
  }
146
- return words
135
+
136
+ return result
147
137
  }
138
+
139
+ return convertBelowYi(n, ones, ten, hundred, thousand)
148
140
  }
149
141
 
150
142
  /**
151
- * Converts a number to Chinese cardinal (written) form.
152
- *
153
- * @param {number|string|bigint} value The number to convert.
154
- * @param {ChineseOptions} [options] Conversion options.
155
- * @returns {string} The number expressed in Chinese words.
156
- * @throws {TypeError} If value is NaN or invalid type.
157
- * @throws {Error} If value is an invalid number string.
143
+ * Convert decimal digits to words using direct concatenation.
144
+ */
145
+ function decimalDigitsToWords (decimalString, ones) {
146
+ let result = ''
147
+ for (let i = 0; i < decimalString.length; i++) {
148
+ result += ones[Number(decimalString[i])]
149
+ }
150
+ return result
151
+ }
152
+
153
+ /**
154
+ * Converts a numeric value to Simplified Chinese words.
158
155
  *
159
- * @example
160
- * convertToWords(42); // '肆拾贰' (formal style)
161
- * convertToWords(42, { formal: false }); // '四十二' (common style)
156
+ * @param {number | string | bigint} value - The numeric value to convert
157
+ * @param {Object} [options] - Optional configuration
158
+ * @param {boolean} [options.formal=true] - Use formal/financial numerals
159
+ * @returns {string} The number in Simplified Chinese words
162
160
  */
163
- export default function convertToWords (value, options = {}) {
164
- return new Chinese(options).convertToWords(value)
161
+ function toWords (value, options) {
162
+ options = validateOptions(options)
163
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
164
+ const formal = options.formal !== false // Default to true
165
+
166
+ let result = isNegative ? NEGATIVE : ''
167
+
168
+ result += integerToWords(integerPart, formal)
169
+
170
+ if (decimalPart) {
171
+ const ones = formal ? ONES_FORMAL : ONES_COMMON
172
+ result += DECIMAL_SEP + decimalDigitsToWords(decimalPart, ones)
173
+ }
174
+
175
+ return result
165
176
  }
177
+
178
+ // ============================================================================
179
+ // Exports
180
+ // ============================================================================
181
+
182
+ export { toWords }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Converts a numeric value to Traditional Chinese words.
3
+ *
4
+ * @param {number | string | bigint} value - The numeric value to convert
5
+ * @param {Object} [options] - Optional configuration
6
+ * @param {boolean} [options.formal=true] - Use formal/financial numerals
7
+ * @returns {string} The number in Traditional Chinese words
8
+ */
9
+ export function toWords(value: number | string | bigint, options?: {
10
+ formal?: boolean | undefined;
11
+ }): string;
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Traditional Chinese language converter - Functional Implementation
3
+ *
4
+ * Self-contained converter for Traditional Chinese.
5
+ *
6
+ * Key features:
7
+ * - Myriad-based (萬, 億) grouping - 4 digits
8
+ * - Formal (financial) vs common numerals
9
+ * - Zero insertion for skipped positions
10
+ * - No word separators (concatenated format)
11
+ *
12
+ * Differences from Simplified:
13
+ * - Different character forms (e.g., 負/负, 點/点, 億/亿, 萬/万)
14
+ * - Some formal numerals differ (參/叁, 貳/贰, 陸/陆)
15
+ */
16
+
17
+ import { parseNumericValue } from '../utils/parse-numeric.js'
18
+ import { validateOptions } from '../utils/validate-options.js'
19
+
20
+ // ============================================================================
21
+ // Vocabulary
22
+ // ============================================================================
23
+
24
+ // Common (everyday) numerals - Traditional forms
25
+ const ONES_COMMON = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']
26
+ const TEN_COMMON = '十'
27
+ const HUNDRED_COMMON = '百'
28
+ const THOUSAND_COMMON = '千'
29
+
30
+ // Formal (financial) numerals - Traditional forms
31
+ const ONES_FORMAL = ['零', '壹', '貳', '參', '肆', '伍', '陸', '柒', '捌', '玖']
32
+ const TEN_FORMAL = '拾'
33
+ const HUNDRED_FORMAL = '佰'
34
+ const THOUSAND_FORMAL = '仟'
35
+
36
+ // Scale words - Traditional forms
37
+ const WAN_WORD = '萬' // 10,000
38
+ const YI_WORD = '億' // 100,000,000
39
+
40
+ const ZERO = '零'
41
+ const NEGATIVE = '負'
42
+ const DECIMAL_SEP = '點'
43
+
44
+ // ============================================================================
45
+ // Conversion Functions
46
+ // ============================================================================
47
+
48
+ function integerToWords (n, formal = true) {
49
+ if (n === 0n) return ZERO
50
+
51
+ const ones = formal ? ONES_FORMAL : ONES_COMMON
52
+ const ten = formal ? TEN_FORMAL : TEN_COMMON
53
+ const hundred = formal ? HUNDRED_FORMAL : HUNDRED_COMMON
54
+ const thousand = formal ? THOUSAND_FORMAL : THOUSAND_COMMON
55
+
56
+ // Convert number below 萬 (10,000)
57
+ function convertBelowWan (value) {
58
+ if (value === 0n) return ''
59
+
60
+ const parts = []
61
+ let needsZero = false
62
+
63
+ // Thousands (千)
64
+ const thousandsVal = value / 1000n
65
+ const thousandsRemainder = value % 1000n
66
+ if (thousandsVal > 0n) {
67
+ parts.push(ones[Number(thousandsVal)] + thousand)
68
+ needsZero = thousandsRemainder > 0n && thousandsRemainder < 100n
69
+ }
70
+
71
+ // Hundreds (百)
72
+ const hundredsVal = thousandsRemainder / 100n
73
+ const hundredsRemainder = thousandsRemainder % 100n
74
+ if (hundredsVal > 0n) {
75
+ if (needsZero) {
76
+ parts.push(ZERO)
77
+ needsZero = false
78
+ }
79
+ parts.push(ones[Number(hundredsVal)] + hundred)
80
+ needsZero = hundredsRemainder > 0n && hundredsRemainder < 10n
81
+ } else if (thousandsVal > 0n && hundredsRemainder > 0n) {
82
+ needsZero = true
83
+ }
84
+
85
+ // Tens (十)
86
+ const tensVal = hundredsRemainder / 10n
87
+ const onesVal = hundredsRemainder % 10n
88
+ if (tensVal > 0n) {
89
+ if (needsZero) {
90
+ parts.push(ZERO)
91
+ needsZero = false
92
+ }
93
+ parts.push(ones[Number(tensVal)] + ten)
94
+ } else if ((hundredsVal > 0n || thousandsVal > 0n) && onesVal > 0n) {
95
+ needsZero = true
96
+ }
97
+
98
+ // Ones
99
+ if (onesVal > 0n) {
100
+ if (needsZero) {
101
+ parts.push(ZERO)
102
+ }
103
+ parts.push(ones[Number(onesVal)])
104
+ }
105
+
106
+ return parts.join('')
107
+ }
108
+
109
+ // Convert number below 億 (100 million)
110
+ function convertBelowYi (value) {
111
+ if (value === 0n) return ''
112
+
113
+ const parts = []
114
+
115
+ if (value >= 10_000n) {
116
+ const wanValue = value / 10_000n
117
+ const wanRemainder = value % 10_000n
118
+
119
+ parts.push(convertBelowWan(wanValue) + WAN_WORD)
120
+
121
+ if (wanRemainder > 0n) {
122
+ const wanEndsWithZero = wanValue % 10n === 0n
123
+ const remainderMissesThousands = wanRemainder < 1000n
124
+ const needsZero = wanEndsWithZero || remainderMissesThousands
125
+ if (needsZero) {
126
+ parts.push(ZERO)
127
+ }
128
+ parts.push(convertBelowWan(wanRemainder))
129
+ }
130
+ } else {
131
+ parts.push(convertBelowWan(value))
132
+ }
133
+
134
+ return parts.join('')
135
+ }
136
+
137
+ // Main conversion
138
+ const parts = []
139
+
140
+ if (n >= 100_000_000n) {
141
+ const yiValue = n / 100_000_000n
142
+ const yiRemainder = n % 100_000_000n
143
+
144
+ const yiWords = convertBelowYi(yiValue)
145
+ parts.push(yiWords + YI_WORD)
146
+
147
+ if (yiRemainder > 0n) {
148
+ const needsZero = yiRemainder < 10_000_000n
149
+ if (needsZero) {
150
+ parts.push(ZERO)
151
+ }
152
+ parts.push(convertBelowYi(yiRemainder))
153
+ }
154
+ } else {
155
+ parts.push(convertBelowYi(n))
156
+ }
157
+
158
+ return parts.join('')
159
+ }
160
+
161
+ function decimalDigitsToWords (decimalString, formal = true) {
162
+ const ones = formal ? ONES_FORMAL : ONES_COMMON
163
+ const words = []
164
+ for (const char of decimalString) {
165
+ words.push(ones[Number(char)])
166
+ }
167
+ return words
168
+ }
169
+
170
+ /**
171
+ * Converts a numeric value to Traditional Chinese words.
172
+ *
173
+ * @param {number | string | bigint} value - The numeric value to convert
174
+ * @param {Object} [options] - Optional configuration
175
+ * @param {boolean} [options.formal=true] - Use formal/financial numerals
176
+ * @returns {string} The number in Traditional Chinese words
177
+ */
178
+ function toWords (value, options) {
179
+ options = validateOptions(options)
180
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
181
+ const formal = options.formal !== false // Default to true
182
+
183
+ let result = ''
184
+
185
+ if (isNegative) {
186
+ result = NEGATIVE
187
+ }
188
+
189
+ result += integerToWords(integerPart, formal)
190
+
191
+ if (decimalPart) {
192
+ result += DECIMAL_SEP + decimalDigitsToWords(decimalPart, formal).join('')
193
+ }
194
+
195
+ return result
196
+ }
197
+
198
+ // ============================================================================
199
+ // Exports
200
+ // ============================================================================
201
+
202
+ export { toWords }
@@ -0,0 +1,53 @@
1
+ import { toWords as am } from './languages/am.js';
2
+ import { toWords as amLatn } from './languages/am-Latn.js';
3
+ import { toWords as ar } from './languages/ar.js';
4
+ import { toWords as az } from './languages/az.js';
5
+ import { toWords as bn } from './languages/bn.js';
6
+ import { toWords as cs } from './languages/cs.js';
7
+ import { toWords as da } from './languages/da.js';
8
+ import { toWords as de } from './languages/de.js';
9
+ import { toWords as el } from './languages/el.js';
10
+ import { toWords as en } from './languages/en.js';
11
+ import { toWords as es } from './languages/es.js';
12
+ import { toWords as fa } from './languages/fa.js';
13
+ import { toWords as fi } from './languages/fi.js';
14
+ import { toWords as fil } from './languages/fil.js';
15
+ import { toWords as fr } from './languages/fr.js';
16
+ import { toWords as frBE } from './languages/fr-BE.js';
17
+ import { toWords as gu } from './languages/gu.js';
18
+ import { toWords as ha } from './languages/ha.js';
19
+ import { toWords as hbo } from './languages/hbo.js';
20
+ import { toWords as he } from './languages/he.js';
21
+ import { toWords as hi } from './languages/hi.js';
22
+ import { toWords as hr } from './languages/hr.js';
23
+ import { toWords as hu } from './languages/hu.js';
24
+ import { toWords as id } from './languages/id.js';
25
+ import { toWords as it } from './languages/it.js';
26
+ import { toWords as ja } from './languages/ja.js';
27
+ import { toWords as kn } from './languages/kn.js';
28
+ import { toWords as ko } from './languages/ko.js';
29
+ import { toWords as lt } from './languages/lt.js';
30
+ import { toWords as lv } from './languages/lv.js';
31
+ import { toWords as mr } from './languages/mr.js';
32
+ import { toWords as ms } from './languages/ms.js';
33
+ import { toWords as nb } from './languages/nb.js';
34
+ import { toWords as nl } from './languages/nl.js';
35
+ import { toWords as pa } from './languages/pa.js';
36
+ import { toWords as pl } from './languages/pl.js';
37
+ import { toWords as pt } from './languages/pt.js';
38
+ import { toWords as ro } from './languages/ro.js';
39
+ import { toWords as ru } from './languages/ru.js';
40
+ import { toWords as srCyrl } from './languages/sr-Cyrl.js';
41
+ import { toWords as srLatn } from './languages/sr-Latn.js';
42
+ import { toWords as sv } from './languages/sv.js';
43
+ import { toWords as sw } from './languages/sw.js';
44
+ import { toWords as ta } from './languages/ta.js';
45
+ import { toWords as te } from './languages/te.js';
46
+ import { toWords as th } from './languages/th.js';
47
+ import { toWords as tr } from './languages/tr.js';
48
+ import { toWords as uk } from './languages/uk.js';
49
+ import { toWords as ur } from './languages/ur.js';
50
+ import { toWords as vi } from './languages/vi.js';
51
+ import { toWords as zhHans } from './languages/zh-Hans.js';
52
+ import { toWords as zhHant } from './languages/zh-Hant.js';
53
+ export { am, amLatn, ar, az, bn, cs, da, de, el, en, es, fa, fi, fil, fr, frBE, gu, ha, hbo, he, hi, hr, hu, id, it, ja, kn, ko, lt, lv, mr, ms, nb, nl, pa, pl, pt, ro, ru, srCyrl, srLatn, sv, sw, ta, te, th, tr, uk, ur, vi, zhHans, zhHant };