num2words 0.2.0 → 0.3.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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/README.md +113 -144
  4. data/config/locales/ar.yml +5 -1
  5. data/config/locales/be.yml +4 -0
  6. data/config/locales/bg.yml +4 -0
  7. data/config/locales/bn.yml +4 -0
  8. data/config/locales/cs.yml +6 -2
  9. data/config/locales/da.yml +4 -0
  10. data/config/locales/de.yml +8 -4
  11. data/config/locales/el.yml +4 -0
  12. data/config/locales/en.yml +4 -0
  13. data/config/locales/es.yml +4 -0
  14. data/config/locales/et.yml +9 -5
  15. data/config/locales/fa.yml +15 -11
  16. data/config/locales/fi.yml +8 -4
  17. data/config/locales/fr.yml +4 -0
  18. data/config/locales/gu.yml +4 -0
  19. data/config/locales/he.yml +6 -2
  20. data/config/locales/hi.yml +4 -0
  21. data/config/locales/hr.yml +8 -4
  22. data/config/locales/hu.yml +6 -2
  23. data/config/locales/id.yml +4 -0
  24. data/config/locales/it.yml +4 -0
  25. data/config/locales/ja.yml +10 -5
  26. data/config/locales/kn.yml +186 -10
  27. data/config/locales/ko.yml +184 -13
  28. data/config/locales/kz.yml +183 -17
  29. data/config/locales/lt.yml +184 -8
  30. data/config/locales/lv.yml +188 -12
  31. data/config/locales/ml.yml +189 -13
  32. data/config/locales/mr.yml +184 -8
  33. data/config/locales/ms.yml +184 -8
  34. data/config/locales/nb.yml +183 -17
  35. data/config/locales/nl.yml +184 -13
  36. data/config/locales/pa.yml +189 -13
  37. data/config/locales/pl.yml +184 -13
  38. data/config/locales/pt.yml +184 -13
  39. data/config/locales/ro.yml +183 -12
  40. data/config/locales/ru.yml +4 -0
  41. data/config/locales/sk.yml +184 -8
  42. data/config/locales/sl.yml +185 -9
  43. data/config/locales/sr.yml +186 -15
  44. data/config/locales/sv.yml +182 -13
  45. data/config/locales/sw.yml +184 -8
  46. data/config/locales/ta.yml +184 -8
  47. data/config/locales/te.yml +185 -9
  48. data/config/locales/th.yml +183 -12
  49. data/config/locales/tr.yml +183 -12
  50. data/config/locales/uk.yml +189 -18
  51. data/config/locales/ur.yml +187 -11
  52. data/config/locales/vi.yml +183 -12
  53. data/config/locales/zh.yml +191 -9
  54. data/lib/num2words/converter.rb +144 -35
  55. data/lib/num2words/locales/ar.rb +130 -0
  56. data/lib/num2words/locales/be.rb +101 -0
  57. data/lib/num2words/locales/bg.rb +127 -0
  58. data/lib/num2words/locales/bn.rb +112 -0
  59. data/lib/num2words/locales/cs.rb +106 -0
  60. data/lib/num2words/locales/da.rb +97 -0
  61. data/lib/num2words/locales/de.rb +119 -0
  62. data/lib/num2words/locales/el.rb +143 -0
  63. data/lib/num2words/locales/en.rb +90 -0
  64. data/lib/num2words/locales/es.rb +185 -0
  65. data/lib/num2words/locales/et.rb +104 -0
  66. data/lib/num2words/locales/fa.rb +104 -0
  67. data/lib/num2words/locales/fi.rb +104 -0
  68. data/lib/num2words/locales/fr.rb +121 -0
  69. data/lib/num2words/locales/gu.rb +112 -0
  70. data/lib/num2words/locales/he.rb +130 -0
  71. data/lib/num2words/locales/hi.rb +112 -0
  72. data/lib/num2words/locales/hr.rb +117 -0
  73. data/lib/num2words/locales/hu.rb +111 -0
  74. data/lib/num2words/locales/id.rb +104 -0
  75. data/lib/num2words/locales/it.rb +142 -0
  76. data/lib/num2words/locales/ja.rb +126 -0
  77. data/lib/num2words/locales/kn.rb +122 -2
  78. data/lib/num2words/locales/ko.rb +158 -2
  79. data/lib/num2words/locales/kz.rb +137 -2
  80. data/lib/num2words/locales/lt.rb +146 -2
  81. data/lib/num2words/locales/lv.rb +148 -2
  82. data/lib/num2words/locales/ml.rb +142 -2
  83. data/lib/num2words/locales/mr.rb +142 -2
  84. data/lib/num2words/locales/ms.rb +134 -2
  85. data/lib/num2words/locales/nb.rb +153 -0
  86. data/lib/num2words/locales/nl.rb +142 -2
  87. data/lib/num2words/locales/pa.rb +122 -2
  88. data/lib/num2words/locales/pl.rb +149 -2
  89. data/lib/num2words/locales/pt.rb +164 -2
  90. data/lib/num2words/locales/ro.rb +165 -2
  91. data/lib/num2words/locales/ru.rb +100 -0
  92. data/lib/num2words/locales/sk.rb +124 -2
  93. data/lib/num2words/locales/sl.rb +154 -2
  94. data/lib/num2words/locales/sr.rb +141 -3
  95. data/lib/num2words/locales/sv.rb +141 -2
  96. data/lib/num2words/locales/sw.rb +133 -2
  97. data/lib/num2words/locales/ta.rb +142 -2
  98. data/lib/num2words/locales/te.rb +142 -2
  99. data/lib/num2words/locales/th.rb +133 -2
  100. data/lib/num2words/locales/tr.rb +141 -2
  101. data/lib/num2words/locales/uk.rb +134 -2
  102. data/lib/num2words/locales/ur.rb +130 -2
  103. data/lib/num2words/locales/vi.rb +142 -2
  104. data/lib/num2words/locales/zh.rb +172 -2
  105. data/lib/num2words/version.rb +1 -1
  106. data/num2words.gemspec +2 -2
  107. metadata +6 -6
  108. data/lib/num2words/locales/no.rb +0 -19
