n2words 2.0.0 → 3.1.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 (335) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/README.md +86 -188
  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 -0
  8. package/dist/languages/ar.js.map +1 -0
  9. package/dist/languages/az.js +3 -0
  10. package/dist/languages/az.js.map +1 -0
  11. package/dist/languages/bn.js +3 -0
  12. package/dist/languages/bn.js.map +1 -0
  13. package/dist/languages/cs.js +3 -0
  14. package/dist/languages/cs.js.map +1 -0
  15. package/dist/languages/da.js +3 -0
  16. package/dist/languages/da.js.map +1 -0
  17. package/dist/languages/de.js +3 -0
  18. package/dist/languages/de.js.map +1 -0
  19. package/dist/languages/el.js +3 -0
  20. package/dist/languages/el.js.map +1 -0
  21. package/dist/languages/en.js +3 -0
  22. package/dist/languages/en.js.map +1 -0
  23. package/dist/languages/es.js +3 -0
  24. package/dist/languages/es.js.map +1 -0
  25. package/dist/languages/fa.js +3 -0
  26. package/dist/languages/fa.js.map +1 -0
  27. package/dist/languages/fi.js +3 -0
  28. package/dist/languages/fi.js.map +1 -0
  29. package/dist/languages/fil.js +3 -0
  30. package/dist/languages/fil.js.map +1 -0
  31. package/dist/languages/fr-BE.js +3 -0
  32. package/dist/languages/fr-BE.js.map +1 -0
  33. package/dist/languages/fr.js +3 -0
  34. package/dist/languages/fr.js.map +1 -0
  35. package/dist/languages/gu.js +3 -0
  36. package/dist/languages/gu.js.map +1 -0
  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 -0
  42. package/dist/languages/he.js.map +1 -0
  43. package/dist/languages/hi.js +3 -0
  44. package/dist/languages/hi.js.map +1 -0
  45. package/dist/languages/hr.js +3 -0
  46. package/dist/languages/hr.js.map +1 -0
  47. package/dist/languages/hu.js +3 -0
  48. package/dist/languages/hu.js.map +1 -0
  49. package/dist/languages/id.js +3 -0
  50. package/dist/languages/id.js.map +1 -0
  51. package/dist/languages/it.js +3 -0
  52. package/dist/languages/it.js.map +1 -0
  53. package/dist/languages/ja.js +3 -0
  54. package/dist/languages/ja.js.map +1 -0
  55. package/dist/languages/ka.js +3 -0
  56. package/dist/languages/ka.js.map +1 -0
  57. package/dist/languages/kn.js +3 -0
  58. package/dist/languages/kn.js.map +1 -0
  59. package/dist/languages/ko.js +3 -0
  60. package/dist/languages/ko.js.map +1 -0
  61. package/dist/languages/lt.js +3 -0
  62. package/dist/languages/lt.js.map +1 -0
  63. package/dist/languages/lv.js +3 -0
  64. package/dist/languages/lv.js.map +1 -0
  65. package/dist/languages/mr.js +3 -0
  66. package/dist/languages/mr.js.map +1 -0
  67. package/dist/languages/ms.js +3 -0
  68. package/dist/languages/ms.js.map +1 -0
  69. package/dist/languages/nb.js +3 -0
  70. package/dist/languages/nb.js.map +1 -0
  71. package/dist/languages/nl.js +3 -0
  72. package/dist/languages/nl.js.map +1 -0
  73. package/dist/languages/pa.js +3 -0
  74. package/dist/languages/pa.js.map +1 -0
  75. package/dist/languages/pl.js +3 -0
  76. package/dist/languages/pl.js.map +1 -0
  77. package/dist/languages/pt.js +3 -0
  78. package/dist/languages/pt.js.map +1 -0
  79. package/dist/languages/ro.js +3 -0
  80. package/dist/languages/ro.js.map +1 -0
  81. package/dist/languages/ru.js +3 -0
  82. package/dist/languages/ru.js.map +1 -0
  83. package/dist/languages/sr-Cyrl.js +3 -0
  84. package/dist/languages/sr-Cyrl.js.map +1 -0
  85. package/dist/languages/sr-Latn.js +3 -0
  86. package/dist/languages/sr-Latn.js.map +1 -0
  87. package/dist/languages/sv.js +3 -0
  88. package/dist/languages/sv.js.map +1 -0
  89. package/dist/languages/sw.js +3 -0
  90. package/dist/languages/sw.js.map +1 -0
  91. package/dist/languages/ta.js +3 -0
  92. package/dist/languages/ta.js.map +1 -0
  93. package/dist/languages/te.js +3 -0
  94. package/dist/languages/te.js.map +1 -0
  95. package/dist/languages/th.js +3 -0
  96. package/dist/languages/th.js.map +1 -0
  97. package/dist/languages/tr.js +3 -0
  98. package/dist/languages/tr.js.map +1 -0
  99. package/dist/languages/uk.js +3 -0
  100. package/dist/languages/uk.js.map +1 -0
  101. package/dist/languages/ur.js +3 -0
  102. package/dist/languages/ur.js.map +1 -0
  103. package/dist/languages/vi.js +3 -0
  104. package/dist/languages/vi.js.map +1 -0
  105. package/dist/languages/yo.js +3 -0
  106. package/dist/languages/yo.js.map +1 -0
  107. package/dist/languages/zh-Hans.js +3 -0
  108. package/dist/languages/zh-Hans.js.map +1 -0
  109. package/dist/languages/zh-Hant.js +3 -0
  110. package/dist/languages/zh-Hant.js.map +1 -0
  111. package/dist/n2words.js +2 -2
  112. package/dist/n2words.js.map +1 -1
  113. package/lib/languages/am-Latn.d.ts +7 -0
  114. package/lib/languages/am-Latn.js +159 -0
  115. package/lib/languages/am.d.ts +7 -0
  116. package/lib/languages/am.js +159 -0
  117. package/lib/languages/ar.d.ts +14 -27
  118. package/lib/languages/ar.js +175 -129
  119. package/lib/languages/az.d.ts +4 -9
  120. package/lib/languages/az.js +166 -37
  121. package/lib/languages/bn.d.ts +4 -8
  122. package/lib/languages/bn.js +159 -124
  123. package/lib/languages/cs.d.ts +15 -85
  124. package/lib/languages/cs.js +293 -114
  125. package/lib/languages/da.d.ts +11 -12
  126. package/lib/languages/da.js +269 -101
  127. package/lib/languages/de.d.ts +14 -11
  128. package/lib/languages/de.js +305 -86
  129. package/lib/languages/el.d.ts +11 -11
  130. package/lib/languages/el.js +224 -78
  131. package/lib/languages/en.d.ts +14 -13
  132. package/lib/languages/en.js +228 -72
  133. package/lib/languages/es.d.ts +18 -12
  134. package/lib/languages/es.js +297 -103
  135. package/lib/languages/fa.d.ts +4 -44
  136. package/lib/languages/fa.js +112 -122
  137. package/lib/languages/fi.d.ts +14 -0
  138. package/lib/languages/fi.js +238 -0
  139. package/lib/languages/fil.d.ts +4 -13
  140. package/lib/languages/fil.js +196 -106
  141. package/lib/languages/fr-BE.d.ts +8 -8
  142. package/lib/languages/fr-BE.js +285 -19
  143. package/lib/languages/fr.d.ts +18 -12
  144. package/lib/languages/fr.js +339 -89
  145. package/lib/languages/gu.d.ts +4 -8
  146. package/lib/languages/gu.js +143 -125
  147. package/lib/languages/ha.d.ts +7 -0
  148. package/lib/languages/ha.js +225 -0
  149. package/lib/languages/hbo.d.ts +10 -110
  150. package/lib/languages/hbo.js +245 -214
  151. package/lib/languages/he.d.ts +10 -77
  152. package/lib/languages/he.js +231 -172
  153. package/lib/languages/hi.d.ts +4 -8
  154. package/lib/languages/hi.js +163 -124
  155. package/lib/languages/hr.d.ts +8 -77
  156. package/lib/languages/hr.js +200 -89
  157. package/lib/languages/hu.d.ts +4 -19
  158. package/lib/languages/hu.js +198 -119
  159. package/lib/languages/id.d.ts +4 -34
  160. package/lib/languages/id.js +166 -129
  161. package/lib/languages/it.d.ts +16 -34
  162. package/lib/languages/it.js +307 -97
  163. package/lib/languages/ja.d.ts +14 -14
  164. package/lib/languages/ja.js +221 -111
  165. package/lib/languages/ka.d.ts +17 -0
  166. package/lib/languages/ka.js +291 -0
  167. package/lib/languages/kn.d.ts +4 -8
  168. package/lib/languages/kn.js +143 -35
  169. package/lib/languages/ko.d.ts +11 -11
  170. package/lib/languages/ko.js +250 -49
  171. package/lib/languages/lt.d.ts +15 -67
  172. package/lib/languages/lt.js +287 -122
  173. package/lib/languages/lv.d.ts +15 -67
  174. package/lib/languages/lv.js +288 -106
  175. package/lib/languages/mr.d.ts +4 -8
  176. package/lib/languages/mr.js +143 -125
  177. package/lib/languages/ms.d.ts +4 -28
  178. package/lib/languages/ms.js +166 -116
  179. package/lib/languages/nb.d.ts +11 -9
  180. package/lib/languages/nb.js +272 -87
  181. package/lib/languages/nl.d.ts +23 -13
  182. package/lib/languages/nl.js +299 -133
  183. package/lib/languages/pa.d.ts +4 -8
  184. package/lib/languages/pa.js +151 -124
  185. package/lib/languages/pl.d.ts +19 -77
  186. package/lib/languages/pl.js +294 -87
  187. package/lib/languages/pt.d.ts +14 -26
  188. package/lib/languages/pt.js +272 -92
  189. package/lib/languages/ro.d.ts +15 -155
  190. package/lib/languages/ro.js +219 -235
  191. package/lib/languages/ru.d.ts +8 -82
  192. package/lib/languages/ru.js +239 -90
  193. package/lib/languages/sr-Cyrl.d.ts +8 -77
  194. package/lib/languages/sr-Cyrl.js +197 -89
  195. package/lib/languages/sr-Latn.d.ts +8 -77
  196. package/lib/languages/sr-Latn.js +197 -89
  197. package/lib/languages/sv.d.ts +11 -11
  198. package/lib/languages/sv.js +278 -74
  199. package/lib/languages/sw.d.ts +4 -36
  200. package/lib/languages/sw.js +133 -106
  201. package/lib/languages/ta.d.ts +4 -17
  202. package/lib/languages/ta.js +143 -202
  203. package/lib/languages/te.d.ts +4 -19
  204. package/lib/languages/te.js +133 -196
  205. package/lib/languages/th.d.ts +4 -14
  206. package/lib/languages/th.js +135 -91
  207. package/lib/languages/tr.d.ts +15 -9
  208. package/lib/languages/tr.js +245 -49
  209. package/lib/languages/uk.d.ts +8 -82
  210. package/lib/languages/uk.js +206 -78
  211. package/lib/languages/ur.d.ts +4 -8
  212. package/lib/languages/ur.js +151 -124
  213. package/lib/languages/vi.d.ts +14 -69
  214. package/lib/languages/vi.js +278 -129
  215. package/lib/languages/yo.d.ts +7 -0
  216. package/lib/languages/yo.js +303 -0
  217. package/lib/languages/zh-Hans.d.ts +8 -18
  218. package/lib/languages/zh-Hans.js +163 -92
  219. package/lib/languages/zh-Hant.d.ts +8 -18
  220. package/lib/languages/zh-Hant.js +181 -90
  221. package/lib/n2words.d.ts +55 -209
  222. package/lib/n2words.js +115 -530
  223. package/lib/utils/is-plain-object.d.ts +13 -0
  224. package/lib/utils/is-plain-object.js +17 -0
  225. package/lib/utils/parse-numeric.d.ts +17 -0
  226. package/lib/utils/parse-numeric.js +108 -0
  227. package/lib/utils/validate-options.d.ts +8 -0
  228. package/lib/utils/validate-options.js +16 -0
  229. package/package.json +26 -14
  230. package/dist/ArabicConverter.js +0 -3
  231. package/dist/ArabicConverter.js.map +0 -1
  232. package/dist/AzerbaijaniConverter.js +0 -3
  233. package/dist/AzerbaijaniConverter.js.map +0 -1
  234. package/dist/BanglaConverter.js +0 -3
  235. package/dist/BanglaConverter.js.map +0 -1
  236. package/dist/BiblicalHebrewConverter.js +0 -3
  237. package/dist/BiblicalHebrewConverter.js.map +0 -1
  238. package/dist/CroatianConverter.js +0 -3
  239. package/dist/CroatianConverter.js.map +0 -1
  240. package/dist/CzechConverter.js +0 -3
  241. package/dist/CzechConverter.js.map +0 -1
  242. package/dist/DanishConverter.js +0 -3
  243. package/dist/DanishConverter.js.map +0 -1
  244. package/dist/DutchConverter.js +0 -3
  245. package/dist/DutchConverter.js.map +0 -1
  246. package/dist/EnglishConverter.js +0 -3
  247. package/dist/EnglishConverter.js.map +0 -1
  248. package/dist/FilipinoConverter.js +0 -3
  249. package/dist/FilipinoConverter.js.map +0 -1
  250. package/dist/FrenchBelgiumConverter.js +0 -3
  251. package/dist/FrenchBelgiumConverter.js.map +0 -1
  252. package/dist/FrenchConverter.js +0 -3
  253. package/dist/FrenchConverter.js.map +0 -1
  254. package/dist/GermanConverter.js +0 -3
  255. package/dist/GermanConverter.js.map +0 -1
  256. package/dist/GreekConverter.js +0 -3
  257. package/dist/GreekConverter.js.map +0 -1
  258. package/dist/GujaratiConverter.js +0 -3
  259. package/dist/GujaratiConverter.js.map +0 -1
  260. package/dist/HebrewConverter.js +0 -3
  261. package/dist/HebrewConverter.js.map +0 -1
  262. package/dist/HindiConverter.js +0 -3
  263. package/dist/HindiConverter.js.map +0 -1
  264. package/dist/HungarianConverter.js +0 -3
  265. package/dist/HungarianConverter.js.map +0 -1
  266. package/dist/IndonesianConverter.js +0 -3
  267. package/dist/IndonesianConverter.js.map +0 -1
  268. package/dist/ItalianConverter.js +0 -3
  269. package/dist/ItalianConverter.js.map +0 -1
  270. package/dist/JapaneseConverter.js +0 -3
  271. package/dist/JapaneseConverter.js.map +0 -1
  272. package/dist/KannadaConverter.js +0 -3
  273. package/dist/KannadaConverter.js.map +0 -1
  274. package/dist/KoreanConverter.js +0 -3
  275. package/dist/KoreanConverter.js.map +0 -1
  276. package/dist/LatvianConverter.js +0 -3
  277. package/dist/LatvianConverter.js.map +0 -1
  278. package/dist/LithuanianConverter.js +0 -3
  279. package/dist/LithuanianConverter.js.map +0 -1
  280. package/dist/MalayConverter.js +0 -3
  281. package/dist/MalayConverter.js.map +0 -1
  282. package/dist/MarathiConverter.js +0 -3
  283. package/dist/MarathiConverter.js.map +0 -1
  284. package/dist/NorwegianBokmalConverter.js +0 -3
  285. package/dist/NorwegianBokmalConverter.js.map +0 -1
  286. package/dist/PersianConverter.js +0 -3
  287. package/dist/PersianConverter.js.map +0 -1
  288. package/dist/PolishConverter.js +0 -3
  289. package/dist/PolishConverter.js.map +0 -1
  290. package/dist/PortugueseConverter.js +0 -3
  291. package/dist/PortugueseConverter.js.map +0 -1
  292. package/dist/PunjabiConverter.js +0 -3
  293. package/dist/PunjabiConverter.js.map +0 -1
  294. package/dist/RomanianConverter.js +0 -3
  295. package/dist/RomanianConverter.js.map +0 -1
  296. package/dist/RussianConverter.js +0 -3
  297. package/dist/RussianConverter.js.map +0 -1
  298. package/dist/SerbianCyrillicConverter.js +0 -3
  299. package/dist/SerbianCyrillicConverter.js.map +0 -1
  300. package/dist/SerbianLatinConverter.js +0 -3
  301. package/dist/SerbianLatinConverter.js.map +0 -1
  302. package/dist/SimplifiedChineseConverter.js +0 -3
  303. package/dist/SimplifiedChineseConverter.js.map +0 -1
  304. package/dist/SpanishConverter.js +0 -3
  305. package/dist/SpanishConverter.js.map +0 -1
  306. package/dist/SwahiliConverter.js +0 -3
  307. package/dist/SwahiliConverter.js.map +0 -1
  308. package/dist/SwedishConverter.js +0 -3
  309. package/dist/SwedishConverter.js.map +0 -1
  310. package/dist/TamilConverter.js +0 -3
  311. package/dist/TamilConverter.js.map +0 -1
  312. package/dist/TeluguConverter.js +0 -3
  313. package/dist/TeluguConverter.js.map +0 -1
  314. package/dist/ThaiConverter.js +0 -3
  315. package/dist/ThaiConverter.js.map +0 -1
  316. package/dist/TraditionalChineseConverter.js +0 -3
  317. package/dist/TraditionalChineseConverter.js.map +0 -1
  318. package/dist/TurkishConverter.js +0 -3
  319. package/dist/TurkishConverter.js.map +0 -1
  320. package/dist/UkrainianConverter.js +0 -3
  321. package/dist/UkrainianConverter.js.map +0 -1
  322. package/dist/UrduConverter.js +0 -3
  323. package/dist/UrduConverter.js.map +0 -1
  324. package/dist/VietnameseConverter.js +0 -3
  325. package/dist/VietnameseConverter.js.map +0 -1
  326. package/lib/classes/abstract-language.d.ts +0 -178
  327. package/lib/classes/abstract-language.js +0 -268
  328. package/lib/classes/greedy-scale-language.d.ts +0 -109
  329. package/lib/classes/greedy-scale-language.js +0 -201
  330. package/lib/classes/slavic-language.d.ts +0 -148
  331. package/lib/classes/slavic-language.js +0 -281
  332. package/lib/classes/south-asian-language.d.ts +0 -70
  333. package/lib/classes/south-asian-language.js +0 -154
  334. package/lib/classes/turkic-language.d.ts +0 -26
  335. package/lib/classes/turkic-language.js +0 -59
