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
@@ -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,356 @@
1
- import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'
2
-
3
1
  /**
4
- * French language converter.
2
+ * French language converter - Functional Implementation
3
+ *
4
+ * Self-contained module with its own input validation, ready for subpath exports.
5
5
  *
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
6
+ * French-specific rules:
7
+ * - Vigesimal patterns: 70 = soixante-dix, 80 = quatre-vingts, 90 = quatre-vingt-dix
8
+ * - "et" conjunction: vingt et un (21), soixante et onze (71), but NOT quatre-vingt-un
9
+ * - Pluralization: "cents" loses 's' when followed by more digits
10
+ * - Long scale with -ard forms: milliard, billiard, trilliard
11
+ * - Omit "un" before mille
10
12
  */
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
13
 
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]
14
+ import { parseNumericValue } from '../utils/parse-numeric.js'
15
+ import { validateOptions } from '../utils/validate-options.js'
16
+
17
+ // ============================================================================
18
+ // Vocabulary (module-level constants)
19
+ // ============================================================================
20
+
21
+ const ONES = ['', 'un', 'deux', 'trois', 'quatre', 'cinq', 'six', 'sept', 'huit', 'neuf']
22
+ const TEENS = ['dix', 'onze', 'douze', 'treize', 'quatorze', 'quinze', 'seize', 'dix-sept', 'dix-huit', 'dix-neuf']
23
+ const TENS = ['', '', 'vingt', 'trente', 'quarante', 'cinquante', 'soixante']
24
+
25
+ // Scale words (even indices: million, billion, trillion, quadrillion)
26
+ const SCALES = ['million', 'billion', 'trillion', 'quadrillion']
27
+ const SCALES_ARD = ['milliard', 'billiard', 'trilliard', 'quadrilliard']
28
+
29
+ const THOUSAND = 'mille'
30
+ const HUNDRED = 'cent'
31
+ const ZERO = 'zéro'
32
+ const NEGATIVE = 'moins'
33
+ const DECIMAL_SEP = 'virgule'
34
+
35
+ // ============================================================================
36
+ // Segment Building
37
+ // ============================================================================
38
+
39
+ /**
40
+ * Builds segment word for 0-999.
41
+ * Returns object with { word, endsWithCents, endsWithVingts } for pluralization handling.
42
+ */
43
+ function buildSegment (n) {
44
+ if (n === 0) return { word: '', endsWithCents: false, endsWithVingts: false }
73
45
 
74
- if (precedingValue === 1n) {
75
- if (followingValue < 1_000_000n) {
76
- return following
46
+ const tensOnes = n % 100
47
+ const hundreds = Math.floor(n / 100)
48
+
49
+ const parts = []
50
+ let endsWithCents = false
51
+ let endsWithVingts = false
52
+
53
+ // Hundreds
54
+ if (hundreds > 0) {
55
+ if (hundreds === 1) {
56
+ if (tensOnes === 0) {
57
+ parts.push(HUNDRED)
58
+ } else {
59
+ parts.push(HUNDRED)
77
60
  }
78
61
  } 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)
62
+ if (tensOnes === 0) {
63
+ // "deux cents", "trois cents" (with 's')
64
+ parts.push(ONES[hundreds] + ' ' + HUNDRED + 's')
65
+ endsWithCents = true
66
+ } else {
67
+ // "deux cent", "trois cent" (no 's' when followed by more)
68
+ parts.push(ONES[hundreds] + ' ' + HUNDRED)
85
69
  }
70
+ }
71
+ }
72
+
73
+ // Tens and ones - vigesimal pattern
74
+ if (tensOnes === 0) {
75
+ // Just hundreds, nothing more
76
+ } else if (tensOnes < 10) {
77
+ // Single digit
78
+ parts.push(ONES[tensOnes])
79
+ } else if (tensOnes < 17) {
80
+ // 10-16: regular teens
81
+ parts.push(TEENS[tensOnes - 10])
82
+ } else if (tensOnes < 20) {
83
+ // 17-19: dix-sept, dix-huit, dix-neuf
84
+ parts.push(TEENS[tensOnes - 10])
85
+ } else if (tensOnes < 70) {
86
+ // 20-69: standard tens + ones
87
+ const t = Math.floor(tensOnes / 10)
88
+ const o = tensOnes % 10
89
+ if (o === 0) {
90
+ parts.push(TENS[t])
91
+ } else if (o === 1) {
92
+ // "et un" for 21, 31, 41, 51, 61
93
+ parts.push(TENS[t] + ' et ' + ONES[1])
94
+ } else {
95
+ parts.push(TENS[t] + '-' + ONES[o])
96
+ }
97
+ } else if (tensOnes < 80) {
98
+ // 70-79: soixante-dix, soixante et onze, soixante-douze...
99
+ const remainder = tensOnes - 60
100
+ if (remainder === 11) {
101
+ // 71: soixante et onze
102
+ parts.push('soixante et onze')
103
+ } else {
104
+ // 70, 72-79: soixante-dix, soixante-douze...
105
+ parts.push('soixante-' + TEENS[remainder - 10])
106
+ }
107
+ } else if (tensOnes === 80) {
108
+ // 80: quatre-vingts (with 's')
109
+ parts.push('quatre-vingts')
110
+ endsWithVingts = true
111
+ } else if (tensOnes < 100) {
112
+ // 81-99: quatre-vingt-un, quatre-vingt-dix...
113
+ const remainder = tensOnes - 80
114
+ if (remainder < 10) {
115
+ // 81-89
116
+ parts.push('quatre-vingt-' + ONES[remainder])
117
+ } else {
118
+ // 90-99
119
+ parts.push('quatre-vingt-' + TEENS[remainder - 10])
120
+ }
121
+ }
122
+
123
+ // Join parts with space (between hundreds and rest)
124
+ return { word: parts.join(' '), endsWithCents, endsWithVingts }
125
+ }
126
+
127
+ // ============================================================================
128
+ // Helper Functions
129
+ // ============================================================================
130
+
131
+ /**
132
+ * Gets scale word for French long scale with -ard pattern.
133
+ *
134
+ * @param {number} scaleIndex - Scale level (1 = thousand, 2 = million, etc.)
135
+ * @param {bigint} segment - Segment value for pluralization
136
+ * @returns {string} Scale word
137
+ */
138
+ function getScaleWord (scaleIndex, segment) {
139
+ if (scaleIndex === 1) return THOUSAND
140
+
141
+ // Even indices (2, 4, 6, 8): million, billion, trillion, quadrillion
142
+ // Odd indices > 1 (3, 5, 7, 9): milliard, billiard, trilliard, quadrilliard
143
+ if (scaleIndex % 2 === 0) {
144
+ const arrayIndex = (scaleIndex / 2) - 1
145
+ const baseWord = SCALES[arrayIndex]
146
+ if (!baseWord) return ''
147
+ return segment > 1n ? baseWord + 's' : baseWord
148
+ } else {
149
+ const arrayIndex = ((scaleIndex - 1) / 2) - 1
150
+ const ardWord = SCALES_ARD[arrayIndex]
151
+ if (!ardWord) return THOUSAND
152
+ return segment > 1n ? ardWord + 's' : ardWord
153
+ }
154
+ }
155
+
156
+ // ============================================================================
157
+ // Conversion Functions
158
+ // ============================================================================
86
159
 