@@ -10,8 +10,143 @@ module Num2words
10
10
  HUNDREDS = I18n.t("num2words.hundreds", locale: :kz)
11
11
  SCALES = I18n.t("num2words.scales", locale: :kz)
12
12
 
13
- MAJOR_UNIT = I18n.t("num2words.currencies.KZT.name", locale: :kz)
14
- MINOR_UNIT = I18n.t("num2words.currencies.KZT.fractional.name", locale: :kz)
13
+ FRACTIONS = I18n.t("num2words.fractions", locale: :kz)
14
+ GRAMMAR = I18n.t("num2words.grammar", locale: :kz)
15
+
16
+ DATE = I18n.t("num2words.date", locale: :kz)
17
+ DATE_TEMPLATE = I18n.t("num2words.date.template", locale: :kz)
18
+ TIME = I18n.t("num2words.time", locale: :kz)
19
+ TIME_TEMPLATE = I18n.t("num2words.time.template", locale: :kz)
20
+ DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :kz)
21
+
22
+ ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :kz)
23
+
24
+ module_function
25
+
26
+ def integer_to_words(number, feminine: false)
27
+ cardinal(number)
28
+ end
29
+
30
+ def triple_to_words(number, scale_idx, feminine: false)
31
+ return [] if number.zero?
32
+
33
+ words = [under_thousand(number)]
34
+ words << SCALES[scale_idx][0] unless scale_idx.zero?
35
+ words
36
+ end
37
+
38
+ def minus_word
39
+ GRAMMAR[:minus]
40
+ end
41
+
42
+ def fraction_joiner(joiner)
43
+ joiner.to_sym == :and ? "және" : GRAMMAR[:conjunction]
44
+ end
45
+
46
+ def default_fraction_word
47
+ GRAMMAR[:default_fraction]
48
+ end
49
+
50
+ def fraction_numerator_feminine?
51
+ false
52
+ end
53
+
54
+ def decimal_separator_word
55
+ "үтір"
56
+ end
57
+
58
+ def decimal_fraction_words(fraction_string)
59
+ fraction_string.chars.map { |digit| cardinal(digit.to_i) }.join(" ")
60
+ end
61
+
62
+ def date_day(day, format:, date_case:)
63
+ ordinal(day, :default)
64
+ end
65
+
66
+ def date_year(year, format:)
67
+ cardinal(year)
68
+ end
69
+
70
+ def time_unit_feminine?(_unit)
71
+ false
72
+ end
73
+
74
+ def time_number_words(number, unit:)
75
+ cardinal(number)
76
+ end
77
+
78
+ def currency_major_feminine?(_currency)
79
+ false
80
+ end
81
+
82
+ def currency_minor_feminine?(_currency)
83
+ false
84
+ end
85
+
86
+ def currency_number_words(number, _currency, unit:)
87
+ cardinal(number)
88
+ end
89
+
90
+ def ordinal(value, format, gender: :masculine)
91
+ ordinals = ORDINALS[format] || ORDINALS[:default] || ORDINALS[:nominative]
92
+ gender_data = ordinals[gender] || ordinals[:masculine]
93
+
94
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
95
+
96
+ cardinal(value)
97
+ end
98
+
99
+ def cardinal(number)
100
+ integer_value = Integer(number)
101
+ negative = integer_value.negative?
102
+ integer_value = integer_value.abs
103
+
104
+ return ONES_MASC[0] if integer_value.zero?
105
+
106
+ groups = integer_value.to_s
107
+ .chars.reverse.each_slice(3).map(&:reverse)
108
+ .map(&:join).map!(&:to_i).reverse
109
+
110
+ words = []
111
+ groups.each_with_index do |group_value, index|
112
+ next if group_value.zero?
113
+
114
+ scale_index = groups.size - index - 1
115
+ words << under_thousand(group_value)
116
+ words << SCALES[scale_index][0] unless scale_index.zero?
117
+ end
118
+
119
+ words.unshift(minus_word) if negative
120
+ words.join(" ")
121
+ end
122
+
123
+ def under_thousand(number)
124
+ return ONES_MASC[number] if number < 10
125
+ return TEENS[number - 10] if number.between?(10, 19)
126
+ return under_hundred(number) if number < 100
127
+
128
+ hundreds = number / 100
129
+ rest = number % 100
130
+ words = [HUNDREDS[hundreds]]
131
+ words << under_hundred(rest) if rest.positive?
132
+ words.join(" ")
133
+ end
134
+
135
+ def under_hundred(number)
136
+ return "" if number.zero?
137
+ return ONES_MASC[number] if number < 10
138
+ return TEENS[number - 10] if number.between?(10, 19)
139
+
140
+ tens = number / 10
141
+ ones = number % 10
142
+ words = [TENS[tens]]
143
+ words << ONES_MASC[ones] if ones.positive?
144
+ words.join(" ")
145
+ end
146
+
147
+ def pluralize(number, singular, few, _plural)
148
+ number.abs == 1 ? singular : few
149
+ end
15
150
  end