@@ -0,0 +1,303 @@
1
+ /**
2
+ * Yoruba language converter - Functional Implementation
3
+ *
4
+ * A number-to-words converter for Yoruba (yo), a Niger-Congo language
5
+ * spoken by ~45 million people in Nigeria, Benin, and Togo.
6
+ *
7
+ * Yoruba uses a complex vigesimal (base-20) system with:
8
+ * - Additive patterns: 1-4 added to decade (lé = "plus")
9
+ * - Subtractive patterns: 5-9 subtracted from next decade (dín = "minus")
10
+ * - Odd decades (30,50,70,90) formed by subtracting 10 from next even decade
11
+ * - Even decades (20,40,60,80,100) are multiples of 20
12
+ *
13
+ * Examples:
14
+ * - 21 = ọ̀kan lé lógún (1 + 20)
15
+ * - 15 = àrùndínlógún (20 - 5)
16
+ * - 50 = àádọ́ta (60 - 10)
17
+ * - 45 = àrùndínláàádọ́ta (50 - 5)
18
+ */
19
+
20
+ import { parseNumericValue } from '../utils/parse-numeric.js'
21
+
22
+ // ============================================================================
23
+ // Vocabulary (module-level constants)
24
+ // ============================================================================
25
+
26
+ // Basic numbers 1-10
27
+ const ONES = [
28
+ '',
29
+ 'ọ̀kan', // 1
30
+ 'èjì', // 2
31
+ 'ẹ̀ta', // 3
32
+ 'ẹ̀rin', // 4
33
+ 'àrùn', // 5
34
+ 'ẹ̀fà', // 6
35
+ 'èje', // 7
36
+ 'ẹ̀jọ', // 8
37
+ 'ẹ̀sán', // 9
38
+ 'ẹ̀wá' // 10
39
+ ]
40
+
41
+ // Numbers 11-14 (additive: X + 10, using "lá")
42
+ const TEENS_ADD = [
43
+ '',
44
+ 'ọ̀kànlá', // 11 = 1 + 10
45
+ 'èjìlá', // 12 = 2 + 10
46
+ 'ẹ̀talá', // 13 = 3 + 10
47
+ 'ẹ̀rinlá' // 14 = 4 + 10
48
+ ]
49
+
50
+ // Numbers 15-19 (subtractive: 20 - X, using "dín")
51
+ const TEENS_SUB = [
52
+ 'àrùndínlógún', // 15 = 20 - 5
53
+ 'ẹ̀rìndínlógún', // 16 = 20 - 4
54
+ 'ẹ̀tadínlógún', // 17 = 20 - 3
55
+ 'èjìdínlógún', // 18 = 20 - 2
56
+ 'ọ̀kàndínlógún' // 19 = 20 - 1
57
+ ]
58
+
59
+ // Decades (base-20 structure)
60
+ // Even decades are multiples of 20
61
+ // Odd decades subtract 10 from next even decade
62
+ const DECADES = {
63
+ 20: 'ogún', // 20 = 20 × 1
64
+ 30: 'ọgbọ̀n', // 30 = 20 + 10 (special word)
65
+ 40: 'ogójì', // 40 = 20 × 2
66
+ 50: 'àádọ́ta', // 50 = 60 - 10
67
+ 60: 'ogóta', // 60 = 20 × 3
68
+ 70: 'àádọ́rin', // 70 = 80 - 10
69
+ 80: 'ogórin', // 80 = 20 × 4
70
+ 90: 'àádọ́rùn', // 90 = 100 - 10
71
+ 100: 'ọgọ́rùn' // 100 = 20 × 5
72
+ }
73
+
74
+ // Prefixes for adding to decades (lé lógún, lé lọgbọ̀n, etc.)
75
+ const DECADE_ADD_SUFFIX = {
76
+ 20: 'lógún',
77
+ 30: 'lọgbọ̀n',
78
+ 40: 'lógójì',
79
+ 50: 'láàádọ́ta',
80
+ 60: 'lógóta',
81
+ 70: 'láàádọ́rin',
82
+ 80: 'lógórin',
83
+ 90: 'láàádọ́rùn',
84
+ 100: 'lọ́gọ́rùn'
85
+ }
86
+
87
+ // Prefixes for subtracting from decades (dín lógójì, etc.)
88
+ const DECADE_SUB_SUFFIX = {
89
+ 20: 'dínlógún',
90
+ 30: 'dínlọgbọ̀n',
91
+ 40: 'dínlógójì',
92
+ 50: 'dínláàádọ́ta',
93
+ 60: 'dínlógóta',
94
+ 70: 'dínláàádọ́rin',
95
+ 80: 'dínlógórin',
96
+ 90: 'dínláàádọ́rùn',
97
+ 100: 'dínlọ́gọ́rùn'
98
+ }
99
+
100
+ // Scale words
101
+ const HUNDRED = 'ọgọ́rùn'
102
+ const TWO_HUNDRED = 'igba' // 200 (special word, historically 200 cowries)
103
+ const FOUR_HUNDRED = 'irinwó' // 400 (20 × 20)
104
+ const THOUSAND = 'ẹgbẹ̀rún' // 1000
105
+ const TEN_THOUSAND = 'ẹgbàárùn' // 10,000 (special)
106
+ const TWENTY_THOUSAND = 'ọ̀kẹ́' // 20,000 (bag of cowries)
107
+ const MILLION = 'mílíọ̀nù' // million (loanword)
108
+
109
+ const ZERO = 'òdo'
110
+ const NEGATIVE = 'àìní'
111
+ const DECIMAL_SEP = 'àmì'
112
+ const AND = 'ó lé' // "and" / "plus" connector
113
+
114
+ // ============================================================================
115
+ // Segment Building
116
+ // ============================================================================
117
+
118
+ /**
119
+ * Builds word for numbers 0-99
120
+ */
121
+ function buildUnder100 (n) {
122
+ if (n === 0) return ''
123
+ if (n <= 10) return ONES[n]
124
+
125
+ // 11-14: additive from 10
126
+ if (n <= 14) return TEENS_ADD[n - 10]
127
+
128
+ // 15-19: subtractive from 20
129
+ if (n <= 19) return TEENS_SUB[n - 15]
130
+
131
+ // Exact decades
132
+ if (n % 10 === 0) return DECADES[n]
133
+
134
+ const decade = Math.floor(n / 10) * 10
135
+ const unit = n % 10
136
+
137
+ // 1-4 are added to current decade (21 = 1 + 20, not 1 + 30)
138
+ if (unit <= 4) {
139
+ return ONES[unit] + ' lé ' + DECADE_ADD_SUFFIX[decade]
140
+ }
141
+
142
+ // 5-9 are subtracted from next decade
143
+ const nextDecade = decade + 10
144
+ const subtractAmount = 10 - unit
145
+ return ONES[subtractAmount] + DECADE_SUB_SUFFIX[nextDecade]
146
+ }
147
+
148
+ // ============================================================================
149
+ // Conversion Functions
150
+ // ============================================================================
151
+
152
+ /**
153
+ * Converts hundreds (100-999)
154
+ */
155
+ function convertHundreds (n) {
156
+ if (n < 100) return buildUnder100(n)
157
+
158
+ const hundreds = Math.floor(n / 100)
159
+ const remainder = n % 100
160
+
161
+ let result
162
+
163
+ // Special cases for 200 and 400
164
+ if (hundreds === 2 && remainder === 0) {
165
+ return TWO_HUNDRED
166
+ }
167
+ if (hundreds === 4 && remainder === 0) {
168
+ return FOUR_HUNDRED
169
+ }
170
+
171
+ // Build hundreds
172
+ if (hundreds === 1) {
173
+ result = HUNDRED
174
+ } else if (hundreds === 2) {
175
+ result = TWO_HUNDRED
176
+ } else if (hundreds === 4) {
177
+ result = FOUR_HUNDRED
178
+ } else {
179
+ // Other hundreds: X ọgọ́rùn
180
+ result = ONES[hundreds] + ' ' + HUNDRED
181
+ }
182
+
183
+ if (remainder > 0) {
184
+ result += ' ' + AND + ' ' + buildUnder100(remainder)
185
+ }
186
+
187
+ return result
188
+ }
189
+
190
+ /**
191
+ * Converts a non-negative integer to Yoruba words.
192
+ *
193
+ * @param {bigint} n - Non-negative integer to convert
194
+ * @returns {string} Yoruba words
195
+ */
196
+ function integerToWords (n) {
197
+ if (n === 0n) return ZERO
198
+
199
+ // Fast path: numbers < 100
200
+ if (n < 100n) {
201
+ return buildUnder100(Number(n))
202
+ }
203
+
204
+ // Numbers < 1000
205
+ if (n < 1000n) {
206
+ return convertHundreds(Number(n))
207
+ }
208
+
209
+ // Build from segments
210
+ const parts = []
211
+ let remaining = n
212
+
213
+ // Millions
214
+ if (remaining >= 1_000_000n) {
215
+ const millions = remaining / 1_000_000n
216
+ remaining = remaining % 1_000_000n
217
+ if (millions === 1n) {
218
+ parts.push(MILLION + ' kan')
219
+ } else {
220
+ parts.push(MILLION + ' ' + integerToWords(millions))
221
+ }
222
+ }
223
+
224
+ // Thousands
225
+ if (remaining >= 1000n) {
226
+ const thousands = remaining / 1000n
227
+ remaining = remaining % 1000n
228
+
229
+ if (thousands === 1n) {
230
+ parts.push(THOUSAND + ' kan')
231
+ } else if (thousands === 10n) {
232
+ parts.push(TEN_THOUSAND)
233
+ } else if (thousands === 20n) {
234
+ parts.push(TWENTY_THOUSAND)
235
+ } else if (thousands < 100n) {
236
+ parts.push(THOUSAND + ' ' + buildUnder100(Number(thousands)))
237
+ } else {
238
+ parts.push(THOUSAND + ' ' + convertHundreds(Number(thousands)))
239
+ }
240
+ }
241
+
242
+ // Hundreds and below
243
+ if (remaining > 0n) {
244
+ if (remaining < 100n) {
245
+ if (parts.length > 0) {
246
+ parts.push(AND + ' ' + buildUnder100(Number(remaining)))
247
+ } else {
248
+ parts.push(buildUnder100(Number(remaining)))
249
+ }
250
+ } else {
251
+ if (parts.length > 0) {
252
+ parts.push(AND + ' ' + convertHundreds(Number(remaining)))
253
+ } else {
254
+ parts.push(convertHundreds(Number(remaining)))
255
+ }
256
+ }
257
+ }
258
+
259
+ return parts.join(', ')
260
+ }
261
+
262
+ /**
263
+ * Converts decimal digits to Yoruba words.
264
+ *
265
+ * @param {string} decimalPart - Decimal digits
266
+ * @returns {string} Yoruba words for decimal
267
+ */
268
+ function decimalPartToWords (decimalPart) {
269
+ const parts = []
270
+
271
+ for (const digit of decimalPart) {
272
+ const d = parseInt(digit, 10)
273
+ parts.push(d === 0 ? ZERO : ONES[d])
274
+ }
275
+
276
+ return parts.join(' ')
277
+ }
278
+
279
+ /**
280
+ * Converts a numeric value to Yoruba words.
281
+ *
282
+ * @param {number | string | bigint} value - The numeric value to convert
283
+ * @returns {string} The number in Yoruba words
284
+ */
285
+ function toWords (value) {
286
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
287
+
288
+ let result = ''
289
+
290
+ if (isNegative) {
291
+ result = NEGATIVE + ' '
292
+ }
293
+
294
+ result += integerToWords(integerPart)
295
+
296
+ if (decimalPart) {
297
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
298
+ }
299
+
300
+ return result
301
+ }
302
+
303
+ export { toWords }
@@ -1,21 +1,11 @@
1
1
  /**
2
- * Simplified Chinese language converter.
2
+ * Converts a numeric value to Simplified Chinese words.
3
3
  *
4
- * Supports:
5
- * - Simplified Chinese characters (简体中文)
6
- * - No word separators (concatenated format)
7
- * - Optional formal (financial) vs common numerals
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 Simplified Chinese words
8
8
  */
