n2words 2.0.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 (327) hide show
  1. package/CHANGELOG.md +49 -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/kn.js +3 -0
  56. package/dist/languages/kn.js.map +1 -0
  57. package/dist/languages/ko.js +3 -0
  58. package/dist/languages/ko.js.map +1 -0
  59. package/dist/languages/lt.js +3 -0
  60. package/dist/languages/lt.js.map +1 -0
  61. package/dist/languages/lv.js +3 -0
  62. package/dist/languages/lv.js.map +1 -0
  63. package/dist/languages/mr.js +3 -0
  64. package/dist/languages/mr.js.map +1 -0
  65. package/dist/languages/ms.js +3 -0
  66. package/dist/languages/ms.js.map +1 -0
  67. package/dist/languages/nb.js +3 -0
  68. package/dist/languages/nb.js.map +1 -0
  69. package/dist/languages/nl.js +3 -0
  70. package/dist/languages/nl.js.map +1 -0
  71. package/dist/languages/pa.js +3 -0
  72. package/dist/languages/pa.js.map +1 -0
  73. package/dist/languages/pl.js +3 -0
  74. package/dist/languages/pl.js.map +1 -0
  75. package/dist/languages/pt.js +3 -0
  76. package/dist/languages/pt.js.map +1 -0
  77. package/dist/languages/ro.js +3 -0
  78. package/dist/languages/ro.js.map +1 -0
  79. package/dist/languages/ru.js +3 -0
  80. package/dist/languages/ru.js.map +1 -0
  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 -0
  84. package/dist/languages/sr-Latn.js.map +1 -0
  85. package/dist/languages/sv.js +3 -0
  86. package/dist/languages/sv.js.map +1 -0
  87. package/dist/languages/sw.js +3 -0
  88. package/dist/languages/sw.js.map +1 -0
  89. package/dist/languages/ta.js +3 -0
  90. package/dist/languages/ta.js.map +1 -0
  91. package/dist/languages/te.js +3 -0
  92. package/dist/languages/te.js.map +1 -0
  93. package/dist/languages/th.js +3 -0
  94. package/dist/languages/th.js.map +1 -0
  95. package/dist/languages/tr.js +3 -0
  96. package/dist/languages/tr.js.map +1 -0
  97. package/dist/languages/uk.js +3 -0
  98. package/dist/languages/uk.js.map +1 -0
  99. package/dist/languages/ur.js +3 -0
  100. package/dist/languages/ur.js.map +1 -0
  101. package/dist/languages/vi.js +3 -0
  102. package/dist/languages/vi.js.map +1 -0
  103. package/dist/languages/zh-Hans.js +3 -0
  104. package/dist/languages/zh-Hans.js.map +1 -0
  105. package/dist/languages/zh-Hant.js +3 -0
  106. package/dist/languages/zh-Hant.js.map +1 -0
  107. package/dist/n2words.js +2 -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 +14 -27
  114. package/lib/languages/ar.js +175 -129
  115. package/lib/languages/az.d.ts +4 -9
  116. package/lib/languages/az.js +171 -37
  117. package/lib/languages/bn.d.ts +4 -8
  118. package/lib/languages/bn.js +138 -124
  119. package/lib/languages/cs.d.ts +15 -85
  120. package/lib/languages/cs.js +310 -114
  121. package/lib/languages/da.d.ts +11 -12
  122. package/lib/languages/da.js +276 -101
  123. package/lib/languages/de.d.ts +14 -11
  124. package/lib/languages/de.js +317 -86
  125. package/lib/languages/el.d.ts +11 -11
  126. package/lib/languages/el.js +231 -78
  127. package/lib/languages/en.d.ts +14 -13
  128. package/lib/languages/en.js +242 -72
  129. package/lib/languages/es.d.ts +18 -12
  130. package/lib/languages/es.js +317 -103
  131. package/lib/languages/fa.d.ts +4 -44
  132. package/lib/languages/fa.js +112 -122
  133. package/lib/languages/fi.d.ts +14 -0
  134. package/lib/languages/fi.js +245 -0
  135. package/lib/languages/fil.d.ts +4 -13
  136. package/lib/languages/fil.js +207 -106
  137. package/lib/languages/fr-BE.d.ts +8 -8
  138. package/lib/languages/fr-BE.js +294 -19
  139. package/lib/languages/fr.d.ts +18 -12
  140. package/lib/languages/fr.js +352 -89
  141. package/lib/languages/gu.d.ts +4 -8
  142. package/lib/languages/gu.js +130 -125
  143. package/lib/languages/ha.d.ts +7 -0
  144. package/lib/languages/ha.js +230 -0
  145. package/lib/languages/hbo.d.ts +10 -110
  146. package/lib/languages/hbo.js +263 -214
  147. package/lib/languages/he.d.ts +10 -77
  148. package/lib/languages/he.js +242 -172
  149. package/lib/languages/hi.d.ts +4 -8
  150. package/lib/languages/hi.js +138 -124
  151. package/lib/languages/hr.d.ts +8 -77
  152. package/lib/languages/hr.js +194 -89
  153. package/lib/languages/hu.d.ts +4 -19
  154. package/lib/languages/hu.js +198 -119
  155. package/lib/languages/id.d.ts +4 -34
  156. package/lib/languages/id.js +171 -129
  157. package/lib/languages/it.d.ts +16 -34
  158. package/lib/languages/it.js +339 -94
  159. package/lib/languages/ja.d.ts +14 -14
  160. package/lib/languages/ja.js +233 -111
  161. package/lib/languages/kn.d.ts +4 -8
  162. package/lib/languages/kn.js +130 -35
  163. package/lib/languages/ko.d.ts +11 -11
  164. package/lib/languages/ko.js +257 -49
  165. package/lib/languages/lt.d.ts +15 -67
  166. package/lib/languages/lt.js +296 -122
  167. package/lib/languages/lv.d.ts +15 -67
  168. package/lib/languages/lv.js +297 -106
  169. package/lib/languages/mr.d.ts +4 -8
  170. package/lib/languages/mr.js +130 -125
  171. package/lib/languages/ms.d.ts +4 -28
  172. package/lib/languages/ms.js +171 -116
  173. package/lib/languages/nb.d.ts +11 -9
  174. package/lib/languages/nb.js +282 -87
  175. package/lib/languages/nl.d.ts +23 -13
  176. package/lib/languages/nl.js +317 -133
  177. package/lib/languages/pa.d.ts +4 -8
  178. package/lib/languages/pa.js +156 -124
  179. package/lib/languages/pl.d.ts +19 -77
  180. package/lib/languages/pl.js +307 -87
  181. package/lib/languages/pt.d.ts +14 -26
  182. package/lib/languages/pt.js +286 -92
  183. package/lib/languages/ro.d.ts +15 -155
  184. package/lib/languages/ro.js +219 -235
  185. package/lib/languages/ru.d.ts +8 -82
  186. package/lib/languages/ru.js +222 -78
  187. package/lib/languages/sr-Cyrl.d.ts +8 -77
  188. package/lib/languages/sr-Cyrl.js +191 -89
  189. package/lib/languages/sr-Latn.d.ts +8 -77
  190. package/lib/languages/sr-Latn.js +191 -89
  191. package/lib/languages/sv.d.ts +11 -11
  192. package/lib/languages/sv.js +288 -74
  193. package/lib/languages/sw.d.ts +4 -36
  194. package/lib/languages/sw.js +133 -106
  195. package/lib/languages/ta.d.ts +4 -17
  196. package/lib/languages/ta.js +129 -201
  197. package/lib/languages/te.d.ts +4 -19
  198. package/lib/languages/te.js +141 -196
  199. package/lib/languages/th.d.ts +4 -14
  200. package/lib/languages/th.js +135 -91
  201. package/lib/languages/tr.d.ts +15 -9
  202. package/lib/languages/tr.js +256 -49
  203. package/lib/languages/uk.d.ts +8 -82
  204. package/lib/languages/uk.js +200 -78
  205. package/lib/languages/ur.d.ts +4 -8
  206. package/lib/languages/ur.js +156 -124
  207. package/lib/languages/vi.d.ts +14 -69
  208. package/lib/languages/vi.js +294 -125
  209. package/lib/languages/zh-Hans.d.ts +8 -18
  210. package/lib/languages/zh-Hans.js +163 -92
  211. package/lib/languages/zh-Hant.d.ts +8 -18
  212. package/lib/languages/zh-Hant.js +181 -90
  213. package/lib/n2words.d.ts +53 -209
  214. package/lib/n2words.js +111 -530
  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 +26 -14
  222. package/dist/ArabicConverter.js +0 -3
  223. package/dist/ArabicConverter.js.map +0 -1
  224. package/dist/AzerbaijaniConverter.js +0 -3
  225. package/dist/AzerbaijaniConverter.js.map +0 -1
  226. package/dist/BanglaConverter.js +0 -3
  227. package/dist/BanglaConverter.js.map +0 -1
  228. package/dist/BiblicalHebrewConverter.js +0 -3
  229. package/dist/BiblicalHebrewConverter.js.map +0 -1
  230. package/dist/CroatianConverter.js +0 -3
  231. package/dist/CroatianConverter.js.map +0 -1
  232. package/dist/CzechConverter.js +0 -3
  233. package/dist/CzechConverter.js.map +0 -1
  234. package/dist/DanishConverter.js +0 -3
  235. package/dist/DanishConverter.js.map +0 -1
  236. package/dist/DutchConverter.js +0 -3
  237. package/dist/DutchConverter.js.map +0 -1
  238. package/dist/EnglishConverter.js +0 -3
  239. package/dist/EnglishConverter.js.map +0 -1
  240. package/dist/FilipinoConverter.js +0 -3
  241. package/dist/FilipinoConverter.js.map +0 -1
  242. package/dist/FrenchBelgiumConverter.js +0 -3
  243. package/dist/FrenchBelgiumConverter.js.map +0 -1
  244. package/dist/FrenchConverter.js +0 -3
  245. package/dist/FrenchConverter.js.map +0 -1
  246. package/dist/GermanConverter.js +0 -3
  247. package/dist/GermanConverter.js.map +0 -1
  248. package/dist/GreekConverter.js +0 -3
  249. package/dist/GreekConverter.js.map +0 -1
  250. package/dist/GujaratiConverter.js +0 -3
  251. package/dist/GujaratiConverter.js.map +0 -1
  252. package/dist/HebrewConverter.js +0 -3
  253. package/dist/HebrewConverter.js.map +0 -1
  254. package/dist/HindiConverter.js +0 -3
  255. package/dist/HindiConverter.js.map +0 -1
  256. package/dist/HungarianConverter.js +0 -3
  257. package/dist/HungarianConverter.js.map +0 -1
  258. package/dist/IndonesianConverter.js +0 -3
  259. package/dist/IndonesianConverter.js.map +0 -1
  260. package/dist/ItalianConverter.js +0 -3
  261. package/dist/ItalianConverter.js.map +0 -1
  262. package/dist/JapaneseConverter.js +0 -3
  263. package/dist/JapaneseConverter.js.map +0 -1
  264. package/dist/KannadaConverter.js +0 -3
  265. package/dist/KannadaConverter.js.map +0 -1
  266. package/dist/KoreanConverter.js +0 -3
  267. package/dist/KoreanConverter.js.map +0 -1
  268. package/dist/LatvianConverter.js +0 -3
  269. package/dist/LatvianConverter.js.map +0 -1
  270. package/dist/LithuanianConverter.js +0 -3
  271. package/dist/LithuanianConverter.js.map +0 -1
  272. package/dist/MalayConverter.js +0 -3
  273. package/dist/MalayConverter.js.map +0 -1
  274. package/dist/MarathiConverter.js +0 -3
  275. package/dist/MarathiConverter.js.map +0 -1
  276. package/dist/NorwegianBokmalConverter.js +0 -3
  277. package/dist/NorwegianBokmalConverter.js.map +0 -1
  278. package/dist/PersianConverter.js +0 -3
  279. package/dist/PersianConverter.js.map +0 -1
  280. package/dist/PolishConverter.js +0 -3
  281. package/dist/PolishConverter.js.map +0 -1
  282. package/dist/PortugueseConverter.js +0 -3
  283. package/dist/PortugueseConverter.js.map +0 -1
  284. package/dist/PunjabiConverter.js +0 -3
  285. package/dist/PunjabiConverter.js.map +0 -1
  286. package/dist/RomanianConverter.js +0 -3
  287. package/dist/RomanianConverter.js.map +0 -1
  288. package/dist/RussianConverter.js +0 -3
  289. package/dist/RussianConverter.js.map +0 -1
  290. package/dist/SerbianCyrillicConverter.js +0 -3
  291. package/dist/SerbianCyrillicConverter.js.map +0 -1
  292. package/dist/SerbianLatinConverter.js +0 -3
  293. package/dist/SerbianLatinConverter.js.map +0 -1
  294. package/dist/SimplifiedChineseConverter.js +0 -3
  295. package/dist/SimplifiedChineseConverter.js.map +0 -1
  296. package/dist/SpanishConverter.js +0 -3
  297. package/dist/SpanishConverter.js.map +0 -1
  298. package/dist/SwahiliConverter.js +0 -3
  299. package/dist/SwahiliConverter.js.map +0 -1
  300. package/dist/SwedishConverter.js +0 -3
  301. package/dist/SwedishConverter.js.map +0 -1
  302. package/dist/TamilConverter.js +0 -3
  303. package/dist/TamilConverter.js.map +0 -1
  304. package/dist/TeluguConverter.js +0 -3
  305. package/dist/TeluguConverter.js.map +0 -1
  306. package/dist/ThaiConverter.js +0 -3
  307. package/dist/ThaiConverter.js.map +0 -1
  308. package/dist/TraditionalChineseConverter.js +0 -3
  309. package/dist/TraditionalChineseConverter.js.map +0 -1
  310. package/dist/TurkishConverter.js +0 -3
  311. package/dist/TurkishConverter.js.map +0 -1
  312. package/dist/UkrainianConverter.js +0 -3
  313. package/dist/UkrainianConverter.js.map +0 -1
  314. package/dist/UrduConverter.js +0 -3
  315. package/dist/UrduConverter.js.map +0 -1
  316. package/dist/VietnameseConverter.js +0 -3
  317. package/dist/VietnameseConverter.js.map +0 -1
  318. package/lib/classes/abstract-language.d.ts +0 -178
  319. package/lib/classes/abstract-language.js +0 -268
  320. package/lib/classes/greedy-scale-language.d.ts +0 -109
  321. package/lib/classes/greedy-scale-language.js +0 -201
  322. package/lib/classes/slavic-language.d.ts +0 -148
  323. package/lib/classes/slavic-language.js +0 -281
  324. package/lib/classes/south-asian-language.d.ts +0 -70
  325. package/lib/classes/south-asian-language.js +0 -154
  326. package/lib/classes/turkic-language.d.ts +0 -26
  327. package/lib/classes/turkic-language.js +0 -59