16
151
 
17
152
  register :kz, KZ
@@ -10,8 +10,152 @@ module Num2words
10
10
  HUNDREDS = I18n.t("num2words.hundreds", locale: :lt)
11
11
  SCALES = I18n.t("num2words.scales", locale: :lt)
12
12
 
13
- MAJOR_UNIT = I18n.t("num2words.currencies.EUR.name", locale: :lt)
14
- MINOR_UNIT = I18n.t("num2words.currencies.EUR.fractional.name", locale: :lt)
13
+ FRACTIONS = I18n.t("num2words.fractions", locale: :lt)
14
+ GRAMMAR = I18n.t("num2words.grammar", locale: :lt)
15
+
16
+ DATE = I18n.t("num2words.date", locale: :lt)
17
+ DATE_TEMPLATE = I18n.t("num2words.date.template", locale: :lt)
18
+ TIME = I18n.t("num2words.time", locale: :lt)
19
+ TIME_TEMPLATE = I18n.t("num2words.time.template", locale: :lt)
20
+ DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :lt)
21
+
22
+ ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :lt)
23
+
24
+ module_function
25
+
26
+ def integer_to_words(number, feminine: false)
27
+ cardinal(number, feminine: feminine)
28
+ end
29
+
30
+ def triple_to_words(number, scale_idx, feminine: false)
31
+ return [] if number.zero?
32
+
33
+ words = [under_thousand(number, feminine: feminine)]
34
+ words << pluralize(number, *SCALES[scale_idx]) unless scale_idx.zero?
35
+ words
36
+ end
37
+
38
+ def feminine_group?(scale_idx)
39
+ false
40
+ end
41
+
42
+ def minus_word
43
+ GRAMMAR[:minus]
44
+ end
45
+
46
+ def fraction_joiner(joiner)
47
+ joiner.to_sym == :and ? "ir" : GRAMMAR[:conjunction]
48
+ end
49
+
50
+ def default_fraction_word
51
+ GRAMMAR[:default_fraction]
52
+ end
53
+
54
+ def fraction_numerator_feminine?
55
+ true
56
+ end
57
+
58
+ def decimal_separator_word
59
+ "kablelis"
60
+ end
61
+
62
+ def decimal_fraction_words(fraction_string)
63
+ fraction_string.chars.map { |digit| cardinal(digit.to_i) }.join(" ")
64
+ end
65
+
66
+ def date_day(day, format:, date_case:)
67
+ ordinal(day, :default, gender: :feminine)
68
+ end
69
+
70
+ def date_year(year, format:)
71
+ cardinal(year)
72
+ end
73
+
74
+ def time_unit_feminine?(unit)
75
+ %i[minute second].include?(unit)
76
+ end
77
+
78
+ def time_number_words(number, unit:)
79
+ cardinal(number, feminine: time_unit_feminine?(unit))
80
+ end
81
+
82
+ def currency_major_feminine?(currency)
83
+ %i[BGN BRL BYN CNY CZK DKK IDR INR JPY KZT NOK PKR RON RSD SEK TRY UAH].include?(currency)
84
+ end
85
+
86
+ def currency_minor_feminine?(currency)
87
+ %i[BGN BRL BYN DKK IDR ILS INR NOK PKR RSD RUB SEK SAR TRY UAH].include?(currency)
88
+ end
89
+
90
+ def currency_number_words(number, currency, unit:)
91
+ feminine = unit == :major ? currency_major_feminine?(currency) : currency_minor_feminine?(currency)
92
+ cardinal(number, feminine: feminine)
93
+ end
94
+
95
+ def ordinal(value, format, gender: :masculine)
96
+ ordinals = ORDINALS[format] || ORDINALS[:default] || ORDINALS[:nominative]
97
+ gender_data = ordinals[gender] || ordinals[:masculine]
98
+
99
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
100
+
101
+ cardinal(value)
102
+ end
103
+
104
+ def cardinal(number, feminine: false)
105
+ integer_value = Integer(number)
106
+ negative = integer_value.negative?
107
+ integer_value = integer_value.abs
108
+
109
+ return ONES_MASC[0] if integer_value.zero?
110
+
111
+ groups = integer_value.to_s
112
+ .chars.reverse.each_slice(3).map(&:reverse)
113
+ .map(&:join).map!(&:to_i).reverse
114
+
115
+ words = []
116
+ groups.each_with_index do |group_value, index|
117
+ next if group_value.zero?
118
+
119
+ scale_idx = groups.size - index - 1
120
+ group_feminine = scale_idx.zero? && feminine
121
+ words.concat triple_to_words(group_value, scale_idx, feminine: group_feminine)
122
+ end
123
+
124
+ words.unshift(minus_word) if negative
125
+ words.join(" ")
126
+ end
127
+
128
+ def under_thousand(number, feminine: false)
129
+ return under_hundred(number, feminine: feminine) if number < 100
130
+
131
+ hundreds = number / 100
132
+ rest = number % 100
133
+ words = [HUNDREDS[hundreds]]
134
+ words << under_hundred(rest, feminine: feminine) if rest.positive?
135
+ words.join(" ")
136
+ end
137
+
138
+ def under_hundred(number, feminine: false)
139
+ return (feminine ? ONES_FEM[number] : ONES_MASC[number]) if number < 10
140
+ return TEENS[number - 10] if number < 20
141
+
142
+ tens = number / 10
143
+ ones = number % 10
144
+ words = [TENS[tens]]
145
+ words << (feminine ? ONES_FEM[ones] : ONES_MASC[ones]) if ones.positive?
146
+ words.join(" ")
147
+ end
148
+
149
+ def pluralize(number, singular, few, plural)
150
+ number = number.abs
151
+ return plural if (10..20).include?(number % 100)
152
+
153
+ case number % 10
154
+ when 1 then singular
155
+ when 2..9 then few
156
+ else plural
157
+ end
158
+ end
15
159
  end
