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,162 +1,222 @@
1
1
  /**
2
- * Filipino (Tagalog) language implementation for n2words
2
+ * Filipino language converter - Functional Implementation
3
3
  *
4
- * Uses scale-based system with irregular patterns for numbers 1-19.
5
- * Filipino numbers follow patterns like: sampu (10), labinisa (11), dalawampu (20)
4
+ * Self-contained converter with precomputed lookup tables.
6
5
  *
7
- * @module lib/languages/tl
8
- * @example
9
- * import fil from './lib/languages/fil.js'
10
- * tl(42) // 'apatnapu dalawa'
11
- * tl(1000) // 'isang libo'
6
+ * Key features:
7
+ * - Linker "ng" after vowels: "isang daang" (100)
8
+ * - Linker " na " after consonants: "siyam na daang" (900)
9
+ * - Special tens with linker: "limampung anim" (56)
10
+ * - Per-digit decimal reading
12
11
  */
13
12
 
14
- import GreedyScaleLanguage from '../classes/greedy-scale-language.js'
13
+ import { parseNumericValue } from '../utils/parse-numeric.js'
14
+
15
+ // ============================================================================
16
+ // Vocabulary
17
+ // ============================================================================
18
+
19
+ const ONES = ['', 'isa', 'dalawa', 'tatlo', 'apat', 'lima', 'anim', 'pito', 'walo', 'siyam']
20
+ const TEENS = ['sampu', 'labinisa', 'labindalawa', 'labintatlo', 'labinapat', 'labinlima', 'labinanum', 'labimpito', 'labingwalo', 'labinsiyam']
21
+ const TENS = ['', '', 'dalawampu', 'tatlumpu', 'apatnapu', 'limampu', 'animnapu', 'pitumpu', 'walumpu', 'siyamnapu']
22
+
23
+ // Scale words include linker (end with "ng")
24
+ const HUNDRED = 'daang'
25
+ const THOUSAND = 'libong'
26
+
27
+ const ZERO = 'zero'
28
+ const NEGATIVE = 'negatibo'
29
+ const DECIMAL_SEP = 'punto'
30
+
31
+ // Short scale with linker
32
+ const SCALE_WORDS = ['', THOUSAND, 'milyong', 'bilyong', 'trilyong']
33
+
34
+ // ============================================================================
35
+ // Helper Functions
36
+ // ============================================================================
37
+
38
+ const VOWELS = ['a', 'e', 'i', 'o', 'u']
39
+
40
+ function addLinker (word) {
41
+ const lastChar = word[word.length - 1]
42
+ if (VOWELS.includes(lastChar)) {
43
+ return word + 'ng'
44
+ }
45
+ return word + ' na'
46
+ }
47
+
48
+ // ============================================================================
49
+ // Precomputed Lookup Table
50
+ // ============================================================================
51
+
52
+ function buildSegment (n) {
53
+ if (n === 0) return ''
54
+
55
+ const ones = n % 10
56
+ const tensDigit = Math.floor(n / 10) % 10
57
+ const hundredsDigit = Math.floor(n / 100)
58
+
59
+ const parts = []
60
+
61
+ // Hundreds: "isang daan", "dalawang daan", "siyam na daan"
62
+ if (hundredsDigit > 0) {
63
+ const hundredPrefix = addLinker(ONES[hundredsDigit])
64
+ parts.push(hundredPrefix + ' ' + HUNDRED)
65
+ }
66
+
67
+ // Tens and ones
68
+ const tensOnes = n % 100
69
+
70
+ if (tensOnes === 0) {
71
+ // Just hundreds
72
+ } else if (tensOnes < 10) {
73
+ // Single digit
74
+ parts.push(ONES[ones])
75
+ } else if (tensOnes < 20) {
76
+ // Teens (10-19)
77
+ parts.push(TEENS[ones])
78
+ } else if (ones === 0) {
79
+ // Even tens (20, 30, 40, etc.)
80
+ parts.push(TENS[tensDigit])
81
+ } else {
82
+ // Tens + ones
83
+ // limampu (50) gets special linker: "limampung anim" (56)
84
+ if (tensDigit === 5) {
85
+ parts.push(TENS[tensDigit] + 'ng ' + ONES[ones])
86
+ } else {
87
+ parts.push(TENS[tensDigit] + ' ' + ONES[ones])
88
+ }
89
+ }
90
+
91
+ return parts.join(' ')
92
+ }
93
+
94
+ const SEGMENTS = new Array(1000)
95
+ for (let i = 0; i < 1000; i++) {
96
+ SEGMENTS[i] = buildSegment(i)
97
+ }
15
98
 
16
99
  /**
17
- * Filipino language implementation
18
- * Extends GreedyScaleLanguage with scale-based number conversion
100
+ * Builds segment with linker added to last word (for use before scale words).
19
101
  */