@@ -1,15 +1,21 @@
1
1
  /**
2
- * French language converter.
2
+ * Converts a numeric value to French words.
3
3
  *
4
- * Supports:
5
- * - Pluralization of "cent" (hundred) and other words
6
- * - "et" (and) before odd numbers in tens place
7
- * - Optional hyphenation for compound numbers
4
+ * This is the main public API. It accepts any valid numeric input
5
+ * (number, string, or bigint) and handles parsing internally.
6
+ *
7
+ * @param {number | string | bigint} value - The numeric value to convert
8
+ * @param {Object} [options] - Optional configuration
9
+ * @param {boolean} [options.withHyphenSeparator=false] - Use hyphens between all words
10
+ * @returns {string} The number in French words
11
+ * @throws {TypeError} If value is not a valid numeric type
12
+ * @throws {Error} If value is not a valid number format
13
+ *
14
+ * @example
15
+ * toWords(21) // 'vingt et un'
16
+ * toWords(80) // 'quatre-vingts'
17
+ * toWords(1000000) // 'un million'
8
18
  */
9
- export class French extends GreedyScaleLanguage {
10
- constructor(options?: {});
11
- scaleWords: (string | bigint)[][];
12
- /** Combines two word-sets with French pluralization and hyphenation rules. */
13
- combineWordSets(preceding: any, following: any): any;
14
- }
15
- import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js';
19
+ export function toWords(value: number | string | bigint, options?: {
20
+ withHyphenSeparator?: boolean | undefined;
21
+ }): string;
@@ -1,106 +1,369 @@
1
- import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'
2
-
3
1
  /**
4
- * French language converter.
2
+ * French language converter - Functional Implementation
3
+ *
4
+ * A performance-optimized number-to-words converter using precomputed lookup tables.
5
+ * Self-contained module with its own input validation, ready for subpath exports.
6
+ *
7
+ * Key optimization: Precompute all segment values (0-999) at module load.
8
+ * This eliminates all per-call string manipulation for segment conversion.
5
9
  *
6
- * Supports:
7
- * - Pluralization of "cent" (hundred) and other words
8
- * - "et" (and) before odd numbers in tens place
9
- * - Optional hyphenation for compound numbers
10
+ * French-specific rules (handled in precomputation):
11
+ * - Vigesimal patterns: 70 = soixante-dix, 80 = quatre-vingts, 90 = quatre-vingt-dix
12
+ * - "et" conjunction: vingt et un (21), soixante et onze (71), but NOT quatre-vingt-un
13
+ * - Pluralization: "cents" loses 's' when followed by more digits
14
+ * - Long scale with -ard forms: milliard, billiard, trilliard
15
+ * - Omit "un" before mille
10
16
  */