9
- export class SimplifiedChinese extends GreedyScaleLanguage {
10
- constructor(options?: {});
11
- scaleWords: (string | bigint)[][];
12
- /** Combines two word-sets according to Chinese grammar rules. */
13
- combineWordSets(preceding: any, following: any): any;
14
- /** Returns the number of digits in a number. */
15
- digit(number_: any): any;
16
- /** Counts the number of zero digits in a number. */
17
- zeroDigit(number_: any): number;
18
- /** Converts decimal digits to words by reading each digit individually. */
19
- decimalDigitsToWords(decimalString: any): string[];
20
- }
21
- import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js';
9
+ export function toWords(value: number | string | bigint, options?: {
10
+ formal?: boolean | undefined;
11
+ }): string;
@@ -1,111 +1,182 @@
1
- import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'
2
-
3
1
  /**
4
- * Simplified Chinese language converter.
2
+ * Simplified Chinese language converter - Functional Implementation
3
+ *
4
+ * Self-contained converter for Simplified Chinese.
5
5
  *
6
- * Supports:
7
- * - Simplified Chinese characters (简体中文)
6
+ * Key features:
7
+ * - Myriad-based (万, 亿) grouping - 4 digits
8
+ * - Formal (financial) vs common numerals
9
+ * - Zero insertion for skipped positions
8
10
  * - No word separators (concatenated format)
9
- * - Optional formal (financial) vs common numerals
10
11
  */