20
- class FilipinoLanguage extends GreedyScaleLanguage {
21
- negativeWord = 'negatibo'
22
- decimalSeparatorWord = 'punto'
23
- zeroWord = 'zero'
24
- convertDecimalsPerDigit = true // Read decimals digit-by-digit
25
-
26
- scaleWordPairs = [
27
- [1000000000000n, 'trilyong'],
28
- [1000000000n, 'milyong'],
29
- [1000000n, 'milyong'],
30
- [1000n, 'libong'],
31
- [100n, 'daang'],
32
-
33
- // Tens
34
- [90n, 'siyamnapu'],
35
- [80n, 'walumpu'],
36
- [70n, 'pitumpu'],
37
- [60n, 'animnapu'],
38
- [50n, 'limampu'],
39
- [40n, 'apatnapu'],
40
- [30n, 'tatlumpu'],
41
- [20n, 'dalawampu'],
42
-
43
- // Teens (must come before 10 to be matched first)
44
- [19n, 'labinsiyam'],
45
- [18n, 'labingwalo'],
46
- [17n, 'labimpito'],
47
- [16n, 'labinanum'],
48
- [15n, 'labinlima'],
49
- [14n, 'labinapat'],
50
- [13n, 'labintatlo'],
51
- [12n, 'labindalawa'],
52
- [11n, 'labinisa'],
53
- [10n, 'sampu'],
54
-
55
- // Ones
56
- [9n, 'siyam'],
57
- [8n, 'walo'],
58
- [7n, 'pito'],
59
- [6n, 'anim'],
60
- [5n, 'lima'],
61
- [4n, 'apat'],
62
- [3n, 'tatlo'],
63
- [2n, 'dalawa'],
64
- [1n, 'isa'],
65
- [0n, 'zero']
66
- ]
67
-
68
- /**
69
- * Convert a whole number to Filipino words.
70
- * Overrides parent to handle zero explicitly.
71
- *
72
- * @param {bigint} wholeNumber The whole number to convert.
73
- * @returns {string} The Filipino representation.
74
- */
75
- convertWholePart (wholeNumber) {
76
- // Handle zero explicitly
77
- if (wholeNumber === 0n) {
78
- return this.zeroWord
102
+ function buildSegmentWithLinker (n) {
103
+ const segmentWord = SEGMENTS[n]
104
+ if (!segmentWord) return ''
105
+
106
+ // Find the last space to get the last word
107
+ const lastSpaceIdx = segmentWord.lastIndexOf(' ')
108
+ if (lastSpaceIdx === -1) {
109
+ // Single word
110
+ const lastChar = segmentWord[segmentWord.length - 1]
111
+ if (lastChar === 'g' && segmentWord.endsWith('ng')) {
112
+ return segmentWord // Already has linker
79
113
  }
80
- return super.convertWholePart(wholeNumber)
114
+ return addLinker(segmentWord)
81
115
  }
82
116
 
83
- /**
84
- * Merge two word-sets according to Filipino grammar rules.
85
- *
86
- * Features Filipino-specific rules:
87
- * - "ng" connector between words
88
- * - Consonant-ending words use "na" instead of "ng"
89
- * - Implicit "one" omission ("isang daan" → "daan")
90
- *
91
- * @param {Object} leftWordSet Left word-set as `{ word: BigInt }`.
92
- * @param {Object} rightWordSet Right word-set as `{ word: BigInt }`.
93
- * @returns {Object} Merged word-set with combined text and value.
94
- */
95
- mergeScales (leftWordSet, rightWordSet) {
96
- const leftWord = Object.keys(leftWordSet)[0]
97
- const rightWord = Object.keys(rightWordSet)[0]
98
- const leftValue = Object.values(leftWordSet)[0]
99
- const rightValue = Object.values(rightWordSet)[0]
100
-
101
- // Don't merge zero with anything - just return the non-zero value
102
- if (leftValue === 0n) {
103
- return rightWordSet
104
- }
105
- if (rightValue === 0n) {
106
- return leftWordSet
107
- }
117
+ // Multi-word: add linker to last word
118
+ const prefix = segmentWord.slice(0, lastSpaceIdx + 1)
119
+ const lastWord = segmentWord.slice(lastSpaceIdx + 1)
108
120
 
109
- // Implicit "one" - omit when adding with values < 100
110
- if (leftValue === 1n && rightValue < 100n) {
111
- return rightWordSet
112
- }
121
+ if (lastWord.endsWith('ng')) {
122
+ return segmentWord // Already has linker
123
+ }
124
+ return prefix + addLinker(lastWord)
125
+ }
113
126
 
114
- // Multiply when right is a scale word AND right > left
115
- // Use "ng" connector for Filipino, but consonant-ending words use " na "
116
- if (rightValue > leftValue && rightValue >= 100n) {
117
- // Words ending in consonants (not vowels) use " na " instead of "ng"
118
- const vowels = ['a', 'e', 'i', 'o', 'u']
119
- const lastChar = leftWord[leftWord.length - 1]
120
- if (!vowels.includes(lastChar)) {
121
- return {
122
- [`${leftWord} na ${rightWord}`]: leftValue * rightValue
123
- }
124
- }
125
- // Vowel-ending words add "ng"
126
- return {
127
- [`${leftWord}ng ${rightWord}`]: leftValue * rightValue
128
- }
129
- }
127
+ // Precompute segments with linker for scale word usage
128
+ const SEGMENTS_WITH_LINKER = new Array(1000)
129
+ for (let i = 0; i < 1000; i++) {
130
+ SEGMENTS_WITH_LINKER[i] = buildSegmentWithLinker(i)
131
+ }
130
132
 
131
- // Special Filipino rule: certain tens words take "-ng" linker when followed by ones
132
- // Only limampu (50) confirmed to use this pattern
133
- if (leftValue >= 10n && leftValue < 100n && rightValue >= 1n && rightValue < 10n) {
134
- const tensWithNg = ['limampu']
135
- if (tensWithNg.includes(leftWord)) {
136
- return {
137
- [`${leftWord}ng ${rightWord}`]: leftValue + rightValue
138
- }
139
- }
140
- }
133
+ // ============================================================================
134
+ // Conversion Functions
135
+ // ============================================================================
136
+
137
+ function integerToWords (n) {
138
+ if (n === 0n) return ZERO
139
+
140
+ if (n < 1000n) {
141
+ return SEGMENTS[Number(n)]
142
+ }
143
+
144
+ return buildLargeNumberWords(n)
145
+ }
146
+
147
+ /**
148
+ * Builds words for large numbers using BigInt division.
149
+ * @param {bigint} n - Number >= 1000
150
+ * @returns {string} Filipino words
151
+ */
152
+ function buildLargeNumberWords (n) {
153
+ // Extract segments using BigInt division (faster than string slicing)
154
+ const segmentValues = []
155
+ let temp = n
156
+ while (temp > 0n) {
157
+ segmentValues.push(Number(temp % 1000n))
158
+ temp = temp / 1000n
159
+ }
160
+
161
+ // Build result string directly
162
+ let result = ''
163
+
164
+ for (let i = segmentValues.length - 1; i >= 0; i--) {
165
+ const segment = segmentValues[i]
166
+ if (segment === 0) continue
167
+
168
+ const scaleWord = SCALE_WORDS[i] || ''
141
169
 
142
- // Default: space for addition
143
- return {
144
- [`${leftWord} ${rightWord}`]: leftValue + rightValue
170
+ if (result) result += ' '
171
+
172
+ if (i === 0) {
173
+ result += SEGMENTS[segment]
174
+ } else {
175
+ // Add linker to segment before scale word
176
+ const segmentWord = SEGMENTS_WITH_LINKER[segment]
177
+ result += segmentWord + ' ' + scaleWord
145
178
  }
146
179
  }
180
+
181
+ return result
182
+ }
183
+
184
+ function decimalPartToWords (decimalPart) {
185
+ // Per-digit decimal reading
186
+ const digits = []
187
+ for (const char of decimalPart) {
188
+ const d = parseInt(char, 10)
189
+ digits.push(d === 0 ? ZERO : ONES[d])
190
+ }
191
+ return digits.join(' ')
147
192
  }
148
193
 
149
194
  /**
150
- * Convert a number to Filipino words
195
+ * Converts a numeric value to Filipino words.
151
196
  *
152
- * @param {number|string|bigint} value - The number to convert
153
- * @param {Object} [options={}] - Conversion options
154
- * @returns {string} The Filipino word representation
155
- * @example
156
- * convertToWords(42) // 'apatnapu dalawa'
157
- * convertToWords(1000) // 'isang libo'
158
- * convertToWords(123456) // 'isang daang dalawampung tatlong libong apat na daang limampung anim'
197
+ * @param {number | string | bigint} value - The numeric value to convert
198
+ * @returns {string} The number in Filipino words
159
199
  */
160
- export default function convertToWords (value, options = {}) {
161
- return new FilipinoLanguage(options).convertToWords(value)
200
+ function toWords (value) {
201
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
202
+
203
+ let result = ''
204
+
205
+ if (isNegative) {
206
+ result = NEGATIVE + ' '
207
+ }
208
+
209
+ result += integerToWords(integerPart)
210
+
211
+ if (decimalPart) {
212
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
213
+ }
214
+
215
+ return result
162
216
  }
217
+
218
+ // ============================================================================
219
+ // Exports
220
+ // ============================================================================
221
+
222
+ export { toWords }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Converts a numeric value to Belgian French words.
3
+ *
4
+ * @param {number | string | bigint} value - The numeric value to convert
5
+ * @param {Object} [options] - Optional configuration
6
+ * @param {boolean} [options.withHyphenSeparator=false] - Use hyphens between words
7
+ * @returns {string} The number in Belgian French words
8
+ */
9
+ export function toWords(value: number | string | bigint, options?: {
10
+ withHyphenSeparator?: boolean | undefined;
11
+ }): string;