num2words 0.1.6 → 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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +65 -0
  3. data/README.md +145 -135
  4. data/bin/console +2 -14
  5. data/config/locales/ar.yml +5 -1
  6. data/config/locales/be.yml +4 -0
  7. data/config/locales/bg.yml +4 -0
  8. data/config/locales/bn.yml +4 -0
  9. data/config/locales/cs.yml +6 -2
  10. data/config/locales/da.yml +4 -0
  11. data/config/locales/de.yml +8 -4
  12. data/config/locales/el.yml +4 -0
  13. data/config/locales/en.yml +4 -0
  14. data/config/locales/es.yml +4 -0
  15. data/config/locales/et.yml +9 -5
  16. data/config/locales/fa.yml +15 -11
  17. data/config/locales/fi.yml +8 -4
  18. data/config/locales/fr.yml +4 -0
  19. data/config/locales/gu.yml +4 -0
  20. data/config/locales/he.yml +6 -2
  21. data/config/locales/hi.yml +4 -0
  22. data/config/locales/hr.yml +8 -4
  23. data/config/locales/hu.yml +6 -2
  24. data/config/locales/id.yml +4 -0
  25. data/config/locales/it.yml +4 -0
  26. data/config/locales/ja.yml +10 -5
  27. data/config/locales/kn.yml +186 -10
  28. data/config/locales/ko.yml +184 -13
  29. data/config/locales/kz.yml +183 -17
  30. data/config/locales/lt.yml +184 -8
  31. data/config/locales/lv.yml +188 -12
  32. data/config/locales/ml.yml +189 -13
  33. data/config/locales/mr.yml +184 -8
  34. data/config/locales/ms.yml +184 -8
  35. data/config/locales/nb.yml +183 -17
  36. data/config/locales/nl.yml +184 -13
  37. data/config/locales/pa.yml +189 -13
  38. data/config/locales/pl.yml +184 -13
  39. data/config/locales/pt.yml +184 -13
  40. data/config/locales/ro.yml +183 -12
  41. data/config/locales/ru.yml +8 -0
  42. data/config/locales/sk.yml +184 -8
  43. data/config/locales/sl.yml +185 -9
  44. data/config/locales/sr.yml +186 -15
  45. data/config/locales/sv.yml +182 -13
  46. data/config/locales/sw.yml +184 -8
  47. data/config/locales/ta.yml +184 -8
  48. data/config/locales/te.yml +185 -9
  49. data/config/locales/th.yml +183 -12
  50. data/config/locales/tr.yml +183 -12
  51. data/config/locales/uk.yml +189 -18
  52. data/config/locales/ur.yml +187 -11
  53. data/config/locales/vi.yml +183 -12
  54. data/config/locales/zh.yml +191 -9
  55. data/exe/num2words-console +7 -0
  56. data/lib/num2words/console.rb +16 -0
  57. data/lib/num2words/converter.rb +195 -47
  58. data/lib/num2words/locales/ar.rb +130 -0
  59. data/lib/num2words/locales/be.rb +101 -0
  60. data/lib/num2words/locales/bg.rb +127 -0
  61. data/lib/num2words/locales/bn.rb +112 -0
  62. data/lib/num2words/locales/cs.rb +106 -0
  63. data/lib/num2words/locales/da.rb +97 -0
  64. data/lib/num2words/locales/de.rb +119 -0
  65. data/lib/num2words/locales/el.rb +143 -0
  66. data/lib/num2words/locales/en.rb +90 -0
  67. data/lib/num2words/locales/es.rb +185 -0
  68. data/lib/num2words/locales/et.rb +104 -0
  69. data/lib/num2words/locales/fa.rb +104 -0
  70. data/lib/num2words/locales/fi.rb +104 -0
  71. data/lib/num2words/locales/fr.rb +121 -0
  72. data/lib/num2words/locales/gu.rb +112 -0
  73. data/lib/num2words/locales/he.rb +130 -0
  74. data/lib/num2words/locales/hi.rb +112 -0
  75. data/lib/num2words/locales/hr.rb +117 -0
  76. data/lib/num2words/locales/hu.rb +111 -0
  77. data/lib/num2words/locales/id.rb +104 -0
  78. data/lib/num2words/locales/it.rb +142 -0
  79. data/lib/num2words/locales/ja.rb +126 -0
  80. data/lib/num2words/locales/kn.rb +122 -2
  81. data/lib/num2words/locales/ko.rb +158 -2
  82. data/lib/num2words/locales/kz.rb +137 -2
  83. data/lib/num2words/locales/lt.rb +146 -2
  84. data/lib/num2words/locales/lv.rb +148 -2
  85. data/lib/num2words/locales/ml.rb +142 -2
  86. data/lib/num2words/locales/mr.rb +142 -2
  87. data/lib/num2words/locales/ms.rb +134 -2
  88. data/lib/num2words/locales/nb.rb +153 -0
  89. data/lib/num2words/locales/nl.rb +142 -2
  90. data/lib/num2words/locales/pa.rb +122 -2
  91. data/lib/num2words/locales/pl.rb +149 -2
  92. data/lib/num2words/locales/pt.rb +164 -2
  93. data/lib/num2words/locales/ro.rb +165 -2
  94. data/lib/num2words/locales/ru.rb +100 -0
  95. data/lib/num2words/locales/sk.rb +124 -2
  96. data/lib/num2words/locales/sl.rb +154 -2
  97. data/lib/num2words/locales/sr.rb +141 -3
  98. data/lib/num2words/locales/sv.rb +141 -2
  99. data/lib/num2words/locales/sw.rb +133 -2
  100. data/lib/num2words/locales/ta.rb +142 -2
  101. data/lib/num2words/locales/te.rb +142 -2
  102. data/lib/num2words/locales/th.rb +133 -2
  103. data/lib/num2words/locales/tr.rb +141 -2
  104. data/lib/num2words/locales/uk.rb +134 -2
  105. data/lib/num2words/locales/ur.rb +130 -2
  106. data/lib/num2words/locales/vi.rb +142 -2
  107. data/lib/num2words/locales/zh.rb +172 -2
  108. data/lib/num2words/version.rb +1 -1
  109. data/num2words.gemspec +3 -3
  110. metadata +13 -10
  111. data/lib/num2words/locales/no.rb +0 -19
