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,137 +1,259 @@
1
- import { AbstractLanguage } from '../classes/abstract-language.js'
2
-
3
1
  /**
4
- * Japanese language converter.
2
+ * Japanese 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-9999) at module load.
8
+ * This eliminates all per-call string manipulation for segment conversion.
5
9
  *
6
- * Supports:
7
- * - Kanji numerals (一, 二, 三, etc.)
8
- * - Grouping by(10,000) instead of 1,000
9
- * - Special (one) omission rules (十, 百, 千 but 一万, 一億)
10
+ * Japanese-specific rules (handled in precomputation):
11
+ * - Myriad (万-based) grouping: 4 digits per segment instead of 3
12
+ * - omission: Omit "一" before 十, 百, 千 but NOT before and higher scales
13
+ * - Kanji numerals: 零一二三四五六七八九
14
+ * - No spaces between characters
10
15
  */
11
- export class Japanese extends AbstractLanguage {
12
- negativeWord = 'マイナス'
13
- decimalSeparatorWord = '点'
14
- zeroWord = '零'
15
- wordSeparator = '' // Japanese doesn't use spaces between characters
16
- usePerDigitDecimals = true // Enable digit-by-digit decimal conversion
17
-
18
- // Ones words used for group conversion (1-9)
19
- onesWords = ['一', '二', '三', '四', '五', '六', '七', '八', '九']
20
-
21
- // Scale words for grouping by 10^4
22
- scaleWords = [
23
- '', // 10^0 (ones)
24
- '万', // 10^4 (man)
25
- '億', // 10^8 (oku)
26
- '兆', // 10^12 (chō)
27
- '京', // 10^16 (kei)
28
- '垓', // 10^20 (gai)
29
- '秭', // 10^24 (jo/shi)
30
- '穣', // 10^28 (jō)
31
- '溝', // 10^32 (kō)
32
- '澗', // 10^36 (kan)
33
- '正', // 10^40 (sei)
34
- '載', // 10^44 (sai)
35
- '極', // 10^48 (goku)
36
- '恒河沙', // 10^52 (gōgasha)
37
- '阿僧祇', // 10^56 (asōgi)
38
- '那由他', // 10^60 (nayuta)
39
- '不可思議', // 10^64 (fukashigi)
40
- '無量大数' // 10^68 (muryōtaisū)
41
- ]
42
-
43
- /** Converts a segment of up to 4 digits to Japanese kanji with 一 omission rules. */
44
- segmentToWords (num) {
45
- if (num === 0n) return ''
46
-
47
- const thousands = num / 1000n
48
- const hundreds = (num % 1000n) / 100n
49
- const tens = (num % 100n) / 10n
50
- const ones = num % 10n
51
-
52
- let result = ''
53
-
54
- // Thousands (千)
55
- if (thousands > 0n) {
56
- // Always omit 一 before 千 when thousands === 1
57
- if (thousands === 1n) {
58
- result += '千'
59
- } else {
60
- result += this.onesWords[Number(thousands) - 1] + '千'
61
- }
62
- }
63
16
 
64
- // Hundreds (百)
65
- if (hundreds > 0n) {
66
- // Always omit 一 before 百 when hundreds === 1
67
- if (hundreds === 1n) {
68
- result += '百'
69
- } else {
70
- result += this.onesWords[Number(hundreds) - 1] + '百'
71
- }
17
+ import { parseNumericValue } from '../utils/parse-numeric.js'
18
+
19
+ // ============================================================================
20
+ // Vocabulary (module-level constants)
21
+ // ============================================================================
22
+
23
+ // Ones words (1-9), index 0 unused
24
+ const ONES = ['', '一', '二', '三', '四', '五', '六', '七', '八', '九']
25
+
26
+ // Scale words for powers of 10,000 (万-based system)
27
+ // Index 0 = 万 (10^4), 1 = 億 (10^8), 2 = 兆 (10^12), etc.
28
+ const SCALES = [
29
+ '万', // 10^4 (man)
30
+ '億', // 10^8 (oku)
31
+ '兆', // 10^12 (chō)
32
+ '京', // 10^16 (kei)
33
+ '垓', // 10^20 (gai)
34
+ '秭', // 10^24 (jo/shi)
35
+ '穣', // 10^28 (jō)
36
+ '溝', // 10^32 (kō)
37
+ '澗', // 10^36 (kan)
38
+ '正', // 10^40 (sei)
39
+ '載', // 10^44 (sai)
40
+ '極', // 10^48 (goku)
41
+ '恒河沙', // 10^52 (gōgasha)
42
+ '阿僧祇', // 10^56 (asōgi)
43
+ '那由他', // 10^60 (nayuta)
44
+ '不可思議', // 10^64 (fukashigi)
45
+ '無量大数' // 10^68 (muryōtaisū)
46
+ ]
47
+
48
+ const ZERO = '零'
49
+ const NEGATIVE = 'マイナス'
50
+ const DECIMAL_SEP = '点'
51
+
52
+ // Internal scale words (within 4-digit segments)
53
+ const TEN = '十'
54
+ const HUNDRED = '百'
55
+ const THOUSAND = '千'
56
+
57
+ // ============================================================================
58
+ // Precomputed Lookup Tables (built once at module load)
59
+ // ============================================================================
60
+
61
+ /**
62
+ * Builds segment word for 0-9999 with 一 omission rules.
63
+ * - Omit 一 before 十, 百, 千
64
+ * Only used during table construction.
65
+ */
66
+ function buildSegment (n) {
67
+ if (n === 0) return ''
68
+
69
+ const ones = n % 10
70
+ const tens = Math.floor(n / 10) % 10
71
+ const hundreds = Math.floor(n / 100) % 10
72
+ const thousands = Math.floor(n / 1000)
73
+
74
+ let result = ''
75
+
76
+ // Thousands (千) - omit 一 when 1
77
+ if (thousands > 0) {
78
+ if (thousands === 1) {
79
+ result += THOUSAND
80
+ } else {
81
+ result += ONES[thousands] + THOUSAND
72
82
  }
83
+ }
73
84
 
74
- // Tens ()
75
- if (tens > 0n) {
76
- // Always omit 一 before 十 when tens === 1
77
- if (tens === 1n) {
78
- result += '十'
79
- } else {
80
- result += this.onesWords[Number(tens) - 1] + '十'
81
- }
85
+ // Hundreds () - omit 一 when 1
86
+ if (hundreds > 0) {
87
+ if (hundreds === 1) {
88
+ result += HUNDRED
89
+ } else {
90
+ result += ONES[hundreds] + HUNDRED
82
91
  }
92
+ }
83
93
 
84
- // Ones
85
- if (ones > 0n) {
86
- result += this.onesWords[Number(ones) - 1]
94
+ // Tens (十) - omit 一 when 1
95
+ if (tens > 0) {
96
+ if (tens === 1) {
97
+ result += TEN
98
+ } else {
99
+ result += ONES[tens] + TEN
87
100
  }
101
+ }
88
102
 
89
- return result
103
+ // Ones
104
+ if (ones > 0) {
105
+ result += ONES[ones]
90
106
  }
91
107
 
92
- /** Converts integer part using Japanese 万-based grouping system. */
93
- integerToWords (integerPart) {
94
- if (integerPart === 0n) {
95
- return this.zeroWord
96
- }
108
+ return result
109
+ }
97
110
 
98
- let temp = integerPart
99
- let scaleIndex = 0
100
- const groups = []
111
+ // Precompute all 10000 segment words (0-9999)
112
+ // SEGMENTS[n] gives the Japanese word for n within a segment
113
+ const SEGMENTS = new Array(10000)
114
+ for (let i = 0; i < 10000; i++) {
115
+ SEGMENTS[i] = buildSegment(i)
116
+ }
101
117
 
102
- // Split into groups of 4 digits (万-based system)
103
- while (temp > 0n) {
104
- const group = temp % 10000n
105
- if (group > 0n) {
106
- groups.push({ value: group, scale: scaleIndex })
107
- }
108
- temp = temp / 10000n
109
- scaleIndex++
118
+ // ============================================================================
119
+ // Conversion Functions
120
+ // ============================================================================
121
+
122
+ /**
123
+ * Converts a non-negative integer to Japanese words.
124
+ *
125
+ * @param {bigint} n - Non-negative integer to convert
126
+ * @returns {string} Japanese kanji words
127
+ */
128
+ function integerToWords (n) {
129
+ if (n === 0n) return ZERO
130
+
131
+ // Fast path: numbers < 10000 (direct lookup)
132
+ if (n < 10000n) {
133
+ return SEGMENTS[Number(n)]
134
+ }
135
+
136
+ // Fast path: numbers < 100,000,000 (万 range)
137
+ if (n < 100_000_000n) {
138
+ const man = Number(n / 10000n)
139
+ const remainder = Number(n % 10000n)
140
+
141
+ // For 万 and above, we need 一 before the scale word when segment is 1
142
+ let result
143
+ if (man === 1) {
144
+ result = '一' + SCALES[0] // 一万
145
+ } else {
146
+ result = SEGMENTS[man] + SCALES[0]
147
+ }
148
+
149
+ if (remainder > 0) {
150
+ result += SEGMENTS[remainder]
110
151
  }
111
152
 
112
- // Reverse to process from highest to lowest
113
- groups.reverse()
153
+ return result
154
+ }
114
155
 
115
- let result = ''
156
+ // For numbers >= 100,000,000, use scale decomposition
157
+ return buildLargeNumberWords(n)
158
+ }
116
159
 
117
- for (let i = 0; i < groups.length; i++) {
118
- const { value, scale } = groups[i]
160
+ /**
161
+ * Builds words for numbers >= 100,000,000.
162
+ * Uses BigInt modulo for 4-digit (myriad) segment extraction.
163
+ *
164
+ * @param {bigint} n - Number >= 100,000,000
165
+ * @returns {string} Japanese kanji words
166
+ */
167
+ function buildLargeNumberWords (n) {
168
+ // Extract segments using BigInt modulo (faster than string slicing)
169
+ // Segments stored least-significant first (index 0 = units, 1 = 万, etc.)
170
+ const segments = []
171
+ let temp = n
172
+ while (temp > 0n) {
173
+ segments.push(Number(temp % 10000n))
174
+ temp = temp / 10000n
175
+ }
119
176
 
120
- const groupStr = this.segmentToWords(value)
177
+ // Build result string directly (process from most-significant to least)
178
+ let result = ''
121
179
 
122
- // For scales >= 1 (万 and above), always add the scale word
123
- if (scale >= 1) {
124
- // Special case: if group is 1 and scale >= 1, we need 一 before the scale
125
- if (value === 1n) {
126
- result += '一' + this.scaleWords[scale]
127
- } else {
128
- result += groupStr + this.scaleWords[scale]
129
- }
180
+ for (let i = segments.length - 1; i >= 0; i--) {
181
+ const segment = segments[i]
182
+ if (segment === 0) continue
183
+
184
+ if (i > 0) {
185
+ // For scales >= 万, we need 一 before scale word when segment is 1
186
+ if (segment === 1) {
187
+ result += '一' + SCALES[i - 1]
130
188
  } else {
131
- result += groupStr
189
+ result += SEGMENTS[segment] + SCALES[i - 1]
132
190
  }
191
+ } else {
192
+ // Units segment (no scale word)
193
+ result += SEGMENTS[segment]
133
194
  }
195
+ }
134
196
 
135
- return result
197
+ return result || ZERO
198
+ }
199
+
200
+ /**
201
+ * Converts decimal digits to Japanese words (digit by digit).
202
+ *
203
+ * @param {string} decimalPart - Decimal digits (without the point)
204
+ * @returns {string} Japanese kanji words for decimal part
205
+ */
206
+ function decimalPartToWords (decimalPart) {
207
+ let result = ''
208
+
209
+ for (let i = 0; i < decimalPart.length; i++) {
210
+ const digit = parseInt(decimalPart[i], 10)
211
+ if (digit === 0) {
212
+ result += ZERO
213
+ } else {
214
+ result += ONES[digit]
215
+ }
216
+ }
217
+
218
+ return result
219
+ }
220
+
221
+ /**
222
+ * Converts a numeric value to Japanese words.
223
+ *
224
+ * This is the main public API. It accepts any valid numeric input
225
+ * (number, string, or bigint) and handles parsing internally.
226
+ *
227
+ * @param {number | string | bigint} value - The numeric value to convert
228
+ * @returns {string} The number in Japanese kanji words
229
+ * @throws {TypeError} If value is not a valid numeric type
230
+ * @throws {Error} If value is not a valid number format
231
+ *
232
+ * @example
233
+ * toWords(42) // '四十二'
234
+ * toWords(10000) // '一万'
235
+ * toWords(100000000) // '一億'
236
+ */
237
+ function toWords (value) {
238
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
239
+
240
+ let result = ''
241
+
242
+ if (isNegative) {
243
+ result = NEGATIVE
136
244
  }
245
+
246
+ result += integerToWords(integerPart)
247
+
248
+ if (decimalPart) {
249
+ result += DECIMAL_SEP + decimalPartToWords(decimalPart)
250
+ }
251
+
252
+ return result
137
253
  }
254
+
255
+ // ============================================================================
256
+ // Public API
257
+ // ============================================================================
258
+
259
+ export { toWords }
@@ -1,11 +1,7 @@
1
1
  /**
2
- * Kannada language converter.
2
+ * Converts a numeric value to Kannada words.
3
3
  *
4
- * Supports:
5
- * - Indian numbering system (ಸಾವಿರ, ಲಕ್ಷ, ಕೋಟಿ)
6
- * - Kannada 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 Kannada words
8
6
  */
9
- export class Kannada extends SouthAsianLanguage {
10
- }
11
- import { SouthAsianLanguage } from '../classes/south-asian-language.js';
7
+ export function toWords(value: number | string | bigint): string;
@@ -1,42 +1,137 @@
1
- import { SouthAsianLanguage } from '../classes/south-asian-language.js'
2
-
3
1
  /**
4
- * Kannada language converter.
2
+ * Kannada language converter - Functional Implementation
3
+ *
4
+ * Self-contained converter for South Asian numbering.
5
5
  *
6
- * Supports:
6
+ * Key features:
7
7
  * - Indian numbering system (ಸಾವಿರ, ಲಕ್ಷ, ಕೋಟಿ)
8
8
  * - Kannada script
9
+ * - 3-2-2 grouping pattern (last 3 digits, then groups of 2)
9
10
  * - Complete word forms for 0-99
11
+ * - Per-digit decimal reading
10
12
  */
11
- export class Kannada extends SouthAsianLanguage {
12
- negativeWord = 'ಋಣಾತ್ಮಕ'
13
- decimalSeparatorWord = 'ದಶಮಾಂಶ'
14
- zeroWord = 'ಸೊನ್ನೆ'
15
- hundredWord = 'ನೂರು'
16
- usePerDigitDecimals = true
17
-
18
- belowHundredWords = [
19
- 'ಸೊನ್ನೆ', 'ಒಂದು', 'ಎರಡು', 'ಮೂರು', 'ನಾಲ್ಕು', 'ಐದು', 'ಆರು', 'ಏಳು', 'ಎಂಟು', 'ಒಂಬತ್ತು',
20
- 'ಹತ್ತು', 'ಹನ್ನೊಂದು', 'ಹನ್ನೆರಡು', 'ಹದಿಮೂರು', 'ಹದಿನಾಲ್ಕು', 'ಹದಿನೈದು', 'ಹದಿನಾರು', 'ಹದಿನೇಳು', 'ಹದಿನೆಂಟು', 'ಹತ್ತೊಂಬತ್ತು',
21
- 'ಇಪ್ಪತ್ತು', 'ಇಪ್ಪತ್ತೊಂದು', 'ಇಪ್ಪತ್ತೆರಡು', 'ಇಪ್ಪತ್ತಮೂರು', 'ಇಪ್ಪತ್ತನಾಲ್ಕು', 'ಇಪ್ಪತ್ತೈದು', 'ಇಪ್ಪತ್ತಾರು', 'ಇಪ್ಪತ್ತೇಳು', 'ಇಪ್ಪತ್ತೆಂಟು', 'ಇಪ್ಪತ್ತೊಂಬತ್ತು',
22
- 'ಮೂವತ್ತು', 'ಮೂವತ್ತೊಂದು', 'ಮೂವತ್ತೆರಡು', 'ಮೂವತ್ತಮೂರು', 'ಮೂವತ್ತನಾಲ್ಕು', 'ಮೂವತ್ತೈದು', 'ಮೂವತ್ತಾರು', 'ಮೂವತ್ತೇಳು', 'ಮೂವತ್ತೆಂಟು', 'ಮೂವತ್ತೊಂಬತ್ತು',
23
- 'ನಲವತ್ತು', 'ನಲವತ್ತೊಂದು', 'ನಲವತ್ತೆರಡು', 'ನಲವತ್ತಮೂರು', 'ನಲವತ್ತನಾಲ್ಕು', 'ನಲವತ್ತೈದು', 'ನಲವತ್ತಾರು', 'ನಲವತ್ತೇಳು', 'ನಲವತ್ತೆಂಟು', 'ನಲವತ್ತೊಂಬತ್ತು',
24
- 'ಐವತ್ತು', 'ಐವತ್ತೊಂದು', 'ಐವತ್ತೆರಡು', 'ಐವತ್ತಮೂರು', 'ಐವತ್ತನಾಲ್ಕು', 'ಐವತ್ತೈದು', 'ಐವತ್ತಾರು', 'ಐವತ್ತೇಳು', 'ಐವತ್ತೆಂಟು', 'ಐವತ್ತೊಂಬತ್ತು',
25
- 'ಅರವತ್ತು', 'ಅರವತ್ತೊಂದು', 'ಅರವತ್ತೆರಡು', 'ಅರವತ್ತಮೂರು', 'ಅರವತ್ತನಾಲ್ಕು', 'ಅರವತ್ತೈದು', 'ಅರವತ್ತಾರು', 'ಅರವತ್ತೇಳು', 'ಅರವತ್ತೆಂಟು', 'ಅರವತ್ತೊಂಬತ್ತು',
26
- 'ಎಪ್ಪತ್ತು', 'ಎಪ್ಪತ್ತೊಂದು', 'ಎಪ್ಪತ್ತೆರಡು', 'ಎಪ್ಪತ್ತಮೂರು', 'ಎಪ್ಪತ್ತನಾಲ್ಕು', 'ಎಪ್ಪತ್ತೈದು', 'ಎಪ್ಪತ್ತಾರು', 'ಎಪ್ಪತ್ತೇಳು', 'ಎಪ್ಪತ್ತೆಂಟು', 'ಎಪ್ಪತ್ತೊಂಬತ್ತು',
27
- 'ಎಂಬತ್ತು', 'ಎಂಬತ್ತೊಂದು', 'ಎಂಬತ್ತೆರಡು', 'ಎಂಬತ್ತಮೂರು', 'ಎಂಬತ್ತನಾಲ್ಕು', 'ಎಂಬತ್ತೈದು', 'ಎಂಬತ್ತಾರು', 'ಎಂಬತ್ತೇಳು', 'ಎಂಬತ್ತೆಂಟು', 'ಎಂಬತ್ತೊಂಬತ್ತು',
28
- 'ತೊಂಬತ್ತು', 'ತೊಂಬತ್ತೊಂದು', 'ತೊಂಬತ್ತೆರಡು', 'ತೊಂಬತ್ತಮೂರು', 'ತೊಂಬತ್ತನಾಲ್ಕು', 'ತೊಂಬತ್ತೈದು', 'ತೊಂಬತ್ತಾರು', 'ತೊಂಬತ್ತೇಳು', 'ತೊಂಬತ್ತೆಂಟು', 'ತೊಂಬತ್ತೊಂಬತ್ತು'
29
- ]
30
-
31
- scaleWords = [
32
- '', // units
33
- 'ಸಾವಿರ', // thousand (1,000)
34
- 'ಲಕ್ಷ', // lakh (100,000)
35
- 'ಕೋಟಿ', // crore (10,000,000)
36
- 'ಅಬ್ಜ', // arab (1,000,000,000)
37
- 'ಖರ್ವ', // kharab (100,000,000,000)
38
- 'ನೀಲ', // neel (10,000,000,000,000)
39
- 'ಪದ್ಮ', // padma (1,000,000,000,000,000)
40
- 'ಶಂಖ' // shankh (100,000,000,000,000,000)
41
- ]
13
+
14
+ import { parseNumericValue } from '../utils/parse-numeric.js'
15
+
16
+ // ============================================================================
17
+ // Vocabulary
18
+ // ============================================================================
19
+
20
+ const ZERO = 'ಸೊನ್ನೆ'
21
+ const NEGATIVE = 'ಋಣಾತ್ಮಕ'
22
+ const DECIMAL_SEP = 'ದಶಮಾಂಶ'
23
+ const HUNDRED = 'ನೂರು'
24
+
25
+ const BELOW_HUNDRED = [
26
+ 'ಸೊನ್ನೆ', 'ಒಂದು', 'ಎರಡು', 'ಮೂರು', 'ನಾಲ್ಕು', 'ಐದು', 'ಆರು', 'ಏಳು', 'ಎಂಟು', 'ಒಂಬತ್ತು',
27
+ 'ಹತ್ತು', 'ಹನ್ನೊಂದು', 'ಹನ್ನೆರಡು', 'ಹದಿಮೂರು', 'ಹದಿನಾಲ್ಕು', 'ಹದಿನೈದು', 'ಹದಿನಾರು', 'ಹದಿನೇಳು', 'ಹದಿನೆಂಟು', 'ಹತ್ತೊಂಬತ್ತು',
28
+ 'ಇಪ್ಪತ್ತು', 'ಇಪ್ಪತ್ತೊಂದು', 'ಇಪ್ಪತ್ತೆರಡು', 'ಇಪ್ಪತ್ತಮೂರು', 'ಇಪ್ಪತ್ತನಾಲ್ಕು', 'ಇಪ್ಪತ್ತೈದು', 'ಇಪ್ಪತ್ತಾರು', 'ಇಪ್ಪತ್ತೇಳು', 'ಇಪ್ಪತ್ತೆಂಟು', 'ಇಪ್ಪತ್ತೊಂಬತ್ತು',
29
+ 'ಮೂವತ್ತು', 'ಮೂವತ್ತೊಂದು', 'ಮೂವತ್ತೆರಡು', 'ಮೂವತ್ತಮೂರು', 'ಮೂವತ್ತನಾಲ್ಕು', 'ಮೂವತ್ತೈದು', 'ಮೂವತ್ತಾರು', 'ಮೂವತ್ತೇಳು', 'ಮೂವತ್ತೆಂಟು', 'ಮೂವತ್ತೊಂಬತ್ತು',
30
+ 'ನಲವತ್ತು', 'ನಲವತ್ತೊಂದು', 'ನಲವತ್ತೆರಡು', 'ನಲವತ್ತಮೂರು', 'ನಲವತ್ತನಾಲ್ಕು', 'ನಲವತ್ತೈದು', 'ನಲವತ್ತಾರು', 'ನಲವತ್ತೇಳು', 'ನಲವತ್ತೆಂಟು', 'ನಲವತ್ತೊಂಬತ್ತು',
31
+ 'ಐವತ್ತು', 'ಐವತ್ತೊಂದು', 'ಐವತ್ತೆರಡು', 'ಐವತ್ತಮೂರು', 'ಐವತ್ತನಾಲ್ಕು', 'ಐವತ್ತೈದು', 'ಐವತ್ತಾರು', 'ಐವತ್ತೇಳು', 'ಐವತ್ತೆಂಟು', 'ಐವತ್ತೊಂಬತ್ತು',
32
+ 'ಅರವತ್ತು', 'ಅರವತ್ತೊಂದು', 'ಅರವತ್ತೆರಡು', 'ಅರವತ್ತಮೂರು', 'ಅರವತ್ತನಾಲ್ಕು', 'ಅರವತ್ತೈದು', 'ಅರವತ್ತಾರು', 'ಅರವತ್ತೇಳು', 'ಅರವತ್ತೆಂಟು', 'ಅರವತ್ತೊಂಬತ್ತು',
33
+ 'ಎಪ್ಪತ್ತು', 'ಎಪ್ಪತ್ತೊಂದು', 'ಎಪ್ಪತ್ತೆರಡು', 'ಎಪ್ಪತ್ತಮೂರು', 'ಎಪ್ಪತ್ತನಾಲ್ಕು', 'ಎಪ್ಪತ್ತೈದು', 'ಎಪ್ಪತ್ತಾರು', 'ಎಪ್ಪತ್ತೇಳು', 'ಎಪ್ಪತ್ತೆಂಟು', 'ಎಪ್ಪತ್ತೊಂಬತ್ತು',
34
+ 'ಎಂಬತ್ತು', 'ಎಂಬತ್ತೊಂದು', 'ಎಂಬತ್ತೆರಡು', 'ಎಂಬತ್ತಮೂರು', 'ಎಂಬತ್ತನಾಲ್ಕು', 'ಎಂಬತ್ತೈದು', 'ಎಂಬತ್ತಾರು', 'ಎಂಬತ್ತೇಳು', 'ಎಂಬತ್ತೆಂಟು', 'ಎಂಬತ್ತೊಂಬತ್ತು',
35
+ 'ತೊಂಬತ್ತು', 'ತೊಂಬತ್ತೊಂದು', 'ತೊಂಬತ್ತೆರಡು', 'ತೊಂಬತ್ತಮೂರು', 'ತೊಂಬತ್ತನಾಲ್ಕು', 'ತೊಂಬತ್ತೈದು', 'ತೊಂಬತ್ತಾರು', 'ತೊಂಬತ್ತೇಳು', 'ತೊಂಬತ್ತೆಂಟು', 'ತೊಂಬತ್ತೊಂಬತ್ತು'
36
+ ]
37
+
38
+ // Scale words: index 0 = units (empty), 1 = thousand, 2 = lakh, 3 = crore, etc.
39
+ const SCALE_WORDS = ['', 'ಸಾವಿರ', 'ಲಕ್ಷ', 'ಕೋಟಿ', 'ಅಬ್ಜ', 'ಖರ್ವ', 'ನೀಲ', 'ಪದ್ಮ', 'ಶಂಖ']
40
+
41
+ // ============================================================================
42
+ // Segment Splitting (inlined for performance)
43
+ // ============================================================================
44
+
45
+ function groupByThreeThenTwos (n) {
46
+ const numStr = n.toString()
47
+ if (numStr.length <= 3) return [Number(numStr)]
48
+
49
+ const segments = []
50
+ segments.unshift(Number(numStr.slice(-3)))
51
+
52
+ let remaining = numStr.slice(0, -3)
53
+ while (remaining.length > 0) {
54
+ segments.unshift(Number(remaining.slice(-2)))
55
+ remaining = remaining.slice(0, -2)
56
+ }
57
+
58
+ return segments
42
59
  }
60
+
61
+ function segmentToWords (n) {
62
+ if (n === 0) return ''
63
+ if (n < 100) return BELOW_HUNDRED[n]
64
+
65
+ const hundreds = Math.trunc(n / 100)
66
+ const remainder = n % 100
67
+
68
+ if (remainder === 0) {
69
+ return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED
70
+ }
71
+ return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED + ' ' + BELOW_HUNDRED[remainder]
72
+ }
73
+
74
+ // ============================================================================
75
+ // Conversion Functions
76
+ // ============================================================================
77
+
78
+ function integerToWords (n) {
79
+ if (n === 0n) return ZERO
80
+
81
+ const segments = groupByThreeThenTwos(n)
82
+ const segmentCount = segments.length
83
+ const words = []
84
+
85
+ for (let i = 0; i < segmentCount; i++) {
86
+ const segmentValue = segments[i]
87
+ if (segmentValue === 0) continue
88
+
89
+ const scaleIndex = segmentCount - i - 1
90
+ words.push(segmentToWords(segmentValue))
91
+ if (scaleIndex > 0 && SCALE_WORDS[scaleIndex]) {
92
+ words.push(SCALE_WORDS[scaleIndex])
93
+ }
94
+ }
95
+
96
+ return words.join(' ').trim()
97
+ }
98
+
99
+ function decimalPartToWords (decimalPart) {
100
+ // Per-digit decimal reading
101
+ const digits = []
102
+ for (const char of decimalPart) {
103
+ const d = parseInt(char, 10)
104
+ digits.push(d === 0 ? ZERO : BELOW_HUNDRED[d])
105
+ }
106
+ return digits.join(' ')
107
+ }
108
+
109
+ /**
110
+ * Converts a numeric value to Kannada words.
111
+ *
112
+ * @param {number | string | bigint} value - The numeric value to convert
113
+ * @returns {string} The number in Kannada words
114
+ */
115
+ function toWords (value) {
116
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
117
+
118
+ let result = ''
119
+
120
+ if (isNegative) {
121
+ result = NEGATIVE + ' '
122
+ }
123
+
124
+ result += integerToWords(integerPart)
125
+
126
+ if (decimalPart) {
127
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
128
+ }
129
+
130
+ return result
131
+ }
132
+
133
+ // ============================================================================
134
+ // Exports
135
+ // ============================================================================
136
+
137
+ export { toWords }
@@ -1,14 +1,14 @@
1
1
  /**
2
- * Korean language converter.
2
+ * Converts a numeric value to Korean words.
3
3
  *
4
- * Supports:
5
- * - Hangul numerals (일, 이, 삼, etc.)
6
- * - Grouping by (10,000) system
7
- * - Implicit '일' (one) omission before multipliers
4
+ * @param {number | string | bigint} value - The numeric value to convert
5
+ * @returns {string} The number in Korean words
6
+ * @throws {TypeError} If value is not a valid numeric type
7
+ * @throws {Error} If value is not a valid number format
8
+ *
9
+ * @example
10
+ * toWords(21) // '이십일'
11
+ * toWords(10000) // '만'
12
+ * toWords(1000000) // '백만'
8
13
  */
9
- export class Korean extends GreedyScaleLanguage {
10
- scaleWords: (string | bigint)[][];
11
- /** Combines two word-sets according to Korean grammar rules. */
12
- combineWordSets(preceding: any, following: any): any;
13
- }
14
- import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js';
14
+ export function toWords(value: number | string | bigint): string;