11
- export class French extends GreedyScaleLanguage {
12
- negativeWord = 'moins'
13
- decimalSeparatorWord = 'virgule'
14
- zeroWord = 'zéro'
15
-
16
- scaleWords = [
17
- [1_000_000_000_000_000_000_000_000_000n, 'quadrilliard'],
18
- [1_000_000_000_000_000_000_000_000n, 'quadrillion'],
19
- [1_000_000_000_000_000_000_000n, 'trilliard'],
20
- [1_000_000_000_000_000_000n, 'trillion'],
21
- [1_000_000_000_000_000n, 'billiard'],
22
- [1_000_000_000_000n, 'billion'],
23
- [1_000_000_000n, 'milliard'],
24
- [1_000_000n, 'million'],
25
- [1000n, 'mille'],
26
- [100n, 'cent'],
27
- [80n, 'quatre-vingts'],
28
- [60n, 'soixante'],
29
- [50n, 'cinquante'],
30
- [40n, 'quarante'],
31
- [30n, 'trente'],
32
- [20n, 'vingt'],
33
- [19n, 'dix-neuf'],
34
- [18n, 'dix-huit'],
35
- [17n, 'dix-sept'],
36
- [16n, 'seize'],
37
- [15n, 'quinze'],
38
- [14n, 'quatorze'],
39
- [13n, 'treize'],
40
- [12n, 'douze'],
41
- [11n, 'onze'],
42
- [10n, 'dix'],
43
- [9n, 'neuf'],
44
- [8n, 'huit'],
45
- [7n, 'sept'],
46
- [6n, 'six'],
47
- [5n, 'cinq'],
48
- [4n, 'quatre'],
49
- [3n, 'trois'],
50
- [2n, 'deux'],
51
- [1n, 'un'],
52
- [0n, 'zéro']
53
- ]
54
-
55
- constructor (options = {}) {
56
- super()
57
-
58
- this.setOptions({
59
- withHyphenSeparator: false
60
- }, options)
61
-
62
- if (options.withHyphenSeparator) {
63
- this.wordSeparator = '-'
64
- }
65
- }
66
17
 
67
- /** Combines two word-sets with French pluralization and hyphenation rules. */
68
- combineWordSets (preceding, following) {
69
- let precedingWord = Object.keys(preceding)[0]
70
- let followingWord = Object.keys(following)[0]
71
- const precedingValue = Object.values(preceding)[0]
72
- const followingValue = Object.values(following)[0]
18
+ import { parseNumericValue } from '../utils/parse-numeric.js'
19
+ import { validateOptions } from '../utils/validate-options.js'
20
+
21
+ // ============================================================================
22
+ // Vocabulary (module-level constants)
23
+ // ============================================================================
24
+
25
+ const ONES = ['', 'un', 'deux', 'trois', 'quatre', 'cinq', 'six', 'sept', 'huit', 'neuf']
26
+ const TEENS = ['dix', 'onze', 'douze', 'treize', 'quatorze', 'quinze', 'seize', 'dix-sept', 'dix-huit', 'dix-neuf']
27
+ const TENS = ['', '', 'vingt', 'trente', 'quarante', 'cinquante', 'soixante']
28
+
29
+ // Scale words (even indices: million, billion, trillion, quadrillion)
30
+ const SCALES = ['million', 'billion', 'trillion', 'quadrillion']
31
+ const SCALES_ARD = ['milliard', 'billiard', 'trilliard', 'quadrilliard']
32
+
33
+ const THOUSAND = 'mille'
34
+ const HUNDRED = 'cent'
35
+ const ZERO = 'zéro'
36
+ const NEGATIVE = 'moins'
37
+ const DECIMAL_SEP = 'virgule'
38
+
39
+ // ============================================================================
40
+ // Precomputed Lookup Tables (built once at module load)
41
+ // ============================================================================
42
+
43
+ /**
44
+ * Builds segment word for 0-999.
45
+ * Returns object with { word, endsWithCents, endsWithVingts } for pluralization handling.
46
+ */
47
+ function buildSegment (n) {
48
+ if (n === 0) return { word: '', endsWithCents: false, endsWithVingts: false }
49
+
50
+ const tensOnes = n % 100
51
+ const hundreds = Math.floor(n / 100)
52
+
53
+ const parts = []
54
+ let endsWithCents = false
55
+ let endsWithVingts = false
73
56
 
74
- if (precedingValue === 1n) {
75
- if (followingValue < 1_000_000n) {
76
- return following
57
+ // Hundreds
58
+ if (hundreds > 0) {
59
+ if (hundreds === 1) {
60
+ if (tensOnes === 0) {
61
+ parts.push(HUNDRED)
62
+ } else {
63
+ parts.push(HUNDRED)
77
64
  }
78
65
  } else {
79
- if (
80
- ((precedingValue - 80n) % 100n === 0n || (precedingValue % 100n === 0n && precedingValue < 1000n)) &&
81
- followingValue < 1_000_000n &&
82
- precedingWord.at(-1) === 's'
83
- ) {
84
- precedingWord = precedingWord.slice(0, -1)
66
+ if (tensOnes === 0) {
67
+ // "deux cents", "trois cents" (with 's')
68
+ parts.push(ONES[hundreds] + ' ' + HUNDRED + 's')
69
+ endsWithCents = true
70
+ } else {
71
+ // "deux cent", "trois cent" (no 's' when followed by more)
72
+ parts.push(ONES[hundreds] + ' ' + HUNDRED)
85
73
  }
74
+ }
75
+ }
76
+
77
+ // Tens and ones - vigesimal pattern
78
+ if (tensOnes === 0) {
79
+ // Just hundreds, nothing more
80
+ } else if (tensOnes < 10) {
81
+ // Single digit
82
+ parts.push(ONES[tensOnes])
83
+ } else if (tensOnes < 17) {
84
+ // 10-16: regular teens
85
+ parts.push(TEENS[tensOnes - 10])
86
+ } else if (tensOnes < 20) {
87
+ // 17-19: dix-sept, dix-huit, dix-neuf
88
+ parts.push(TEENS[tensOnes - 10])
89
+ } else if (tensOnes < 70) {
90
+ // 20-69: standard tens + ones
91
+ const t = Math.floor(tensOnes / 10)
92
+ const o = tensOnes % 10
93
+ if (o === 0) {
94
+ parts.push(TENS[t])
95
+ } else if (o === 1) {
96
+ // "et un" for 21, 31, 41, 51, 61
97
+ parts.push(TENS[t] + ' et ' + ONES[1])
98
+ } else {
99
+ parts.push(TENS[t] + '-' + ONES[o])
100
+ }
101
+ } else if (tensOnes < 80) {
102
+ // 70-79: soixante-dix, soixante et onze, soixante-douze...
103
+ const remainder = tensOnes - 60
104
+ if (remainder === 11) {
105
+ // 71: soixante et onze
106
+ parts.push('soixante et onze')
107
+ } else {
108
+ // 70, 72-79: soixante-dix, soixante-douze...
109
+ parts.push('soixante-' + TEENS[remainder - 10])
110
+ }
111
+ } else if (tensOnes === 80) {
112
+ // 80: quatre-vingts (with 's')
113
+ parts.push('quatre-vingts')
114
+ endsWithVingts = true
115
+ } else if (tensOnes < 100) {
116
+ // 81-99: quatre-vingt-un, quatre-vingt-dix...
117
+ const remainder = tensOnes - 80
118
+ if (remainder < 10) {
119
+ // 81-89
120
+ parts.push('quatre-vingt-' + ONES[remainder])
121
+ } else {
122
+ // 90-99
123
+ parts.push('quatre-vingt-' + TEENS[remainder - 10])
124
+ }
125
+ }
126
+
127
+ // Join parts with space (between hundreds and rest)
128
+ return { word: parts.join(' '), endsWithCents, endsWithVingts }
129
+ }
130
+
131
+ // Precompute all 1000 segment words (0-999)
132
+ const SEGMENTS = new Array(1000)
133
+ const SEGMENTS_ENDS_CENTS = new Array(1000)
134
+ const SEGMENTS_ENDS_VINGTS = new Array(1000)
135
+
136
+ for (let i = 0; i < 1000; i++) {
137
+ const result = buildSegment(i)
138
+ SEGMENTS[i] = result.word
139
+ SEGMENTS_ENDS_CENTS[i] = result.endsWithCents
140
+ SEGMENTS_ENDS_VINGTS[i] = result.endsWithVingts
141
+ }
142
+
143
+ // ============================================================================
144
+ // Helper Functions
145
+ // ============================================================================
146
+
147
+ /**
148
+ * Gets scale word for French long scale with -ard pattern.
149
+ *
150
+ * @param {number} scaleIndex - Scale level (1 = thousand, 2 = million, etc.)
151
+ * @param {bigint} segment - Segment value for pluralization
152
+ * @returns {string} Scale word
153
+ */
154
+ function getScaleWord (scaleIndex, segment) {
155
+ if (scaleIndex === 1) return THOUSAND
156
+
157
+ // Even indices (2, 4, 6, 8): million, billion, trillion, quadrillion
158
+ // Odd indices > 1 (3, 5, 7, 9): milliard, billiard, trilliard, quadrilliard
159
+ if (scaleIndex % 2 === 0) {
160
+ const arrayIndex = (scaleIndex / 2) - 1
161
+ const baseWord = SCALES[arrayIndex]
162
+ if (!baseWord) return ''
163
+ return segment > 1n ? baseWord + 's' : baseWord
164
+ } else {
165
+ const arrayIndex = ((scaleIndex - 1) / 2) - 1
166
+ const ardWord = SCALES_ARD[arrayIndex]
167
+ if (!ardWord) return THOUSAND
168
+ return segment > 1n ? ardWord + 's' : ardWord
169
+ }
170
+ }
171
+
172
+ // ============================================================================
173
+ // Conversion Functions
174
+ // ============================================================================
175
+
176
+ /**
177
+ * Converts a non-negative integer to French words.
178
+ *
179
+ * @param {bigint} n - Non-negative integer to convert
180
+ * @param {boolean} withHyphen - Whether to use hyphen separators
181
+ * @returns {string} French words
182
+ */
183
+ function integerToWords (n, withHyphen = false) {
184
+ if (n === 0n) return ZERO
185
+
186
+ // Fast path: numbers < 1000 (direct lookup)
187
+ if (n < 1000n) {
188
+ const word = SEGMENTS[Number(n)]
189
+ return withHyphen ? word.replace(/ /g, '-') : word
190
+ }
86
191
 
87
- if (
88
- precedingValue < 1000n && followingValue !== 1000n &&
89
- followingWord.at(-1) !== 's' &&
90
- followingValue % 100n === 0n
91
- ) {
92
- followingWord += 's'
192
+ // Fast path: numbers < 1,000,000 (thousands)
193
+ if (n < 1_000_000n) {
194
+ const thousands = Number(n / 1000n)
195
+ const remainder = Number(n % 1000n)
196
+
197
+ let result
198
+ if (thousands === 1) {
199
+ // "mille" not "un mille"
200
+ result = THOUSAND
201
+ } else {
202
+ // Check if segment ends with "cents" or "vingts" - need to strip 's' before mille
203
+ let thousandsWord = SEGMENTS[thousands]
204
+ if (SEGMENTS_ENDS_CENTS[thousands] || SEGMENTS_ENDS_VINGTS[thousands]) {
205
+ thousandsWord = thousandsWord.slice(0, -1) // Remove trailing 's'
93
206
  }
207
+ result = thousandsWord + (withHyphen ? '-' : ' ') + THOUSAND
208
+ }
209
+
210
+ if (remainder > 0) {
211
+ result += (withHyphen ? '-' : ' ') + SEGMENTS[remainder]
212
+ }
213
+
214
+ if (withHyphen) {
215
+ result = result.replace(/ /g, '-')
94
216
  }
95
217
 
96
- if (followingValue < precedingValue && precedingValue < 100n) {
97
- if (followingValue % 10n === 1n && precedingValue !== 80n) {
98
- return { [`${precedingWord}${this.wordSeparator}et${this.wordSeparator}${followingWord}`]: precedingValue + followingValue }
218
+ return result
219
+ }
220
+
221
+ // For numbers >= 1,000,000, use scale decomposition
222
+ return buildLargeNumberWords(n, withHyphen)
223
+ }
224
+
225
+ /**
226
+ * Builds words for numbers >= 1,000,000.
227
+ *
228
+ * @param {bigint} n - Number >= 1,000,000
229
+ * @param {boolean} withHyphen - Whether to use hyphen separators
230
+ * @returns {string} French words
231
+ */
232
+ function buildLargeNumberWords (n, withHyphen) {
233
+ const numStr = n.toString()
234
+ const len = numStr.length
235
+
236
+ // Build segments of 3 digits from right to left
237
+ const segments = []
238
+ const segmentSize = 3
239
+
240
+ const remainderLen = len % segmentSize
241
+ let pos = 0
242
+ if (remainderLen > 0) {
243
+ segments.push(Number(numStr.slice(0, remainderLen)))
244
+ pos = remainderLen
245
+ }
246
+ while (pos < len) {
247
+ segments.push(Number(numStr.slice(pos, pos + segmentSize)))
248
+ pos += segmentSize
249
+ }
250
+
251
+ // Convert segments to words
252
+ const parts = []
253
+ let scaleIndex = segments.length - 1
254
+
255
+ for (let i = 0; i < segments.length; i++) {
256
+ const segment = segments[i]
257
+
258
+ if (segment !== 0) {
259
+ const scaleWord = scaleIndex > 0 ? getScaleWord(scaleIndex, BigInt(segment)) : ''
260
+
261
+ if (scaleIndex === 0) {
262
+ // Units segment
263
+ parts.push(SEGMENTS[segment])
264
+ } else if (scaleIndex === 1) {
265
+ // Thousands: "mille" not "un mille"
266
+ if (segment === 1) {
267
+ parts.push(THOUSAND)
268
+ } else {
269
+ let segWords = SEGMENTS[segment]
270
+ // Strip 's' from cents/vingts before mille
271
+ if (SEGMENTS_ENDS_CENTS[segment] || SEGMENTS_ENDS_VINGTS[segment]) {
272
+ segWords = segWords.slice(0, -1)
273
+ }
274
+ parts.push(segWords)
275
+ parts.push(scaleWord)
276
+ }
277
+ } else {
278
+ // Million and above
279
+ parts.push(SEGMENTS[segment])
280
+ parts.push(scaleWord)
99
281
  }
100
- return { [`${precedingWord}-${followingWord}`]: precedingValue + followingValue }
101
282
  }
102
283
 
103
- if (followingValue > precedingValue) return { [`${precedingWord}${this.wordSeparator}${followingWord}`]: precedingValue * followingValue }
104
- return { [`${precedingWord}${this.wordSeparator}${followingWord}`]: precedingValue + followingValue }
284
+ scaleIndex--
285
+ }
286
+
287
+ const sep = withHyphen ? '-' : ' '
288
+ let result = parts.join(sep)
289
+
290
+ if (withHyphen) {
291
+ result = result.replace(/ /g, '-')
292
+ }
293
+
294
+ return result
295
+ }
296
+
297
+ /**
298
+ * Converts decimal digits to French words.
299
+ *
300
+ * @param {string} decimalPart - Decimal digits (without the point)
301
+ * @param {boolean} withHyphen - Whether to use hyphen separators
302
+ * @returns {string} French words for decimal part
303
+ */
304
+ function decimalPartToWords (decimalPart, withHyphen) {
305
+ let result = ''
306
+ const sep = withHyphen ? '-' : ' '
307
+
308
+ // Handle leading zeros
309
+ let i = 0
310
+ while (i < decimalPart.length && decimalPart[i] === '0') {
311
+ if (result) result += sep
312
+ result += ZERO
313
+ i++
314
+ }
315
+
316
+ // Convert remainder as a single number
317
+ const remainder = decimalPart.slice(i)
318
+ if (remainder) {
319
+ if (result) result += sep
320
+ result += integerToWords(BigInt(remainder), withHyphen)
321
+ }
322
+
323
+ return result
324
+ }
325
+
326
+ /**
327
+ * Converts a numeric value to French words.
328
+ *
329
+ * This is the main public API. It accepts any valid numeric input
330
+ * (number, string, or bigint) and handles parsing internally.
331
+ *
332
+ * @param {number | string | bigint} value - The numeric value to convert
333
+ * @param {Object} [options] - Optional configuration
334
+ * @param {boolean} [options.withHyphenSeparator=false] - Use hyphens between all words
335
+ * @returns {string} The number in French words
336
+ * @throws {TypeError} If value is not a valid numeric type
337
+ * @throws {Error} If value is not a valid number format
338
+ *
339
+ * @example
340
+ * toWords(21) // 'vingt et un'
341
+ * toWords(80) // 'quatre-vingts'
342
+ * toWords(1000000) // 'un million'
343
+ */
344
+ function toWords (value, options) {
345
+ options = validateOptions(options)
346
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
347
+ const withHyphen = options.withHyphenSeparator || false
348
+
349
+ let result = ''
350
+ const sep = withHyphen ? '-' : ' '
351
+
352
+ if (isNegative) {
353
+ result = NEGATIVE + sep
105
354
  }
355
+
356
+ result += integerToWords(integerPart, withHyphen)
357
+
358
+ if (decimalPart) {
359
+ result += sep + DECIMAL_SEP + sep + decimalPartToWords(decimalPart, withHyphen)
360
+ }
361
+
362
+ return result
106
363
  }
364
+
365
+ // ============================================================================
366
+ // Public API
367
+ // ============================================================================
368
+
369
+ export { toWords }
@@ -1,11 +1,7 @@
1
1
  /**
2
- * Gujarati language converter.
2
+ * Converts a numeric value to Gujarati words.
3
3
  *
4
- * Supports:
5
- * - Indian numbering system (હજાર, લાખ, કરોડ)
6
- * - Gujarati script
7
- * - Complete word forms for 0-99
4
+ * @param {number | string | bigint} value - The numeric value to convert
5
+ * @returns {string} The number in Gujarati words
8
6
  */
9
- export class Gujarati extends SouthAsianLanguage {
10
- }
11
- import { SouthAsianLanguage } from '../classes/south-asian-language.js';
7
+ export function toWords(value: number | string | bigint): string;