@@ -10,8 +10,170 @@ module Num2words
10
10
  HUNDREDS = I18n.t("num2words.hundreds", locale: :pt)
11
11
  SCALES = I18n.t("num2words.scales", locale: :pt)
12
12
 
13
- MAJOR_UNIT = I18n.t("num2words.currencies.EUR.name", locale: :pt)
14
- MINOR_UNIT = I18n.t("num2words.currencies.EUR.fractional.name", locale: :pt)
13
+ FRACTIONS = I18n.t("num2words.fractions", locale: :pt)
14
+ GRAMMAR = I18n.t("num2words.grammar", locale: :pt)
15
+
16
+ DATE = I18n.t("num2words.date", locale: :pt)
17
+ DATE_TEMPLATE = I18n.t("num2words.date.template", locale: :pt)
18
+ TIME = I18n.t("num2words.time", locale: :pt)
19
+ TIME_TEMPLATE = I18n.t("num2words.time.template", locale: :pt)
20
+ DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :pt)
21
+
22
+ ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :pt)
23
+
24
+ HUNDREDS_FEM = ["", "cento", "duzentas", "trezentas", "quatrocentas", "quinhentas", "seiscentas", "setecentas", "oitocentas", "novecentas"].freeze
25
+ FEMININE_MAJOR_CURRENCIES = %i[GBP TRY UAH CZK SEK NOK DKK INR IDR PKR BDT ILS].freeze
26
+ FEMININE_MINOR_CURRENCIES = %i[ILS SAR].freeze
27
+
28
+ module_function
29
+
30
+ def integer_to_words(number, feminine: false)
31
+ cardinal(number, gender: feminine ? :feminine : :masculine)
32
+ end
33
+
34
+ def triple_to_words(number, scale_idx, feminine: false)
35
+ return [] if number.zero?
36
+
37
+ gender = feminine ? :feminine : :masculine
38
+
39
+ case scale_idx
40
+ when 0
41
+ [under_thousand(number, gender: gender)]
42
+ when 1
43
+ words = []
44
+ words << under_thousand(number, gender: :masculine) unless number == 1
45
+ words << SCALES[1][0]
46
+ words
47
+ else
48
+ [under_thousand(number, gender: :masculine), pluralize(number, *SCALES[scale_idx])]
49
+ end
50
+ end
51
+
52
+ def minus_word
53
+ GRAMMAR[:minus]
54
+ end
55
+
56
+ def fraction_joiner(joiner)
57
+ joiner.to_sym == :and ? "e" : GRAMMAR[:conjunction]
58
+ end
59
+
60
+ def default_fraction_word
61
+ GRAMMAR[:default_fraction]
62
+ end
63
+
64
+ def fraction_numerator_feminine?
65
+ false
66
+ end
67
+
68
+ def decimal_separator_word
69
+ "vírgula"
70
+ end
71
+
72
+ def decimal_fraction_words(fraction_string)
73
+ fraction_string.chars.map { |digit| cardinal(digit.to_i) }.join(" ")
74
+ end
75
+
76
+ def time_unit_feminine?(unit)
77
+ unit == :hour
78
+ end
79
+
80
+ def time_number_words(number, unit:)
81
+ cardinal(number, gender: unit == :hour ? :feminine : :masculine)
82
+ end
83
+
84
+ def currency_major_feminine?(currency)
85
+ FEMININE_MAJOR_CURRENCIES.include?(currency)
86
+ end
87
+
88
+ def currency_minor_feminine?(currency)
89
+ FEMININE_MINOR_CURRENCIES.include?(currency)
90
+ end
91
+
92
+ def currency_number_words(number, currency, unit:)
93
+ gender = if unit == :major
94
+ currency_major_feminine?(currency) ? :feminine : :masculine
95
+ else
96
+ currency_minor_feminine?(currency) ? :feminine : :masculine
97
+ end
98
+
99
+ cardinal(number, gender: gender)
100
+ end
101
+
102
+ def date_day(day, format:, date_case:)
103
+ day == 1 ? "primeiro" : cardinal(day)
104
+ end
105
+
106
+ def date_year(year, format:)
107
+ cardinal(year)
108
+ end
109
+
110
+ def ordinal(value, format, gender: :masculine)
111
+ ordinals = ORDINALS[format] || 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, gender: gender)
117
+ end
118
+
119
+ def cardinal(number, gender: :masculine)
120
+ integer_value = Integer(number)
121
+ negative = integer_value.negative?
122
+ integer_value = integer_value.abs
123
+
124
+ return ONES_MASC[0] if integer_value.zero?
125
+
126
+ groups = integer_value.to_s
127
+ .chars.reverse.each_slice(3).map(&:reverse)
128
+ .map(&:join).map!(&:to_i).reverse
129
+
130
+ words = []
131
+ groups.each_with_index do |group_value, index|
132
+ next if group_value.zero?
133
+
134
+ scale_idx = groups.size - index - 1
135
+ words.concat triple_to_words(group_value, scale_idx, feminine: gender == :feminine && scale_idx.zero?)
136
+ end
137
+
138
+ words.unshift(minus_word) if negative
139
+ words.join(" ")
140
+ end
141
+
142
+ def under_thousand(number, gender: :masculine)
143
+ return under_hundred(number, gender: gender) if number < 100
144
+ return "cem" if number == 100
145
+
146
+ hundreds = number / 100
147
+ rest = number % 100
148
+ words = [hundreds_data(gender)[hundreds]]
149
+ words << under_hundred(rest, gender: gender) if rest.positive?
150
+ words.join(" e ")
151
+ end
152
+
153
+ def under_hundred(number, gender: :masculine)
154
+ return one_word(number, gender: gender) if number < 10
155
+ return TEENS[number - 10] if number < 20
156
+
157
+ tens = number / 10
158
+ ones = number % 10
159
+ return TENS[tens] if ones.zero?
160
+
161
+ [TENS[tens], one_word(ones, gender: gender)].join(" e ")
162
+ end
163
+
164
+ def one_word(number, gender:)
165
+ return ONES_FEM[number] if gender == :feminine
166
+
167
+ ONES_MASC[number]
168
+ end
169
+
170
+ def hundreds_data(gender)
171
+ gender == :feminine ? HUNDREDS_FEM : HUNDREDS
172
+ end
173
+
174
+ def pluralize(number, singular, _few, plural)
175
+ number.abs == 1 ? singular : plural
176
+ end
15
177
  end