16
160
 
17
161
  register :lt, LT
@@ -10,8 +10,154 @@ module Num2words
10
10
  HUNDREDS = I18n.t("num2words.hundreds", locale: :lv)
11
11
  SCALES = I18n.t("num2words.scales", locale: :lv)
12
12
 
13
- MAJOR_UNIT = I18n.t("num2words.currencies.EUR.name", locale: :lv)
14
- MINOR_UNIT = I18n.t("num2words.currencies.EUR.fractional.name", locale: :lv)
13
+ FRACTIONS = I18n.t("num2words.fractions", locale: :lv)
14
+ GRAMMAR = I18n.t("num2words.grammar", locale: :lv)
15
+
16
+ DATE = I18n.t("num2words.date", locale: :lv)
17
+ DATE_TEMPLATE = I18n.t("num2words.date.template", locale: :lv)
18
+ TIME = I18n.t("num2words.time", locale: :lv)
19
+ TIME_TEMPLATE = I18n.t("num2words.time.template", locale: :lv)
20
+ DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :lv)
21
+
22
+ ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :lv)
23
+
24
+ module_function
25
+
26
+ def integer_to_words(number, feminine: false)
27
+ cardinal(number, feminine: feminine)
28
+ end
29
+
30
+ def triple_to_words(number, scale_idx, feminine: false)
31
+ return [] if number.zero?
32
+
33
+ words = [under_thousand(number, feminine: feminine)]
34
+ words << scale_form(number, scale_idx) unless scale_idx.zero?
35
+ words
36
+ end
37
+
38
+ def feminine_group?(_scale_idx)
39
+ false
40
+ end
41
+
42
+ def minus_word
43
+ GRAMMAR[:minus]
44
+ end
45
+
46
+ def fraction_joiner(joiner)
47
+ joiner.to_sym == :and ? "un" : GRAMMAR[:conjunction]
48
+ end
49
+
50
+ def default_fraction_word
51
+ GRAMMAR[:default_fraction]
52
+ end
53
+
54
+ def fraction_numerator_feminine?
55
+ true
56
+ end
57
+
58
+ def decimal_separator_word
59
+ "komats"
60
+ end
61
+
62
+ def decimal_fraction_words(fraction_string)
63
+ fraction_string.chars.map { |digit| cardinal(digit.to_i) }.join(" ")
64
+ end
65
+
66
+ def date_day(day, format:, date_case:)
67
+ ordinal(day, :default, gender: :feminine)
68
+ end
69
+
70
+ def date_year(year, format:)
71
+ cardinal(year)
72
+ end
73
+
74
+ def time_unit_feminine?(_unit)
75
+ true
76
+ end
77
+
78
+ def time_number_words(number, unit:)
79
+ cardinal(number, feminine: true)
80
+ end
81
+
82
+ def currency_major_feminine?(currency)
83
+ %i[BDT BGN CNY CZK DKK GBP IDR INR JPY KZT NOK PKR RON RSD SEK TRY UAH].include?(currency)
84
+ end
85
+
86
+ def currency_minor_feminine?(currency)
87
+ %i[BDT BGN BRL BYN DKK ILS NOK RSD RUB SAR SEK TRY UAH].include?(currency)
88
+ end
89
+
90
+ def currency_number_words(number, currency, unit:)
91
+ feminine = unit == :major ? currency_major_feminine?(currency) : currency_minor_feminine?(currency)
92
+ cardinal(number, feminine: feminine)
93
+ end
94
+
95
+ def ordinal(value, format, gender: :masculine)
96
+ ordinals = ORDINALS[format] || ORDINALS[:default] || ORDINALS[:nominative]
97
+ gender_data = ordinals[gender] || ordinals[:masculine]
98
+
99
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
100
+
101
+ cardinal(value)
102
+ end
103
+
104
+ def cardinal(number, feminine: false)
105
+ integer_value = Integer(number)
106
+ negative = integer_value.negative?
107
+ integer_value = integer_value.abs
108
+
109
+ return ONES_MASC[0] if integer_value.zero?
110
+
111
+ groups = integer_value.to_s
112
+ .chars.reverse.each_slice(3).map(&:reverse)
113
+ .map(&:join).map!(&:to_i).reverse
114
+
115
+ words = []
116
+ groups.each_with_index do |group_value, index|
117
+ next if group_value.zero?
118
+
119
+ scale_idx = groups.size - index - 1
120
+ group_feminine = scale_idx.zero? && feminine
121
+ words.concat triple_to_words(group_value, scale_idx, feminine: group_feminine)
122
+ end
123
+
124
+ words.unshift(minus_word) if negative
125
+ words.join(" ")
126
+ end
127
+
128
+ def under_thousand(number, feminine: false)
129
+ return under_hundred(number, feminine: feminine) if number < 100
130
+
131
+ hundreds = number / 100
132
+ rest = number % 100
133
+ words = [HUNDREDS[hundreds]]
134
+ words << under_hundred(rest, feminine: feminine) if rest.positive?
135
+ words.join(" ")
136
+ end
137
+
138
+ def under_hundred(number, feminine: false)
139
+ return (feminine ? ONES_FEM[number] : ONES_MASC[number]) if number < 10
140
+ return TEENS[number - 10] if number < 20
141
+
142
+ tens = number / 10
143
+ ones = number % 10
144
+ words = [TENS[tens]]
145
+ words << (feminine ? ONES_FEM[ones] : ONES_MASC[ones]) if ones.positive?
146
+ words.join(" ")
147
+ end
148
+
149
+ def scale_form(number, scale_idx)
150
+ singular, plural, = SCALES[scale_idx]
151
+ number.abs % 10 == 1 && number.abs % 100 != 11 ? singular : plural
152
+ end
153
+
154
+ def pluralize(number, singular, few, plural)
155
+ number = number.abs
156
+ return singular if number % 10 == 1 && number % 100 != 11
157
+ return plural if number.zero? || (number % 10).zero? || (10..20).include?(number % 100)
158
+
159
+ few
160
+ end
15
161
  end
