n2words 3.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 (154) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/languages/am-Latn.js +2 -2
  3. package/dist/languages/am-Latn.js.map +1 -1
  4. package/dist/languages/am.js +2 -2
  5. package/dist/languages/am.js.map +1 -1
  6. package/dist/languages/ar.js +1 -1
  7. package/dist/languages/az.js +2 -2
  8. package/dist/languages/az.js.map +1 -1
  9. package/dist/languages/bn.js +2 -2
  10. package/dist/languages/bn.js.map +1 -1
  11. package/dist/languages/cs.js +2 -2
  12. package/dist/languages/cs.js.map +1 -1
  13. package/dist/languages/da.js +2 -2
  14. package/dist/languages/da.js.map +1 -1
  15. package/dist/languages/de.js +2 -2
  16. package/dist/languages/de.js.map +1 -1
  17. package/dist/languages/el.js +2 -2
  18. package/dist/languages/el.js.map +1 -1
  19. package/dist/languages/en.js +2 -2
  20. package/dist/languages/en.js.map +1 -1
  21. package/dist/languages/es.js +2 -2
  22. package/dist/languages/es.js.map +1 -1
  23. package/dist/languages/fa.js +1 -1
  24. package/dist/languages/fi.js +2 -2
  25. package/dist/languages/fi.js.map +1 -1
  26. package/dist/languages/fil.js +2 -2
  27. package/dist/languages/fil.js.map +1 -1
  28. package/dist/languages/fr-BE.js +2 -2
  29. package/dist/languages/fr-BE.js.map +1 -1
  30. package/dist/languages/fr.js +2 -2
  31. package/dist/languages/fr.js.map +1 -1
  32. package/dist/languages/gu.js +2 -2
  33. package/dist/languages/gu.js.map +1 -1
  34. package/dist/languages/ha.js +2 -2
  35. package/dist/languages/ha.js.map +1 -1
  36. package/dist/languages/hbo.js +2 -2
  37. package/dist/languages/hbo.js.map +1 -1
  38. package/dist/languages/he.js +2 -2
  39. package/dist/languages/he.js.map +1 -1
  40. package/dist/languages/hi.js +2 -2
  41. package/dist/languages/hi.js.map +1 -1
  42. package/dist/languages/hr.js +2 -2
  43. package/dist/languages/hr.js.map +1 -1
  44. package/dist/languages/hu.js +1 -1
  45. package/dist/languages/id.js +2 -2
  46. package/dist/languages/id.js.map +1 -1
  47. package/dist/languages/it.js +2 -2
  48. package/dist/languages/it.js.map +1 -1
  49. package/dist/languages/ja.js +2 -2
  50. package/dist/languages/ja.js.map +1 -1
  51. package/dist/languages/ka.js +3 -0
  52. package/dist/languages/ka.js.map +1 -0
  53. package/dist/languages/kn.js +2 -2
  54. package/dist/languages/kn.js.map +1 -1
  55. package/dist/languages/ko.js +2 -2
  56. package/dist/languages/ko.js.map +1 -1
  57. package/dist/languages/lt.js +2 -2
  58. package/dist/languages/lt.js.map +1 -1
  59. package/dist/languages/lv.js +2 -2
  60. package/dist/languages/lv.js.map +1 -1
  61. package/dist/languages/mr.js +2 -2
  62. package/dist/languages/mr.js.map +1 -1
  63. package/dist/languages/ms.js +2 -2
  64. package/dist/languages/ms.js.map +1 -1
  65. package/dist/languages/nb.js +2 -2
  66. package/dist/languages/nb.js.map +1 -1
  67. package/dist/languages/nl.js +2 -2
  68. package/dist/languages/nl.js.map +1 -1
  69. package/dist/languages/pa.js +2 -2
  70. package/dist/languages/pa.js.map +1 -1
  71. package/dist/languages/pl.js +2 -2
  72. package/dist/languages/pl.js.map +1 -1
  73. package/dist/languages/pt.js +2 -2
  74. package/dist/languages/pt.js.map +1 -1
  75. package/dist/languages/ro.js +1 -1
  76. package/dist/languages/ru.js +2 -2
  77. package/dist/languages/ru.js.map +1 -1
  78. package/dist/languages/sr-Cyrl.js +2 -2
  79. package/dist/languages/sr-Cyrl.js.map +1 -1
  80. package/dist/languages/sr-Latn.js +2 -2
  81. package/dist/languages/sr-Latn.js.map +1 -1
  82. package/dist/languages/sv.js +2 -2
  83. package/dist/languages/sv.js.map +1 -1
  84. package/dist/languages/sw.js +1 -1
  85. package/dist/languages/ta.js +2 -2
  86. package/dist/languages/ta.js.map +1 -1
  87. package/dist/languages/te.js +2 -2
  88. package/dist/languages/te.js.map +1 -1
  89. package/dist/languages/th.js +1 -1
  90. package/dist/languages/tr.js +2 -2
  91. package/dist/languages/tr.js.map +1 -1
  92. package/dist/languages/uk.js +2 -2
  93. package/dist/languages/uk.js.map +1 -1
  94. package/dist/languages/ur.js +2 -2
  95. package/dist/languages/ur.js.map +1 -1
  96. package/dist/languages/vi.js +2 -2
  97. package/dist/languages/vi.js.map +1 -1
  98. package/dist/languages/yo.js +3 -0
  99. package/dist/languages/yo.js.map +1 -0
  100. package/dist/languages/zh-Hans.js +1 -1
  101. package/dist/languages/zh-Hant.js +1 -1
  102. package/dist/n2words.js +2 -2
  103. package/dist/n2words.js.map +1 -1
  104. package/lib/languages/am-Latn.js +4 -9
  105. package/lib/languages/am.js +4 -9
  106. package/lib/languages/az.js +4 -9
  107. package/lib/languages/bn.js +51 -30
  108. package/lib/languages/cs.js +9 -26
  109. package/lib/languages/da.js +6 -13
  110. package/lib/languages/de.js +21 -33
  111. package/lib/languages/el.js +6 -13
  112. package/lib/languages/en.js +44 -58
  113. package/lib/languages/es.js +10 -30
  114. package/lib/languages/fi.js +6 -13
  115. package/lib/languages/fil.js +6 -17
  116. package/lib/languages/fr-BE.js +17 -26
  117. package/lib/languages/fr.js +18 -31
  118. package/lib/languages/gu.js +43 -30
  119. package/lib/languages/ha.js +4 -9
  120. package/lib/languages/hbo.js +7 -25
  121. package/lib/languages/he.js +7 -18
  122. package/lib/languages/hi.js +55 -30
  123. package/lib/languages/hr.js +61 -55
  124. package/lib/languages/id.js +8 -13
  125. package/lib/languages/it.js +64 -99
  126. package/lib/languages/ja.js +8 -20
  127. package/lib/languages/ka.d.ts +17 -0
  128. package/lib/languages/ka.js +291 -0
  129. package/lib/languages/kn.js +43 -30
  130. package/lib/languages/ko.js +6 -13
  131. package/lib/languages/lt.js +6 -15
  132. package/lib/languages/lv.js +6 -15
  133. package/lib/languages/mr.js +43 -30
  134. package/lib/languages/ms.js +8 -13
  135. package/lib/languages/nb.js +13 -23
  136. package/lib/languages/nl.js +11 -29
  137. package/lib/languages/pa.js +32 -37
  138. package/lib/languages/pl.js +7 -20
  139. package/lib/languages/pt.js +17 -31
  140. package/lib/languages/ru.js +64 -59
  141. package/lib/languages/sr-Cyrl.js +58 -52
  142. package/lib/languages/sr-Latn.js +58 -52
  143. package/lib/languages/sv.js +14 -24
  144. package/lib/languages/ta.js +55 -42
  145. package/lib/languages/te.js +33 -41
  146. package/lib/languages/tr.js +7 -18
  147. package/lib/languages/uk.js +61 -55
  148. package/lib/languages/ur.js +32 -37
  149. package/lib/languages/vi.js +23 -43
  150. package/lib/languages/yo.d.ts +7 -0
  151. package/lib/languages/yo.js +303 -0
  152. package/lib/n2words.d.ts +3 -1
  153. package/lib/n2words.js +4 -0
  154. package/package.json +1 -1