16
178
 
17
179
  register :pt, PT
@@ -10,8 +10,171 @@ module Num2words
10
10
  HUNDREDS = I18n.t("num2words.hundreds", locale: :ro)
11
11
  SCALES = I18n.t("num2words.scales", locale: :ro)
12
12
 
13
- MAJOR_UNIT = I18n.t("num2words.currencies.RON.name", locale: :ro)
14
- MINOR_UNIT = I18n.t("num2words.currencies.RON.fractional.name", locale: :ro)
13
+ FRACTIONS = I18n.t("num2words.fractions", locale: :ro)
14
+ GRAMMAR = I18n.t("num2words.grammar", locale: :ro)
15
+
16
+ DATE = I18n.t("num2words.date", locale: :ro)
17
+ DATE_TEMPLATE = I18n.t("num2words.date.template", locale: :ro)
18
+ TIME = I18n.t("num2words.time", locale: :ro)
19
+ TIME_TEMPLATE = I18n.t("num2words.time.template", locale: :ro)
20
+ DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :ro)
21
+
22
+ ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :ro)
23
+
24
+ FEMININE_MAJOR_CURRENCIES = %i[GBP RUB TRY UAH CZK BYN SEK NOK DKK INR IDR PKR].freeze
25
+ FEMININE_MINOR_CURRENCIES = %i[RUB UAH BYN ILS SAR].freeze
26
+
27
+ module_function
28
+
29
+ def integer_to_words(number, feminine: false)
30
+ cardinal(number, gender: feminine ? :feminine : :masculine)
31
+ end
32
+
33
+ def triple_to_words(number, scale_idx, feminine: false)
34
+ return [] if number.zero?
35
+
36
+ return [under_thousand(number, gender: feminine ? :feminine : :masculine)] if scale_idx.zero?
37
+
38
+ scale = pluralize(number, *SCALES[scale_idx])
39
+ words = scale_number_words(number, scale_idx)
40
+ words << "de" if needs_de_before_scale?(number)
41
+ words << scale
42
+ words
43
+ end
44
+
45
+ def minus_word
46
+ GRAMMAR[:minus]
47
+ end
48
+
49
+ def fraction_joiner(joiner)
50
+ joiner.to_sym == :and ? "și" : GRAMMAR[:conjunction]
51
+ end
52
+
53
+ def default_fraction_word
54
+ GRAMMAR[:default_fraction]
55
+ end
56
+
57
+ def fraction_numerator_feminine?
58
+ true
59
+ end
60
+
61
+ def decimal_separator_word
62
+ "virgulă"
63
+ end
64
+
65
+ def decimal_fraction_words(fraction_string)
66
+ fraction_string.chars.map { |digit| cardinal(digit.to_i) }.join(" ")
67
+ end
68
+
69
+ def time_unit_feminine?(unit)
70
+ %i[hour second].include?(unit)
71
+ end
72
+
73
+ def time_number_words(number, unit:)
74
+ gender = time_unit_feminine?(unit) ? :feminine : :masculine
75
+ cardinal(number, gender: gender)
76
+ end
77
+
78
+ def currency_major_feminine?(currency)
79
+ FEMININE_MAJOR_CURRENCIES.include?(currency)
80
+ end
81
+
82
+ def currency_minor_feminine?(currency)
83
+ FEMININE_MINOR_CURRENCIES.include?(currency)
84
+ end
85
+
86
+ def currency_number_words(number, currency, unit:)
87
+ gender = if unit == :major
88
+ currency_major_feminine?(currency) ? :feminine : :masculine
89
+ else
90
+ currency_minor_feminine?(currency) ? :feminine : :masculine
91
+ end
92
+
93
+ cardinal(number, gender: gender)
94
+ end
95
+
96
+ def date_day(day, format:, date_case:)
97
+ ordinal(day, :default)
98
+ end
99
+
100
+ def date_year(year, format:)
101
+ cardinal(year)
102
+ end
103
+
104
+ def ordinal(value, format, gender: :masculine)
105
+ ordinals = ORDINALS[format] || ORDINALS[:default] || ORDINALS[:nominative]
106
+ gender_data = ordinals[gender] || ordinals[:masculine]
107
+
108
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
109
+
110
+ cardinal(value, gender: gender)
111
+ end
112
+
113
+ def cardinal(number, gender: :masculine)
114
+ integer_value = Integer(number)
115
+ negative = integer_value.negative?
116
+ integer_value = integer_value.abs
117
+
118
+ return ONES_MASC[0] if integer_value.zero?
119
+
120
+ groups = integer_value.to_s
121
+ .chars.reverse.each_slice(3).map(&:reverse)
122
+ .map(&:join).map!(&:to_i).reverse
123
+
124
+ words = []
125
+ groups.each_with_index do |group_value, index|
126
+ next if group_value.zero?
127
+
128
+ scale_idx = groups.size - index - 1
129
+ words.concat triple_to_words(group_value, scale_idx, feminine: gender == :feminine && scale_idx.zero?)
130
+ end
131
+
132
+ words.unshift(minus_word) if negative
133
+ words.join(" ")
134
+ end
135
+
136
+ def scale_number_words(number, scale_idx)
137
+ return ["o"] if scale_idx == 1 && number == 1
138
+ return ["un"] if scale_idx > 1 && number == 1
139
+ return ["două"] if number == 2
140
+
141
+ [under_thousand(number, gender: :masculine)]
142
+ end
143
+
144
+ def needs_de_before_scale?(number)
145
+ number >= 20
146
+ end
147
+
148
+ def under_thousand(number, gender: :masculine)
149
+ return under_hundred(number, gender: gender) if number < 100
150
+
151
+ hundreds = number / 100
152
+ rest = number % 100
153
+ words = [HUNDREDS[hundreds]]
154
+ words << under_hundred(rest, gender: gender) if rest.positive?
155
+ words.join(" ")
156
+ end
157
+
158
+ def under_hundred(number, gender: :masculine)
159
+ return one_word(number, gender: gender) if number < 10
160
+ return TEENS[number - 10] if number < 20
161
+
162
+ tens = number / 10
163
+ ones = number % 10
164
+ return TENS[tens] if ones.zero?
165
+
166
+ [TENS[tens], one_word(ones, gender: gender)].join(" și ")
167
+ end
168
+
169
+ def one_word(number, gender:)
170
+ return ONES_FEM[number] if gender == :feminine
171
+
172
+ ONES_MASC[number]
173
+ end
174
+
175
+ def pluralize(number, singular, few, _plural)
176
+ number.abs == 1 ? singular : few
177
+ end
15
178
  end