16
162
 
17
163
  register :lv, LV
@@ -10,8 +10,148 @@ module Num2words
10
10
  HUNDREDS = I18n.t("num2words.hundreds", locale: :ml)
11
11
  SCALES = I18n.t("num2words.scales", locale: :ml)
12
12
 
13
- MAJOR_UNIT = I18n.t("num2words.currencies.INR.name", locale: :ml)
14
- MINOR_UNIT = I18n.t("num2words.currencies.INR.fractional.name", locale: :ml)
13
+ FRACTIONS = I18n.t("num2words.fractions", locale: :ml)
14
+ GRAMMAR = I18n.t("num2words.grammar", locale: :ml)
15
+
16
+ DATE = I18n.t("num2words.date", locale: :ml)
17
+ DATE_TEMPLATE = I18n.t("num2words.date.template", locale: :ml)
18
+ TIME = I18n.t("num2words.time", locale: :ml)
19
+ TIME_TEMPLATE = I18n.t("num2words.time.template", locale: :ml)
20
+ DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :ml)
21
+
22
+ ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :ml)
23
+
24
+ UNDER_HUNDRED = [
25
+ "പൂജ്യം", "ഒന്ന്", "രണ്ട്", "മൂന്ന്", "നാല്", "അഞ്ച്", "ആറ്", "ഏഴ്", "എട്ട്", "ഒമ്പത്",
26
+ "പത്ത്", "പതിനൊന്ന്", "പന്ത്രണ്ട്", "പതിമൂന്ന്", "പതിനാല്", "പതിനഞ്ച്", "പതിനാറ്", "പതിനേഴ്", "പതിനെട്ട്", "പത്തൊമ്പത്",
27
+ "ഇരുപത്", "ഇരുപത്തൊന്ന്", "ഇരുപത്തിരണ്ട്", "ഇരുപത്തിമൂന്ന്", "ഇരുപത്തിനാല്", "ഇരുപത്തിയഞ്ച്", "ഇരുപത്തിയാറ്", "ഇരുപത്തിയേഴ്", "ഇരുപത്തിയെട്ട്", "ഇരുപത്തൊമ്പത്",
28
+ "മുപ്പത്", "മുപ്പത്തൊന്ന്", "മുപ്പത്തിരണ്ട്", "മുപ്പത്തിമൂന്ന്", "മുപ്പത്തിനാല്", "മുപ്പത്തിയഞ്ച്", "മുപ്പത്തിയാറ്", "മുപ്പത്തിയേഴ്", "മുപ്പത്തിയെട്ട്", "മുപ്പത്തൊമ്പത്",
29
+ "നാല്പത്", "നാല്പത്തൊന്ന്", "നാല്പത്തിരണ്ട്", "നാല്പത്തിമൂന്ന്", "നാല്പത്തിനാല്", "നാല്പത്തിയഞ്ച്", "നാല്പത്തിയാറ്", "നാല്പത്തിയേഴ്", "നാല്പത്തിയെട്ട്", "നാല്പത്തൊമ്പത്",
30
+ "അമ്പത്", "അമ്പത്തൊന്ന്", "അമ്പത്തിരണ്ട്", "അമ്പത്തിമൂന്ന്", "അമ്പത്തിനാല്", "അമ്പത്തിയഞ്ച്", "അമ്പത്തിയാറ്", "അമ്പത്തിയേഴ്", "അമ്പത്തിയെട്ട്", "അമ്പത്തൊമ്പത്",
31
+ "അറുപത്", "അറുപത്തൊന്ന്", "അറുപത്തിരണ്ട്", "അറുപത്തിമൂന്ന്", "അറുപത്തിനാല്", "അറുപത്തിയഞ്ച്", "അറുപത്തിയാറ്", "അറുപത്തിയേഴ്", "അറുപത്തിയെട്ട്", "അറുപത്തൊമ്പത്",
32
+ "എഴുപത്", "എഴുപത്തൊന്ന്", "എഴുപത്തിരണ്ട്", "എഴുപത്തിമൂന്ന്", "എഴുപത്തിനാല്", "എഴുപത്തിയഞ്ച്", "എഴുപത്തിയാറ്", "എഴുപത്തിയേഴ്", "എഴുപത്തിയെട്ട്", "എഴുപത്തൊമ്പത്",
33
+ "എൺപത്", "എൺപത്തൊന്ന്", "എൺപത്തിരണ്ട്", "എൺപത്തിമൂന്ന്", "എൺപത്തിനാല്", "എൺപത്തിയഞ്ച്", "എൺപത്തിയാറ്", "എൺപത്തിയേഴ്", "എൺപത്തിയെട്ട്", "എൺപത്തൊമ്പത്",
34
+ "തൊണ്ണൂറ്", "തൊണ്ണൂറ്റൊന്ന്", "തൊണ്ണൂറ്റിരണ്ട്", "തൊണ്ണൂറ്റിമൂന്ന്", "തൊണ്ണൂറ്റിനാല്", "തൊണ്ണൂറ്റിയഞ്ച്", "തൊണ്ണൂറ്റിയാറ്", "തൊണ്ണൂറ്റിയേഴ്", "തൊണ്ണൂറ്റിയെട്ട്", "തൊണ്ണൂറ്റൊമ്പത്"
35
+ ].freeze
36
+
37
+ INDIAN_SCALES = [
38
+ [1_000_000_000, "ശതകോടി"],
39
+ [10_000_000, "കോടി"],
40
+ [100_000, "ലക്ഷം"],
41
+ [1_000, "ആയിരം"]
42
+ ].freeze
43
+
44
+ module_function
45
+
46
+ def integer_to_words(number, feminine: false)
47
+ cardinal(number)
48
+ end
49
+
50
+ def triple_to_words(number, scale_idx, feminine: false)
51
+ return [] if number.zero?
52
+
53
+ words = [under_thousand(number)]
54
+ words << SCALES[scale_idx][0] unless scale_idx.zero?
55
+ words
56
+ end
57
+
58
+ def minus_word
59
+ GRAMMAR[:minus]
60
+ end
61
+
62
+ def fraction_joiner(joiner)
63
+ joiner.to_sym == :and ? "കൂടാതെ" : GRAMMAR[:conjunction]
64
+ end
65
+
66
+ def default_fraction_word
67
+ GRAMMAR[:default_fraction]
68
+ end
69
+
70
+ def fraction_numerator_feminine?
71
+ false
72
+ end
73
+
74
+ def decimal_separator_word
75
+ "ദശാംശം"
76
+ end
77
+
78
+ def decimal_fraction_words(fraction_string)
79
+ fraction_string.chars.map { |digit| cardinal(digit.to_i) }.join(" ")
80
+ end
81
+
82
+ def date_day(day, format:, date_case:)
83
+ ordinal(day, :default)
84
+ end
85
+
86
+ def date_year(year, format:)
87
+ cardinal(year)
88
+ end
89
+
90
+ def time_unit_feminine?(_unit)
91
+ false
92
+ end
93
+
94
+ def time_number_words(number, unit:)
95
+ cardinal(number)
96
+ end
97
+
98
+ def currency_major_feminine?(_currency)
99
+ false
100
+ end
101
+
102
+ def currency_minor_feminine?(_currency)
103
+ false
104
+ end
105
+
106
+ def currency_number_words(number, _currency, unit:)
107
+ cardinal(number)
108
+ end
109
+
110
+ def ordinal(value, format, gender: :masculine)
111
+ ordinals = ORDINALS[format] || ORDINALS[:default] || ORDINALS[:nominative]
112
+ gender_data = ordinals[gender] || ordinals[:masculine]
113
+
114
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
115
+
116
+ cardinal(value)
117
+ end
118
+
119
+ def cardinal(number)
120
+ integer_value = Integer(number)
121
+ negative = integer_value.negative?
122
+ integer_value = integer_value.abs
123
+
124
+ return UNDER_HUNDRED[0] if integer_value.zero?
125
+
126
+ remainder = integer_value
127
+ words = []
128
+
129
+ INDIAN_SCALES.each do |value, label|
130
+ next if remainder < value
131
+
132
+ group_value = remainder / value
133
+ remainder %= value
134
+ words << [cardinal(group_value), label].join(" ")
135
+ end
136
+
137
+ words << under_thousand(remainder) if remainder.positive?
138
+ words.unshift(minus_word) if negative
139
+ words.join(" ")
140
+ end
141
+
142
+ def under_thousand(number)
143
+ return UNDER_HUNDRED[number] if number < 100
144
+
145
+ hundreds = number / 100
146
+ rest = number % 100
147
+ words = [HUNDREDS[hundreds]]
148
+ words << UNDER_HUNDRED[rest] if rest.positive?
149
+ words.join(" ")
150
+ end
151
+
152
+ def pluralize(_number, singular, _few, _plural)
153
+ singular
154
+ end
15
155
  end
16
156
 
17
157
  register :ml, ML