87
- if (
88
- precedingValue < 1000n && followingValue !== 1000n &&
89
- followingWord.at(-1) !== 's' &&
90
- followingValue % 100n === 0n
91
- ) {
92
- followingWord += 's'
160
+ /**
161
+ * Converts a non-negative integer to French words.
162
+ *
163
+ * @param {bigint} n - Non-negative integer to convert
164
+ * @param {boolean} withHyphen - Whether to use hyphen separators
165
+ * @returns {string} French words
166
+ */
167
+ function integerToWords (n, withHyphen = false) {
168
+ if (n === 0n) return ZERO
169
+
170
+ // Fast path: numbers < 1000
171
+ if (n < 1000n) {
172
+ const { word } = buildSegment(Number(n))
173
+ return withHyphen ? word.replace(/ /g, '-') : word
174
+ }
175
+
176
+ // Fast path: numbers < 1,000,000 (thousands)
177
+ if (n < 1_000_000n) {
178
+ const thousands = Number(n / 1000n)
179
+ const remainder = Number(n % 1000n)
180
+
181
+ let result
182
+ if (thousands === 1) {
183
+ // "mille" not "un mille"
184
+ result = THOUSAND
185
+ } else {
186
+ // Check if segment ends with "cents" or "vingts" - need to strip 's' before mille
187
+ const { word: thousandsWord, endsWithCents, endsWithVingts } = buildSegment(thousands)
188
+ let adjustedWord = thousandsWord
189
+ if (endsWithCents || endsWithVingts) {
190
+ adjustedWord = thousandsWord.slice(0, -1) // Remove trailing 's'
93
191
  }
192
+ result = adjustedWord + (withHyphen ? '-' : ' ') + THOUSAND
193
+ }
194
+
195
+ if (remainder > 0) {
196
+ const { word: remainderWord } = buildSegment(remainder)
197
+ result += (withHyphen ? '-' : ' ') + remainderWord
198
+ }
199
+
200
+ if (withHyphen) {
201
+ result = result.replace(/ /g, '-')
94
202
  }
95
203
 
96
- if (followingValue < precedingValue && precedingValue < 100n) {
97
- if (followingValue % 10n === 1n && precedingValue !== 80n) {
98
- return { [`${precedingWord}${this.wordSeparator}et${this.wordSeparator}${followingWord}`]: precedingValue + followingValue }
204
+ return result
205
+ }
206
+
207
+ // For numbers >= 1,000,000, use scale decomposition
208
+ return buildLargeNumberWords(n, withHyphen)
209
+ }
210
+
211
+ /**
212
+ * Builds words for numbers >= 1,000,000.
213
+ *
214
+ * @param {bigint} n - Number >= 1,000,000
215
+ * @param {boolean} withHyphen - Whether to use hyphen separators
216
+ * @returns {string} French words
217
+ */
218
+ function buildLargeNumberWords (n, withHyphen) {
219
+ const numStr = n.toString()
220
+ const len = numStr.length
221
+
222
+ // Build segments of 3 digits from right to left
223
+ const segments = []
224
+ const segmentSize = 3
225
+
226
+ const remainderLen = len % segmentSize
227
+ let pos = 0
228
+ if (remainderLen > 0) {
229
+ segments.push(Number(numStr.slice(0, remainderLen)))
230
+ pos = remainderLen
231
+ }
232
+ while (pos < len) {
233
+ segments.push(Number(numStr.slice(pos, pos + segmentSize)))
234
+ pos += segmentSize
235
+ }
236
+
237
+ // Convert segments to words
238
+ const parts = []
239
+ let scaleIndex = segments.length - 1
240
+
241
+ for (let i = 0; i < segments.length; i++) {
242
+ const segment = segments[i]
243
+
244
+ if (segment !== 0) {
245
+ const scaleWord = scaleIndex > 0 ? getScaleWord(scaleIndex, BigInt(segment)) : ''
246
+ const { word: segWords, endsWithCents, endsWithVingts } = buildSegment(segment)
247
+
248
+ if (scaleIndex === 0) {
249
+ // Units segment
250
+ parts.push(segWords)
251
+ } else if (scaleIndex === 1) {
252
+ // Thousands: "mille" not "un mille"
253
+ if (segment === 1) {
254
+ parts.push(THOUSAND)
255
+ } else {
256
+ // Strip 's' from cents/vingts before mille
257
+ let adjustedWord = segWords
258
+ if (endsWithCents || endsWithVingts) {
259
+ adjustedWord = segWords.slice(0, -1)
260
+ }
261
+ parts.push(adjustedWord)
262
+ parts.push(scaleWord)
263
+ }
264
+ } else {
265
+ // Million and above
266
+ parts.push(segWords)
267
+ parts.push(scaleWord)
99
268
  }
100
- return { [`${precedingWord}-${followingWord}`]: precedingValue + followingValue }
101
269
  }
102
270
 
103
- if (followingValue > precedingValue) return { [`${precedingWord}${this.wordSeparator}${followingWord}`]: precedingValue * followingValue }
104
- return { [`${precedingWord}${this.wordSeparator}${followingWord}`]: precedingValue + followingValue }
271
+ scaleIndex--
272
+ }
273
+
274
+ const sep = withHyphen ? '-' : ' '
275
+ let result = parts.join(sep)
276
+
277
+ if (withHyphen) {
278
+ result = result.replace(/ /g, '-')
279
+ }
280
+
281
+ return result
282
+ }
283
+
284
+ /**
285
+ * Converts decimal digits to French words.
286
+ *
287
+ * @param {string} decimalPart - Decimal digits (without the point)
288
+ * @param {boolean} withHyphen - Whether to use hyphen separators
289
+ * @returns {string} French words for decimal part
290
+ */
291
+ function decimalPartToWords (decimalPart, withHyphen) {
292
+ let result = ''
293
+ const sep = withHyphen ? '-' : ' '
294
+
295
+ // Handle leading zeros
296
+ let i = 0
297
+ while (i < decimalPart.length && decimalPart[i] === '0') {
298
+ if (result) result += sep
299
+ result += ZERO
300
+ i++
301
+ }
302
+
303
+ // Convert remainder as a single number
304
+ const remainder = decimalPart.slice(i)
305
+ if (remainder) {
306
+ if (result) result += sep
307
+ result += integerToWords(BigInt(remainder), withHyphen)
105
308
  }
309
+
310
+ return result
106
311
  }
312
+
313
+ /**
314
+ * Converts a numeric value to French words.
315
+ *
316
+ * This is the main public API. It accepts any valid numeric input
317
+ * (number, string, or bigint) and handles parsing internally.
318
+ *
319
+ * @param {number | string | bigint} value - The numeric value to convert
320
+ * @param {Object} [options] - Optional configuration
321
+ * @param {boolean} [options.withHyphenSeparator=false] - Use hyphens between all words
322
+ * @returns {string} The number in French words
323
+ * @throws {TypeError} If value is not a valid numeric type
324
+ * @throws {Error} If value is not a valid number format
325
+ *
326
+ * @example
327
+ * toWords(21) // 'vingt et un'
328
+ * toWords(80) // 'quatre-vingts'
329
+ * toWords(1000000) // 'un million'
330
+ */
331
+ function toWords (value, options) {
332
+ options = validateOptions(options)
333
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
334
+ const withHyphen = options.withHyphenSeparator || false
335
+
336
+ let result = ''
337
+ const sep = withHyphen ? '-' : ' '
338
+
339
+ if (isNegative) {
340
+ result = NEGATIVE + sep
341
+ }
342
+
343
+ result += integerToWords(integerPart, withHyphen)
344
+
345
+ if (decimalPart) {
346
+ result += sep + DECIMAL_SEP + sep + decimalPartToWords(decimalPart, withHyphen)
347
+ }
348
+
349
+ return result
350
+ }
351
+
352
+ // ============================================================================
353
+ // Public API
354
+ // ============================================================================
355
+
356
+ 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;