16
179
 
17
180
  register :ro, RO
@@ -20,6 +20,106 @@ module Num2words
20
20
  DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :ru)
21
21
 
22
22
  ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :ru)
23
+
24
+ module_function
25
+
26
+ def feminine_group?(scale_idx)
27
+ scale_idx == 1
28
+ end
29
+
30
+ def fraction_numerator_feminine?
31
+ true
32
+ end
33
+
34
+ def minus_word
35
+ GRAMMAR[:minus]
36
+ end
37
+
38
+ def fraction_joiner(joiner)
39
+ joiner.to_sym == :and ? "и" : GRAMMAR[:conjunction]
40
+ end
41
+
42
+ def default_fraction_word
43
+ GRAMMAR[:default_fraction]
44
+ end
45
+
46
+ def triple_to_words(number, scale_idx, feminine: false)
47
+ words = []
48
+
49
+ words << HUNDREDS[number / 100] if number >= 100
50
+ rest = number % 100
51
+
52
+ if rest.between?(10, 19)
53
+ words << TEENS[rest - 10]
54
+ else
55
+ words << TENS[rest / 10] if rest >= 20
56
+ ones = rest % 10
57
+ words << (feminine ? ONES_FEM[ones] : ONES_MASC[ones]) if ones.positive?
58
+ end
59
+
60
+ words << pluralize(number, *SCALES[scale_idx]) unless scale_idx.zero?
61
+ words.compact
62
+ end
63
+
64
+ def date_day(day, format:, date_case:)
65
+ gender = date_case.to_sym == :genitive ? :masculine : :neuter
66
+ ordinal(day, format, gender: gender)
67
+ end
68
+
69
+ def date_year(year, format:)
70
+ ordinal(year, format)
71
+ end
72
+
73
+ def ordinal(value, format, gender: :masculine)
74
+ ordinals = ORDINALS[format]
75
+ gender_data = ordinals[gender] || ordinals[:masculine]
76
+
77
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
78
+
79
+ if value > 31
80
+ thousands = (value / 100) * 100
81
+ last_two = value % 100
82
+ base_year = cardinal(thousands)
83
+ last_ordinal = gender_data[last_two - 1] || cardinal(last_two)
84
+
85
+ return [base_year, last_ordinal].join(" ")
86
+ end
87
+
88
+ cardinal(value)
89
+ end
90
+
91
+ def cardinal(number, feminine: false)
92
+ integer_value = Integer(number)
93
+ negative = integer_value.negative?
94
+ integer_value = integer_value.abs
95
+
96
+ return (feminine ? ONES_FEM[0] : ONES_MASC[0]) if integer_value.zero?
97
+
98
+ groups = integer_value.to_s
99
+ .chars.reverse.each_slice(3).map(&:reverse)
100
+ .map(&:join).map!(&:to_i).reverse
101
+
102
+ words = []
103
+ groups.each_with_index do |group_value, index|
104
+ scale_idx = groups.size - index - 1
105
+ group_feminine = feminine_group?(scale_idx) || feminine
106
+ words.concat triple_to_words(group_value, scale_idx, feminine: group_feminine)
107
+ end
108
+
109
+ words.unshift(minus_word) if negative
110
+ words.join(" ")
111
+ end
112
+
113
+ def pluralize(number, singular, few, plural)
114
+ number = number.abs
115
+ return plural if (11..14).include?(number % 100)
116
+
117
+ case number % 10
118
+ when 1 then singular
119
+ when 2..4 then few
120
+ else plural
121
+ end
122
+ end
23
123
  end