11
- export class SimplifiedChinese extends GreedyScaleLanguage {
12
- negativeWord = ''
13
- decimalSeparatorWord = ''
14
- zeroWord = '零'
15
- wordSeparator = ''
16
-
17
- constructor (options = {}) {
18
- super()
19
-
20
- this.setOptions({
21
- formal: true
22
- }, options)
23
-
24
- if (this.options.formal) {
25
- this.scaleWords = [
26
- [1_000_000_000_000n, ''],
27
- [100_000_000n, '亿'],
28
- [10_000n, ''],
29
- [1000n, '仟'],
30
- [100n, '佰'],
31
- [10n, '拾'],
32
- [9n, ''],
33
- [8n, ''],
34
- [7n, '柒'],
35
- [6n, ''],
36
- [5n, ''],
37
- [4n, ''],
38
- [3n, '叁'],
39
- [2n, '贰'],
40
- [1n, '壹'],
41
- [0n, '零']
42
- ]
43
- } else {
44
- this.scaleWords = [
45
- [1_000_000_000_000n, '万'],
46
- [100_000_000n, '亿'],
47
- [10_000n, ''],
48
- [1000n, '千'],
49
- [100n, ''],
50
- [10n, '十'],
51
- [9n, '九'],
52
- [8n, '八'],
53
- [7n, '七'],
54
- [6n, '六'],
55
- [5n, '五'],
56
- [4n, '四'],
57
- [3n, '三'],
58
- [2n, '二'],
59
- [1n, '一'],
60
- [0n, '零']
61
- ]
62
- }
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
+
44
+ /**
45
+ * Convert number below 万 (10,000) to words using direct string concatenation.
46
+ */
47
+ function convertBelowWan (value, ones, ten, hundred, thousand) {
48
+ if (value === 0n) return ''
49
+
50
+ let result = ''
51
+ let needsZero = false
52
+
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
63
59
  }
64
60
 
65
- /** Combines two word-sets according to Chinese grammar rules. */
66
- combineWordSets (preceding, following) {
67
- const precedingWord = Object.keys(preceding)[0]
68
- const precedingValue = Object.values(preceding)[0]
69
- const followingWord = Object.keys(following)[0]
70
- const followingValue = Object.values(following)[0]
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
+ }
71
71
 
72
- // Implicit one: omit 1 before single digits (< 10)
73
- if (precedingValue === 1n && followingValue < 10n) {
74
- return following
75
- }
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
81
+ }
76
82
 
77
- // Multiply when following > preceding (scale words like 仁, 万, 亿)
78
- if (followingValue > precedingValue) {
79
- return { [`${precedingWord}${followingWord}`]: precedingValue * followingValue }
80
- }
83
+ // Ones
84
+ if (onesVal > 0n) {
85
+ if (needsZero) result += ZERO
86
+ result += ones[Number(onesVal)]
87
+ }
88
+
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 ''
81
97
 
82
- // Insert "零" (zero) when position skip levels (e.g., 1003 = 千零三)
83
- // zeroDigit() checks if gap exists between preceding and following magnitude
84
- if (this.zeroDigit(precedingValue) > this.digit(followingValue)) {
85
- return { [`${precedingWord}${this.zeroWord}${followingWord}`]: precedingValue + followingValue }
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)
86
108
  }