@@ -1,13 +1,9 @@
1
1
  /**
2
- * Vietnamese language converter - Functional Implementation v2
2
+ * Vietnamese language converter - Functional Implementation
3
3
  *
4
- * A performance-optimized number-to-words converter using precomputed lookup tables.
5
4
  * Self-contained module with its own input validation, ready for subpath exports.
6
5
  *
7
- * Key optimization: Precompute all segment values (0-999) at module load.
8
- * This eliminates all per-call string manipulation for segment conversion.
9
- *
10
- * Vietnamese-specific rules (handled in precomputation):
6
+ * Vietnamese-specific rules:
11
7
  * - Special pronunciation: "lăm" for 5 in tens position, "mốt" for final 1
12
8
  * - "Lẻ" (odd/extra) marker when tens place is zero after hundreds/scales
13
9
  * - Short scale system with Vietnamese words (nghìn, triệu, tỷ)
@@ -42,12 +38,11 @@ const MOT_FINAL = 'mốt' // 1 in tens position (21, 31, etc.)
42
38
  const LAM = 'lăm' // 5 in tens position (25, 35, etc.)
43
39
 
44
40
  // ============================================================================
45
- // Precomputed Lookup Tables (built once at module load)
41
+ // Segment Building
46
42
  // ============================================================================
47
43
 
48
44
  /**
49
45
  * Builds word for 0-99 with special forms (mốt, lăm).
50
- * Only used during table construction.
51
46
  */