24
124
 
25
125
  register :ru, RU
@@ -10,8 +10,130 @@ module Num2words
10
10
  HUNDREDS = I18n.t("num2words.hundreds", locale: :sk)
11
11
  SCALES = I18n.t("num2words.scales", locale: :sk)
12
12
 
13
- MAJOR_UNIT = I18n.t("num2words.currencies.EUR.name", locale: :sk)
14
- MINOR_UNIT = I18n.t("num2words.currencies.EUR.fractional.name", locale: :sk)
13
+ FRACTIONS = I18n.t("num2words.fractions", locale: :sk)
14
+ GRAMMAR = I18n.t("num2words.grammar", locale: :sk)
15
+
16
+ DATE = I18n.t("num2words.date", locale: :sk)
17
+ DATE_TEMPLATE = I18n.t("num2words.date.template", locale: :sk)
18
+ TIME = I18n.t("num2words.time", locale: :sk)
19
+ TIME_TEMPLATE = I18n.t("num2words.time.template", locale: :sk)
20
+ DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :sk)
21
+
22
+ ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :sk)
23
+
24
+ module_function
25
+
26
+ def fraction_numerator_feminine?
27
+ true
28
+ end
29
+
30
+ def feminine_group?(scale_idx)
31
+ scale_idx == 3
32
+ end
33
+
34
+ def currency_major_feminine?(currency)
35
+ %i[CZK GBP UAH SEK NOK DKK INR IDR PKR TRY].include?(currency)
36
+ end
37
+
38
+ def currency_minor_feminine?(currency)
39
+ %i[BGN BYN RUB UAH].include?(currency)
40
+ end
41
+
42
+ def minus_word
43
+ GRAMMAR[:minus]
44
+ end
45
+
46
+ def fraction_joiner(joiner)
47
+ joiner.to_sym == :and ? "a" : GRAMMAR[:conjunction]
48
+ end
49
+
50
+ def default_fraction_word
51
+ GRAMMAR[:default_fraction]
52
+ end
53
+
54
+ def decimal_separator_word
55
+ "čiarka"
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 integer_to_words(number, feminine: false)
63
+ cardinal(number, feminine: feminine)
64
+ end
65
+
66
+ def triple_to_words(number, scale_idx, feminine: false)
67
+ return [] if number.zero?
68
+
69
+ words = []
70
+ words << HUNDREDS[number / 100] if number >= 100
71
+ rest = number % 100
72
+
73
+ if rest.between?(10, 19)
74
+ words << TEENS[rest - 10]
75
+ else
76
+ words << TENS[rest / 10] if rest >= 20
77
+ ones = rest % 10
78
+ words << (feminine ? ONES_FEM[ones] : ONES_MASC[ones]) if ones.positive?
79
+ end
80
+
81
+ words << pluralize(number, *SCALES[scale_idx]) unless scale_idx.zero?
82
+ words.compact
83
+ end
84
+
85
+ def date_day(day, format:, date_case:)
86
+ ordinal(day, :default)
87
+ end
88
+
89
+ def date_year(year, format:)
90
+ cardinal(year)
91
+ end
92
+
93
+ def ordinal(value, format, gender: :masculine)
94
+ ordinals = ORDINALS[format] || ORDINALS[:default] || ORDINALS[:nominative]
95
+ gender_data = ordinals[gender] || ordinals[:masculine]
96
+
97
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
98
+
99
+ cardinal(value)
100
+ end
101
+
102
+ def cardinal(number, feminine: false)
103
+ integer_value = Integer(number)
104
+ negative = integer_value.negative?
105
+ integer_value = integer_value.abs
106
+
107
+ return (feminine ? ONES_FEM[0] : ONES_MASC[0]) if integer_value.zero?
108
+
109
+ groups = integer_value.to_s
110
+ .chars.reverse.each_slice(3).map(&:reverse)
111
+ .map(&:join).map!(&:to_i).reverse
112
+
113
+ words = []
114
+ groups.each_with_index do |group_value, index|
115
+ next if group_value.zero?
116
+
117
+ scale_idx = groups.size - index - 1
118
+ group_words = triple_to_words(group_value, scale_idx, feminine: feminine_group?(scale_idx) || (feminine && scale_idx.zero?))
119
+ group_words.shift if scale_idx == 1 && group_value == 1
120
+ words.concat(group_words)
121
+ end
122
+
123
+ words.unshift(minus_word) if negative
124
+ words.join(" ")
125
+ end
126
+
127
+ def pluralize(number, singular, few, plural)
128
+ number = number.abs
129
+ return plural if (11..14).include?(number % 100)
130
+
131
+ case number % 10
132
+ when 1 then singular
133
+ when 2..4 then few
134
+ else plural
135
+ end
136
+ end
15
137
  end
16
138
 
17
139
  register :sk, SK