87
109
 
88
- // Default: concatenate without zero insertion
89
- return { [`${precedingWord}${followingWord}`]: precedingValue + followingValue }
110
+ return result
90
111
  }
91
112
 
92
- /** Returns the number of digits in a number. */
93
- digit (number_) {
94
- return number_.toString().length
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)
134
+ }
135
+
136
+ return result
95
137
  }
96
138
 
97
- /** Counts the number of zero digits in a number. */
98
- zeroDigit (number_) {
99
- return [...number_.toString()].filter(c => c === '0').length
139
+ return convertBelowYi(n, ones, ten, hundred, thousand)
140
+ }
141
+
142
+ /**
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])]
100
149
  }
150
+ return result
151
+ }
101
152
 
102
- /** Converts decimal digits to words by reading each digit individually. */
103
- decimalDigitsToWords (decimalString) {
104
- const words = []
105
- for (let i = 0; i < decimalString.length; i++) {
106
- const digitValue = BigInt(decimalString[i])
107
- words.push(this.integerToWords(digitValue))
108
- }
109
- return words
153
+ /**
154
+ * Converts a numeric value to Simplified Chinese words.
155
+ *
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
160
+ */
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)
110
173
  }
174
+
175
+ return result
111
176
  }
177
+
178
+ // ============================================================================
179
+ // Exports
180
+ // ============================================================================
181
+
182
+ export { toWords }
@@ -1,21 +1,11 @@
1
1
  /**
2
- * Traditional Chinese language converter.
2
+ * Converts a numeric value to Traditional Chinese words.
3
3
  *
4
- * Supports:
5
- * - Traditional Chinese characters (繁體中文)
6
- * - No word separators (concatenated format)
7
- * - Optional formal (financial) vs common numerals
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
8
  */
9
- export class TraditionalChinese extends GreedyScaleLanguage {
10
- constructor(options?: {});
11
- scaleWords: (string | bigint)[][];
12
- /** Combines two word-sets with Traditional Chinese grammar rules and zero insertion. */
13
- combineWordSets(preceding: any, following: any): any;
14
- /** Returns the number of digits in a number. */
15
- digit(number_: any): any;
16
- /** Counts the number of zero digits in a number. */
17
- zeroDigit(number_: any): number;
18
- /** Converts decimal digits to words by reading each digit individually. */
19
- decimalDigitsToWords(decimalString: any): string[];
20
- }
21
- import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js';
9
+ export function toWords(value: number | string | bigint, options?: {
10
+ formal?: boolean | undefined;
11
+ }): string;