52
47
  function buildBelowHundred (n) {
53
48
  if (n === 0) return ONES[0]
@@ -74,7 +69,6 @@ function buildBelowHundred (n) {
74
69
 
75
70
  /**
76
71
  * Builds segment word for 0-999.
77
- * Only used during table construction.
78
72
  */
79
73
  function buildSegment (n) {
80
74
  if (n === 0) return ''
@@ -101,37 +95,23 @@ function buildSegment (n) {
101
95
  } else {
102
96
  // 10-99 after hundreds
103
97
  if (result) result += ' '
104
- result += BELOW_100[remainder]
98
+ result += buildBelowHundred(remainder)
105
99
  }
106
100
  }
107
101
 
108
102
  return result
109
103
  }
110
104
 
111
- // Precompute all 100 below-hundred words (0-99)
112
- // BELOW_100[n] gives the Vietnamese word for n
113
- const BELOW_100 = new Array(100)
114
- for (let i = 0; i < 100; i++) {
115
- BELOW_100[i] = buildBelowHundred(i)
116
- }
117
-
118
- // Precompute all 1000 segment words (0-999)
119
- // SEGMENTS[n] gives the Vietnamese word for n within a segment
120
- const SEGMENTS = new Array(1000)
121
- for (let i = 0; i < 1000; i++) {
122
- SEGMENTS[i] = buildSegment(i)
123
- }
124
-
125
- // Precompute "lẻ" prefixed versions for small remainders after scale words
126
- // LE_SEGMENTS[n] gives "lẻ X" for n in range 1-99
127
- const LE_SEGMENTS = new Array(100)
128
- LE_SEGMENTS[0] = ''
129
- for (let i = 1; i < 10; i++) {
130
- // Use "năm" not "lăm" after lẻ
131
- LE_SEGMENTS[i] = LE + ' ' + (i === 5 ? 'năm' : ONES[i])
132
- }
133
- for (let i = 10; i < 100; i++) {
134
- LE_SEGMENTS[i] = LE + ' ' + BELOW_100[i]
105
+ /**
106
+ * Builds "lẻ" prefixed word for small remainders (1-99) after scale words.
107
+ */
108
+ function buildLeSegment (n) {
109
+ if (n === 0) return ''
110
+ if (n < 10) {
111
+ // Use "năm" not "lăm" after lẻ
112
+ return LE + ' ' + (n === 5 ? 'năm' : ONES[n])
113
+ }
114
+ return LE + ' ' + buildBelowHundred(n)
135
115
  }
136
116
 
137
117
  // ============================================================================
@@ -147,14 +127,14 @@ for (let i = 10; i < 100; i++) {
147
127
  function integerToWords (n) {
148
128
  if (n === 0n) return ZERO
149
129
 
150
- // Fast path: numbers < 100 (direct lookup)
130
+ // Fast path: numbers < 100
151
131
  if (n < 100n) {
152
- return BELOW_100[Number(n)]
132
+ return buildBelowHundred(Number(n))
153
133
  }
154
134
 
155
- // Fast path: numbers < 1000 (direct lookup)
135
+ // Fast path: numbers < 1000
156
136
  if (n < 1000n) {
157
- return SEGMENTS[Number(n)]
137
+ return buildSegment(Number(n))
158
138
  }
159
139
 
160
140
  // Fast path: numbers < 1,000,000 (thousands)
@@ -162,7 +142,7 @@ function integerToWords (n) {
162
142
  const thousands = Number(n / 1000n)
163
143
  const remainder = Number(n % 1000n)
164
144
 
165
- const thousandsWords = SEGMENTS[thousands] + ' ' + SCALES[1]
145
+ const thousandsWords = buildSegment(thousands) + ' ' + SCALES[1]
166
146
 
167
147
  if (remainder === 0) {
168
148
  return thousandsWords
@@ -170,10 +150,10 @@ function integerToWords (n) {
170
150
 
171
151
  // Check if remainder needs "lẻ" marker (< 100)
172
152
  if (remainder < 100) {
173
- return thousandsWords + ' ' + LE_SEGMENTS[remainder]
153
+ return thousandsWords + ' ' + buildLeSegment(remainder)
174
154
  }
175
155
 
176
- return thousandsWords + ' ' + SEGMENTS[remainder]
156
+ return thousandsWords + ' ' + buildSegment(remainder)
177
157
  }
178
158
 
179
159
  // For numbers >= 1,000,000, use scale decomposition
@@ -212,7 +192,7 @@ function buildLargeNumberWords (n) {
212
192
  for (let i = 0; i < segments.length; i++) {
213
193
  const segment = segments[i]
214
194
  if (segment !== 0) {
215
- const words = SEGMENTS[segment]
195
+ const words = buildSegment(segment)
216
196
  if (words) {
217
197
  if (scaleIndex > 0) {
218
198
  parts.push(words + ' ' + SCALES[scaleIndex])
@@ -237,7 +217,7 @@ function buildLargeNumberWords (n) {
237
217
  for (let i = 1; i < partsLen - 1; i++) {
238
218
  result += ' ' + parts[i]
239
219
  }
240
- return result + ' ' + LE_SEGMENTS[lastSegment]
220
+ return result + ' ' + buildLeSegment(lastSegment)
241
221
  }
242
222
 
243
223
  // Join with spaces
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Converts a numeric value to Yoruba words.
3
+ *
4
+ * @param {number | string | bigint} value - The numeric value to convert
5
+ * @returns {string} The number in Yoruba words
6
+ */
7
+ export function toWords(value: number | string | bigint): string;
@@ -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 }
package/lib/n2words.d.ts CHANGED
@@ -24,6 +24,7 @@ import { toWords as hu } from './languages/hu.js';
24
24
  import { toWords as id } from './languages/id.js';
25
25
  import { toWords as it } from './languages/it.js';
26
26
  import { toWords as ja } from './languages/ja.js';
27
+ import { toWords as ka } from './languages/ka.js';
27
28
  import { toWords as kn } from './languages/kn.js';
28
29
  import { toWords as ko } from './languages/ko.js';
29
30
  import { toWords as lt } from './languages/lt.js';
@@ -48,6 +49,7 @@ import { toWords as tr } from './languages/tr.js';
48
49
  import { toWords as uk } from './languages/uk.js';
49
50
  import { toWords as ur } from './languages/ur.js';
50
51
  import { toWords as vi } from './languages/vi.js';
52
+ import { toWords as yo } from './languages/yo.js';
51
53
  import { toWords as zhHans } from './languages/zh-Hans.js';
52
54
  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 };
55
+ 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, ka, kn, ko, lt, lv, mr, ms, nb, nl, pa, pl, pt, ro, ru, srCyrl, srLatn, sv, sw, ta, te, th, tr, uk, ur, vi, yo, zhHans, zhHant };
package/lib/n2words.js CHANGED
@@ -39,6 +39,7 @@ import { toWords as hu } from './languages/hu.js'
39
39
  import { toWords as id } from './languages/id.js'
40
40
  import { toWords as it } from './languages/it.js'
41
41
  import { toWords as ja } from './languages/ja.js'
42
+ import { toWords as ka } from './languages/ka.js'
42
43
  import { toWords as kn } from './languages/kn.js'
43
44
  import { toWords as ko } from './languages/ko.js'
44
45
  import { toWords as lt } from './languages/lt.js'
@@ -63,6 +64,7 @@ import { toWords as tr } from './languages/tr.js'
63
64
  import { toWords as uk } from './languages/uk.js'
64
65
  import { toWords as ur } from './languages/ur.js'
65
66
  import { toWords as vi } from './languages/vi.js'
67
+ import { toWords as yo } from './languages/yo.js'
66
68
  import { toWords as zhHans } from './languages/zh-Hans.js'
67
69
  import { toWords as zhHant } from './languages/zh-Hant.js'
68
70
 
@@ -93,6 +95,7 @@ export {
93
95
  id,
94
96
  it,
95
97
  ja,
98
+ ka,
96
99
  kn,
97
100
  ko,
98
101
  lt,
@@ -117,6 +120,7 @@ export {
117
120
  uk,
118
121
  ur,
119
122
  vi,
123
+ yo,
120
124
  zhHans,
121
125
  zhHant
122
126
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n2words",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "Convert numbers to words in 52 languages with zero dependencies. Supports BigInt, decimals, and browser/Node.js environments.",
5
5
  "keywords": [
6
6
  "number-to-words",