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,148 +0,0 @@
1
- /**
2
- * Base class for Slavic and related languages with complex pluralization.
3
- *
4
- * This class provides a reusable implementation for languages that share:
5
- * - Three-form pluralization (singular/few/many)
6
- * - Gender-aware number forms (masculine/feminine for 1-9)
7
- * - Hundreds, tens, ones decomposition pattern
8
- * - Segment-based large number handling (thousands, millions, etc.)
9
- * - Inherits decimal handling from AbstractLanguage (supports both grouped and
10
- * per-digit modes via the `usePerDigitDecimals` class property).
11
- *
12
- * Used by: Russian (ru), Czech (cs), Polish (pl), Ukrainian (uk), Serbian (sr-Latn),
13
- * Croatian (hr), Lithuanian (lt), Latvian (lv), Hebrew (he), and Biblical Hebrew (hbo).
14
- *
15
- * Subclasses MUST define these properties with language-specific vocabulary:
16
- * - `onesWords` - Object mapping 1-9 to masculine forms (or default forms)
17
- * - `onesFeminineWords` - Object mapping 1-9 to feminine forms (if gender distinction exists)
18
- * - `teensWords` - Object mapping 0-9 to teen numbers (10-19)
19
- * - `twentiesWords` - Object mapping 2-9 to tens (20-90)
20
- * - `hundredsWords` - Object mapping 1-9 to hundreds (100-900) or special hundreds handling
21
- * - `pluralForms` - Object mapping segment indices to [singular, few, many] plural forms
22
- *
23
- * Optional properties:
24
- * - `scaleGenders` - Object mapping segment indices to boolean (true = feminine scale word)
25
- * If not defined, defaults to thousands (index 1) being feminine, others masculine.
26
- *
27
- * @abstract
28
- * @extends AbstractLanguage
29
- */
30
- export class SlavicLanguage extends AbstractLanguage {
31
- /**
32
- * Constructs a SlavicLanguage instance with optional configuration.
33
- *
34
- * @param {Object} [options] Configuration options.
35
- * @param {('masculine'|'feminine')} [options.gender='masculine'] Grammatical gender for number forms.
36
- */
37
- constructor(options?: {
38
- gender?: "feminine" | "masculine" | undefined;
39
- });
40
- /**
41
- * Masculine forms for digits 1-9 (or default forms if no gender distinction).
42
- *
43
- * @type {Object.<number, string>}
44
- */
45
- onesWords: {
46
- [x: number]: string;
47
- };
48
- /**
49
- * Feminine forms for digits 1-9 (if language has gender distinction).
50
- *
51
- * @type {Object.<number, string>}
52
- */
53
- onesFeminineWords: {
54
- [x: number]: string;
55
- };
56
- /**
57
- * Words for teen numbers (10-19).
58
- *
59
- * @type {Object.<number, string>}
60
- */
61
- teensWords: {
62
- [x: number]: string;
63
- };
64
- /**
65
- * Words for multiples of ten (20, 30, 40, etc.).
66
- *
67
- * @type {Object.<number, string>}
68
- */
69
- twentiesWords: {
70
- [x: number]: string;
71
- };
72
- /**
73
- * Words for hundreds (100, 200, 300, etc.) or special hundreds handling.
74
- *
75
- * @type {Object.<number, string>}
76
- */
77
- hundredsWords: {
78
- [x: number]: string;
79
- };
80
- /**
81
- * Plural forms for scale words (thousands, millions, billions, etc.).
82
- * Maps segment indices to [singular, few, many] forms.
83
- *
84
- * @type {Object.<number, string[]>}
85
- */
86
- pluralForms: {
87
- [x: number]: string[];
88
- };
89
- /**
90
- * Gender of each scale word.
91
- * Maps segment indices to boolean: true = feminine, false = masculine.
92
- * Default is empty (all masculine). Languages with feminine thousands
93
- * (Russian, Ukrainian, Serbian, Croatian) should set `{ 1: true }`.
94
- *
95
- * @type {Object.<number, boolean>}
96
- */
97
- scaleGenders: {
98
- [x: number]: boolean;
99
- };
100
- /**
101
- * Whether to omit "one" before scale words (e.g., "thousand" instead of "one thousand").
102
- * When true, 1000 becomes "tysiąc" (Polish) instead of "jeden tysiąc".
103
- * Used by Polish, Czech, and similar languages.
104
- *
105
- * @type {boolean}
106
- */
107
- omitOneBeforeScale: boolean;
108
- /**
109
- * Splits a number string into segments of specified size from right to left.
110
- *
111
- * Example: splitToSegments('1234567', 3) => [1n, 234n, 567n]
112
- * This represents: 1 million + 234 thousand + 567 ones
113
- *
114
- * @param {string} numberString The number as a string.
115
- * @param {number} segmentSize Segment size (typically 3 for thousands grouping).
116
- * @returns {bigint[]} Array of BigInt segments from highest to lowest scale.
117
- */
118
- splitToSegments(numberString: string, segmentSize: number): bigint[];
119
- /**
120
- * Extracts individual digits from a number (units, tens, hundreds).
121
- *
122
- * Returns digits in reverse order: [ones, tens, hundreds]
123
- * Example: 456 => [6n, 5n, 4n]
124
- *
125
- * @param {bigint} value The number to extract digits from (0-999).
126
- * @returns {bigint[]} Array of [ones, tens, hundreds] as BigInts.
127
- */
128
- extractDigits(value: bigint): bigint[];
129
- /**
130
- * Selects the correct plural form based on Slavic pluralization rules.
131
- *
132
- * Slavic languages typically use three forms:
133
- * - Form 0 (singular): numbers ending in 1, except 11 (1, 21, 31, 101...)
134
- * - Form 1 (few): numbers ending in 2-4, except 12-14 (2-4, 22-24, 32-34...)
135
- * - Form 2 (many): all other numbers (0, 5-20, 25-30, 100, 111-119...)
136
- *
137
- * Examples using Russian тысяча (thousand):
138
- * - 1, 21, 31... ⇒ тысяча (form 0, singular)
139
- * - 2-4, 22-24, 32-34... ⇒ тысячи (form 1, few)
140
- * - 0, 5-20, 25-30, 100... ⇒ тысяч (form 2, many)
141
- *
142
- * @param {bigint} number The number to check.
143
- * @param {string[]} pluralForms Array of [singular, few, many] forms.
144
- * @returns {string} The appropriate form for the number.
145
- */
146
- pluralize(number: bigint, pluralForms: string[]): string;
147
- }
148
- import { AbstractLanguage } from './abstract-language.js';
@@ -1,281 +0,0 @@
1
- import { AbstractLanguage } from './abstract-language.js'
2
-
3
- /**
4
- * Base class for Slavic and related languages with complex pluralization.
5
- *
6
- * This class provides a reusable implementation for languages that share:
7
- * - Three-form pluralization (singular/few/many)
8
- * - Gender-aware number forms (masculine/feminine for 1-9)
9
- * - Hundreds, tens, ones decomposition pattern
10
- * - Segment-based large number handling (thousands, millions, etc.)
11
- * - Inherits decimal handling from AbstractLanguage (supports both grouped and
12
- * per-digit modes via the `usePerDigitDecimals` class property).
13
- *
14
- * Used by: Russian (ru), Czech (cs), Polish (pl), Ukrainian (uk), Serbian (sr-Latn),
15
- * Croatian (hr), Lithuanian (lt), Latvian (lv), Hebrew (he), and Biblical Hebrew (hbo).
16
- *
17
- * Subclasses MUST define these properties with language-specific vocabulary:
18
- * - `onesWords` - Object mapping 1-9 to masculine forms (or default forms)
19
- * - `onesFeminineWords` - Object mapping 1-9 to feminine forms (if gender distinction exists)
20
- * - `teensWords` - Object mapping 0-9 to teen numbers (10-19)
21
- * - `twentiesWords` - Object mapping 2-9 to tens (20-90)
22
- * - `hundredsWords` - Object mapping 1-9 to hundreds (100-900) or special hundreds handling
23
- * - `pluralForms` - Object mapping segment indices to [singular, few, many] plural forms
24
- *
25
- * Optional properties:
26
- * - `scaleGenders` - Object mapping segment indices to boolean (true = feminine scale word)
27
- * If not defined, defaults to thousands (index 1) being feminine, others masculine.
28
- *
29
- * @abstract
30
- * @extends AbstractLanguage
31
- */
32
- export class SlavicLanguage extends AbstractLanguage {
33
- // ============================================================================
34
- // Required Properties (subclasses must define)
35
- // ============================================================================
36
-
37
- /**
38
- * Masculine forms for digits 1-9 (or default forms if no gender distinction).
39
- *
40
- * @type {Object.<number, string>}
41
- */
42
- onesWords = {}
43
-
44
- /**
45
- * Feminine forms for digits 1-9 (if language has gender distinction).
46
- *
47
- * @type {Object.<number, string>}
48
- */
49
- onesFeminineWords = {}
50
-
51
- /**
52
- * Words for teen numbers (10-19).
53
- *
54
- * @type {Object.<number, string>}
55
- */
56
- teensWords = {}
57
-
58
- /**
59
- * Words for multiples of ten (20, 30, 40, etc.).
60
- *
61
- * @type {Object.<number, string>}
62
- */
63
- twentiesWords = {}
64
-
65
- /**
66
- * Words for hundreds (100, 200, 300, etc.) or special hundreds handling.
67
- *
68
- * @type {Object.<number, string>}
69
- */
70
- hundredsWords = {}
71
-
72
- /**
73
- * Plural forms for scale words (thousands, millions, billions, etc.).
74
- * Maps segment indices to [singular, few, many] forms.
75
- *
76
- * @type {Object.<number, string[]>}
77
- */
78
- pluralForms = {}
79
-
80
- // ============================================================================
81
- // Optional Properties (subclasses may override)
82
- // ============================================================================
83
-
84
- /**
85
- * Gender of each scale word.
86
- * Maps segment indices to boolean: true = feminine, false = masculine.
87
- * Default is empty (all masculine). Languages with feminine thousands
88
- * (Russian, Ukrainian, Serbian, Croatian) should set `{ 1: true }`.
89
- *
90
- * @type {Object.<number, boolean>}
91
- */
92
- scaleGenders = {}
93
-
94
- /**
95
- * Whether to omit "one" before scale words (e.g., "thousand" instead of "one thousand").
96
- * When true, 1000 becomes "tysiąc" (Polish) instead of "jeden tysiąc".
97
- * Used by Polish, Czech, and similar languages.
98
- *
99
- * @type {boolean}
100
- */
101
- omitOneBeforeScale = false
102
-
103
- // ============================================================================
104
- // Constructor
105
- // ============================================================================
106
-
107
- /**
108
- * Constructs a SlavicLanguage instance with optional configuration.
109
- *
110
- * @param {Object} [options] Configuration options.
111
- * @param {('masculine'|'feminine')} [options.gender='masculine'] Grammatical gender for number forms.
112
- */
113
- constructor (options = {}) {
114
- super()
115
-
116
- this.setOptions({
117
- gender: 'masculine'
118
- }, options)
119
- }
120
-
121
- // ============================================================================
122
- // Public Methods
123
- // ============================================================================
124
-
125
- /**
126
- * Converts an integer to its word representation.
127
- *
128
- * This method implements the Slavic number construction algorithm:
129
- * 1. Split number into 3-digit segments from right to left (567, 234, 1 for 1,234,567)
130
- * 2. For each segment (processing left to right): convert hundreds, tens, ones
131
- * 3. Apply gender rules: feminine forms for thousands segment or when feminine=true
132
- * 4. Add appropriate pluralized scale word (thousand/million/billion/etc.)
133
- * 5. Join all parts with spaces
134
- *
135
- * @param {bigint} integerPart The integer to convert (non-negative).
136
- * @returns {string} The number in words.
137
- */
138
- integerToWords (integerPart) {
139
- if (integerPart === 0n) {
140
- return this.zeroWord
141
- }
142
-
143
- const words = []
144
- const segments = this.splitToSegments(integerPart.toString(), 3)
145
- let segmentIndex = segments.length
146
-
147
- for (const segmentValue of segments) {
148
- segmentIndex = segmentIndex - 1
149
-
150
- if (segmentValue === 0n) {
151
- continue
152
- }
153
-
154
- const [onesDigit, tensDigit, hundredsDigit] = this.extractDigits(segmentValue)
155
-
156
- if (hundredsDigit > 0n) {
157
- words.push(this.hundredsWords[hundredsDigit])
158
- }
159
-
160
- if (tensDigit > 1n) {
161
- words.push(this.twentiesWords[tensDigit])
162
- }
163
-
164
- // Handle teens (10-19) or ones (1-9)
165
- if (tensDigit === 1n) {
166
- // Teens: use teensWords array directly
167
- words.push(this.teensWords[onesDigit])
168
- } else if (onesDigit > 0n) {
169
- // Skip "one" before scale words if omitOneBeforeScale is set
170
- // e.g., Polish says "tysiąc" not "jeden tysiąc" for 1000
171
- const shouldOmitOne = this.omitOneBeforeScale && segmentIndex > 0 && segmentValue === 1n
172
-
173
- if (!shouldOmitOne) {
174
- // Determine if feminine forms should be used:
175
- // 1. Check scaleGenders for this segment index (e.g., thousands = index 1)
176
- // 2. Also use feminine if user requested gender='feminine' for the ones segment
177
- const isScaleFeminine = this.scaleGenders[segmentIndex] === true
178
- const isFeminine = isScaleFeminine || (this.options.gender === 'feminine' && segmentIndex === 0)
179
- const onesArray = isFeminine ? this.onesFeminineWords : this.onesWords
180
- words.push(onesArray[onesDigit])
181
- }
182
- }
183
-
184
- // Add power word (thousand, million, etc.) with proper pluralization
185
- if (segmentIndex > 0) {
186
- words.push(this.pluralize(segmentValue, this.pluralForms[segmentIndex]))
187
- }
188
- }
189
-
190
- return words.join(' ')
191
- }
192
-
193
- // ============================================================================
194
- // Protected Methods (subclasses may call or override)
195
- // ============================================================================
196
-
197
- /**
198
- * Splits a number string into segments of specified size from right to left.
199
- *
200
- * Example: splitToSegments('1234567', 3) => [1n, 234n, 567n]
201
- * This represents: 1 million + 234 thousand + 567 ones
202
- *
203
- * @param {string} numberString The number as a string.
204
- * @param {number} segmentSize Segment size (typically 3 for thousands grouping).
205
- * @returns {bigint[]} Array of BigInt segments from highest to lowest scale.
206
- */
207
- splitToSegments (numberString, segmentSize) {
208
- const segments = []
209
- const stringLength = numberString.length
210
-
211
- if (stringLength > segmentSize) {
212
- const remainderLength = stringLength % segmentSize
213
-
214
- if (remainderLength > 0) {
215
- segments.push(BigInt(numberString.slice(0, remainderLength)))
216
- }
217
-
218
- for (let i = remainderLength; i < stringLength; i += segmentSize) {
219
- segments.push(BigInt(numberString.slice(i, i + segmentSize)))
220
- }
221
- } else {
222
- segments.push(BigInt(numberString))
223
- }
224
-
225
- return segments
226
- }
227
-
228
- /**
229
- * Extracts individual digits from a number (units, tens, hundreds).
230
- *
231
- * Returns digits in reverse order: [ones, tens, hundreds]
232
- * Example: 456 => [6n, 5n, 4n]
233
- *
234
- * @param {bigint} value The number to extract digits from (0-999).
235
- * @returns {bigint[]} Array of [ones, tens, hundreds] as BigInts.
236
- */
237
- extractDigits (value) {
238
- // Direct BigInt arithmetic is faster than string manipulation
239
- const onesPlace = value % 10n
240
- const tensPlace = (value / 10n) % 10n
241
- const hundredsPlace = value / 100n
242
- return [onesPlace, tensPlace, hundredsPlace]
243
- }
244
-
245
- /**
246
- * Selects the correct plural form based on Slavic pluralization rules.
247
- *
248
- * Slavic languages typically use three forms:
249
- * - Form 0 (singular): numbers ending in 1, except 11 (1, 21, 31, 101...)
250
- * - Form 1 (few): numbers ending in 2-4, except 12-14 (2-4, 22-24, 32-34...)
251
- * - Form 2 (many): all other numbers (0, 5-20, 25-30, 100, 111-119...)
252
- *
253
- * Examples using Russian тысяча (thousand):
254
- * - 1, 21, 31... ⇒ тысяча (form 0, singular)
255
- * - 2-4, 22-24, 32-34... ⇒ тысячи (form 1, few)
256
- * - 0, 5-20, 25-30, 100... ⇒ тысяч (form 2, many)
257
- *
258
- * @param {bigint} number The number to check.
259
- * @param {string[]} pluralForms Array of [singular, few, many] forms.
260
- * @returns {string} The appropriate form for the number.
261
- */
262
- pluralize (number, pluralForms) {
263
- const remainder100 = number % 100n
264
- const remainder10 = number % 10n
265
-
266
- // Check if in 11-19 range (special case)
267
- if (remainder100 >= 10n && remainder100 <= 20n) {
268
- return pluralForms[2] // Always use "many" form for 11-20
269
- }
270
-
271
- if (remainder10 === 1n) {
272
- return pluralForms[0] // Singular
273
- }
274
-
275
- if (remainder10 >= 2n && remainder10 <= 4n) {
276
- return pluralForms[1] // Few (2-4)
277
- }
278
-
279
- return pluralForms[2] // Many
280
- }
281
- }
@@ -1,70 +0,0 @@
1
- /**
2
- * Base class for South Asian languages with shared grouping patterns.
3
- *
4
- * This class provides a reusable implementation for South Asian languages that share:
5
- * - Indian-style number grouping: last 3 digits, then 2-2 (1,23,45,67,89)
6
- * - Lakh (100,000), Crore (10,000,000), Arab (1,000,000,000) scale words
7
- * - Standard negative and decimal handling (inherits AbstractLanguage decimal logic,
8
- * including `usePerDigitDecimals` support when set by subclasses)
9
- *
10
- * Used by: Hindi (hi), Bengali (bn), Urdu (ur), Punjabi (pa), Marathi (mr), Gujarati (gu), Kannada (kn)
11
- *
12
- * Subclasses MUST define language-specific vocabulary via class properties:
13
- * - `belowHundredWords` array with digit and teen words (0-99)
14
- * - `hundredWord` string used inside `segmentToWords`
15
- * - `scaleWords` array with grouping words (hazaar, lakh, crore, etc.) indexed by grouping level
16
- * - `negativeWord`, `decimalSeparatorWord`, `zeroWord`, `wordSeparator`
17
- *
18
- * @abstract
19
- * @extends AbstractLanguage
20
- */
21
- export class SouthAsianLanguage extends AbstractLanguage {
22
- /**
23
- * Array of words for numbers 0-99 (digits and teens).
24
- * Index directly: belowHundredWords[0] through belowHundredWords[99].
25
- * @type {Array<string>}
26
- */
27
- belowHundredWords: Array<string>;
28
- /**
29
- * Word for "hundred" in the language (e.g., 'सौ' in Hindi, 'শত' in Bengali).
30
- * Used to construct hundreds (e.g., "1 hundred", "2 hundred").
31
- * @type {string}
32
- */
33
- hundredWord: string;
34
- /**
35
- * Array of scale words for Indian-style grouping (hazaar, lakh, crore, arab, etc.).
36
- * Index 0 contains empty string (ones place has no scale word).
37
- * Index 1 is for thousands, Index 2 for lakhs, Index 3 for crores, etc.
38
- * @type {Array<string>}
39
- */
40
- scaleWords: Array<string>;
41
- /**
42
- * Splits a number into Indian numbering system segments.
43
- *
44
- * The Indian system segments differently than Western (3-3-3) systems:
45
- * - First segment (rightmost): Up to 3 digits (ones, tens, hundreds)
46
- * - Subsequent segments: Exactly 2 digits each (thousands, lakhs, crores, etc.)
47
- *
48
- * This creates the familiar Indian comma pattern: 1,23,45,67,890
49
- *
50
- * @protected
51
- * @param {bigint} integerPart The integer to split into segments.
52
- * @returns {Array<number>} Array of segments from most significant to least significant.
53
- *
54
- * @example
55
- * // splitToSegments(1234567n) → [12, 34, 567]
56
- * // Reads as: 12 lakhs, 34 thousands, 567 units
57
- * // splitToSegments(98765432n) → [9, 87, 65, 432]
58
- * // Reads as: 9 crores, 87 lakhs, 65 thousands, 432 units
59
- */
60
- protected splitToSegments(integerPart: bigint): Array<number>;
61
- /**
62
- * Converts a segment (0-999) to words.
63
- *
64
- * @protected
65
- * @param {number} segmentValue Value between 0 and 999.
66
- * @returns {string} Language-specific word representation.
67
- */
68
- protected segmentToWords(segmentValue: number): string;
69
- }
70
- import { AbstractLanguage } from './abstract-language.js';
@@ -1,154 +0,0 @@
1
- import { AbstractLanguage } from './abstract-language.js'
2
-
3
- /**
4
- * Base class for South Asian languages with shared grouping patterns.
5
- *
6
- * This class provides a reusable implementation for South Asian languages that share:
7
- * - Indian-style number grouping: last 3 digits, then 2-2 (1,23,45,67,89)
8
- * - Lakh (100,000), Crore (10,000,000), Arab (1,000,000,000) scale words
9
- * - Standard negative and decimal handling (inherits AbstractLanguage decimal logic,
10
- * including `usePerDigitDecimals` support when set by subclasses)
11
- *
12
- * Used by: Hindi (hi), Bengali (bn), Urdu (ur), Punjabi (pa), Marathi (mr), Gujarati (gu), Kannada (kn)
13
- *
14
- * Subclasses MUST define language-specific vocabulary via class properties:
15
- * - `belowHundredWords` array with digit and teen words (0-99)
16
- * - `hundredWord` string used inside `segmentToWords`
17
- * - `scaleWords` array with grouping words (hazaar, lakh, crore, etc.) indexed by grouping level
18
- * - `negativeWord`, `decimalSeparatorWord`, `zeroWord`, `wordSeparator`
19
- *
20
- * @abstract
21
- * @extends AbstractLanguage
22
- */
23
- export class SouthAsianLanguage extends AbstractLanguage {
24
- // ============================================================================
25
- // Required Properties (subclasses must define)
26
- // ============================================================================
27
-
28
- /**
29
- * Array of words for numbers 0-99 (digits and teens).
30
- * Index directly: belowHundredWords[0] through belowHundredWords[99].
31
- * @type {Array<string>}
32
- */
33
- belowHundredWords
34
-
35
- /**
36
- * Word for "hundred" in the language (e.g., 'सौ' in Hindi, 'শত' in Bengali).
37
- * Used to construct hundreds (e.g., "1 hundred", "2 hundred").
38
- * @type {string}
39
- */
40
- hundredWord
41
-
42
- /**
43
- * Array of scale words for Indian-style grouping (hazaar, lakh, crore, arab, etc.).
44
- * Index 0 contains empty string (ones place has no scale word).
45
- * Index 1 is for thousands, Index 2 for lakhs, Index 3 for crores, etc.
46
- * @type {Array<string>}
47
- */
48
- scaleWords
49
-
50
- // ============================================================================
51
- // Protected Methods (subclasses may call or override)
52
- // ============================================================================
53
-
54
- /**
55
- * Splits a number into Indian numbering system segments.
56
- *
57
- * The Indian system segments differently than Western (3-3-3) systems:
58
- * - First segment (rightmost): Up to 3 digits (ones, tens, hundreds)
59
- * - Subsequent segments: Exactly 2 digits each (thousands, lakhs, crores, etc.)
60
- *
61
- * This creates the familiar Indian comma pattern: 1,23,45,67,890
62
- *
63
- * @protected
64
- * @param {bigint} integerPart The integer to split into segments.
65
- * @returns {Array<number>} Array of segments from most significant to least significant.
66
- *
67
- * @example
68
- * // splitToSegments(1234567n) → [12, 34, 567]
69
- * // Reads as: 12 lakhs, 34 thousands, 567 units
70
- * // splitToSegments(98765432n) → [9, 87, 65, 432]
71
- * // Reads as: 9 crores, 87 lakhs, 65 thousands, 432 units
72
- */
73
- splitToSegments (integerPart) {
74
- const numStr = integerPart.toString()
75
-
76
- if (numStr.length <= 3) {
77
- return [Number(numStr)]
78
- }
79
-
80
- const segments = []
81
- const last3 = numStr.slice(-3)
82
- segments.unshift(Number(last3))
83
-
84
- let remaining = numStr.slice(0, -3)
85
- while (remaining.length > 0) {
86
- const segment = remaining.slice(-2)
87
- segments.unshift(Number(segment))
88
- remaining = remaining.slice(0, -2)
89
- }
90
-
91
- return segments
92
- }
93
-
94
- /**
95
- * Converts a segment (0-999) to words.
96
- *
97
- * @protected
98
- * @param {number} segmentValue Value between 0 and 999.
99
- * @returns {string} Language-specific word representation.
100
- */
101
- segmentToWords (segmentValue) {
102
- if (segmentValue === 0) return ''
103
- if (segmentValue < 100) return this.belowHundredWords[segmentValue]
104
-
105
- const hundreds = Math.trunc(segmentValue / 100)
106
- const remainder = segmentValue % 100
107
- const parts = []
108
-
109
- if (hundreds === 1) {
110
- parts.push(this.belowHundredWords[1] + ' ' + this.hundredWord)
111
- } else {
112
- parts.push(this.belowHundredWords[hundreds] + ' ' + this.hundredWord)
113
- }
114
-
115
- if (remainder > 0) {
116
- parts.push(this.belowHundredWords[remainder])
117
- }
118
-
119
- return parts.join(' ')
120
- }
121
-
122
- // ============================================================================
123
- // Public Methods
124
- // ============================================================================
125
-
126
- /**
127
- * Converts integer to cardinal words using South Asian grouping.
128
- *
129
- * @param {bigint} integerPart Number to convert.
130
- * @returns {string} Cardinal representation.
131
- */
132
- integerToWords (integerPart) {
133
- if (integerPart === 0n) {
134
- return this.zeroWord
135
- }
136
-
137
- const segments = this.splitToSegments(integerPart)
138
- const segmentCount = segments.length
139
- const words = []
140
-
141
- for (let i = 0; i < segmentCount; i++) {
142
- const segmentValue = segments[i]
143
- if (segmentValue === 0) continue
144
-
145
- const scaleIndex = segmentCount - i - 1
146
- words.push(this.segmentToWords(segmentValue))
147
- if (scaleIndex > 0 && this.scaleWords[scaleIndex]) {
148
- words.push(this.scaleWords[scaleIndex])
149
- }
150
- }
151
-
152
- return words.join(' ').trim()
153
- }
154
- }
@@ -1,26 +0,0 @@
1
- /**
2
- * Base class for Turkic languages with shared grammar patterns.
3
- *
4
- * This class provides a reusable implementation for Turkic languages that share:
5
- * - Space-separated number combinations
6
- * - Implicit 'bir' (one) before hundreds and thousands
7
- * - Simple multiplication/addition logic
8
- * - Consistent magnitude handling
9
- * - Inherits decimal handling from AbstractLanguage via GreedyScaleLanguage
10
- * (supports both grouped and per-digit modes via the `usePerDigitDecimals` class property).
11
- *
12
- * Used by: Turkish (TR), Azerbaijani (AZ)
13
- *
14
- * Subclasses MUST define (from GreedyScaleLanguage requirements):
15
- * - `scaleWords` array of [value, word] tuples as a class property (ordered descending by value).
16
- * Optionally, language-specific class properties (e.g., `negativeWord`, `zeroWord`, `decimalSeparatorWord`, `wordSeparator`).
17
- *
18
- * TurkicLanguage provides a default `combineWordSets()` implementation; subclasses may override
19
- * if specialized combine logic is needed (unlikely for Turkic languages).
20
- *
21
- * @abstract
22
- * @extends GreedyScaleLanguage
23
- */
24
- export class TurkicLanguage extends GreedyScaleLanguage {
25
- }
26
- import { GreedyScaleLanguage } from './greedy-scale-language.js';