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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +113 -144
- data/config/locales/ar.yml +5 -1
- data/config/locales/be.yml +4 -0
- data/config/locales/bg.yml +4 -0
- data/config/locales/bn.yml +4 -0
- data/config/locales/cs.yml +6 -2
- data/config/locales/da.yml +4 -0
- data/config/locales/de.yml +8 -4
- data/config/locales/el.yml +4 -0
- data/config/locales/en.yml +4 -0
- data/config/locales/es.yml +4 -0
- data/config/locales/et.yml +9 -5
- data/config/locales/fa.yml +15 -11
- data/config/locales/fi.yml +8 -4
- data/config/locales/fr.yml +4 -0
- data/config/locales/gu.yml +4 -0
- data/config/locales/he.yml +6 -2
- data/config/locales/hi.yml +4 -0
- data/config/locales/hr.yml +8 -4
- data/config/locales/hu.yml +6 -2
- data/config/locales/id.yml +4 -0
- data/config/locales/it.yml +4 -0
- data/config/locales/ja.yml +10 -5
- data/config/locales/kn.yml +186 -10
- data/config/locales/ko.yml +184 -13
- data/config/locales/kz.yml +183 -17
- data/config/locales/lt.yml +184 -8
- data/config/locales/lv.yml +188 -12
- data/config/locales/ml.yml +189 -13
- data/config/locales/mr.yml +184 -8
- data/config/locales/ms.yml +184 -8
- data/config/locales/nb.yml +183 -17
- data/config/locales/nl.yml +184 -13
- data/config/locales/pa.yml +189 -13
- data/config/locales/pl.yml +184 -13
- data/config/locales/pt.yml +184 -13
- data/config/locales/ro.yml +183 -12
- data/config/locales/ru.yml +4 -0
- data/config/locales/sk.yml +184 -8
- data/config/locales/sl.yml +185 -9
- data/config/locales/sr.yml +186 -15
- data/config/locales/sv.yml +182 -13
- data/config/locales/sw.yml +184 -8
- data/config/locales/ta.yml +184 -8
- data/config/locales/te.yml +185 -9
- data/config/locales/th.yml +183 -12
- data/config/locales/tr.yml +183 -12
- data/config/locales/uk.yml +189 -18
- data/config/locales/ur.yml +187 -11
- data/config/locales/vi.yml +183 -12
- data/config/locales/zh.yml +191 -9
- data/lib/num2words/converter.rb +144 -35
- data/lib/num2words/locales/ar.rb +130 -0
- data/lib/num2words/locales/be.rb +101 -0
- data/lib/num2words/locales/bg.rb +127 -0
- data/lib/num2words/locales/bn.rb +112 -0
- data/lib/num2words/locales/cs.rb +106 -0
- data/lib/num2words/locales/da.rb +97 -0
- data/lib/num2words/locales/de.rb +119 -0
- data/lib/num2words/locales/el.rb +143 -0
- data/lib/num2words/locales/en.rb +90 -0
- data/lib/num2words/locales/es.rb +185 -0
- data/lib/num2words/locales/et.rb +104 -0
- data/lib/num2words/locales/fa.rb +104 -0
- data/lib/num2words/locales/fi.rb +104 -0
- data/lib/num2words/locales/fr.rb +121 -0
- data/lib/num2words/locales/gu.rb +112 -0
- data/lib/num2words/locales/he.rb +130 -0
- data/lib/num2words/locales/hi.rb +112 -0
- data/lib/num2words/locales/hr.rb +117 -0
- data/lib/num2words/locales/hu.rb +111 -0
- data/lib/num2words/locales/id.rb +104 -0
- data/lib/num2words/locales/it.rb +142 -0
- data/lib/num2words/locales/ja.rb +126 -0
- data/lib/num2words/locales/kn.rb +122 -2
- data/lib/num2words/locales/ko.rb +158 -2
- data/lib/num2words/locales/kz.rb +137 -2
- data/lib/num2words/locales/lt.rb +146 -2
- data/lib/num2words/locales/lv.rb +148 -2
- data/lib/num2words/locales/ml.rb +142 -2
- data/lib/num2words/locales/mr.rb +142 -2
- data/lib/num2words/locales/ms.rb +134 -2
- data/lib/num2words/locales/nb.rb +153 -0
- data/lib/num2words/locales/nl.rb +142 -2
- data/lib/num2words/locales/pa.rb +122 -2
- data/lib/num2words/locales/pl.rb +149 -2
- data/lib/num2words/locales/pt.rb +164 -2
- data/lib/num2words/locales/ro.rb +165 -2
- data/lib/num2words/locales/ru.rb +100 -0
- data/lib/num2words/locales/sk.rb +124 -2
- data/lib/num2words/locales/sl.rb +154 -2
- data/lib/num2words/locales/sr.rb +141 -3
- data/lib/num2words/locales/sv.rb +141 -2
- data/lib/num2words/locales/sw.rb +133 -2
- data/lib/num2words/locales/ta.rb +142 -2
- data/lib/num2words/locales/te.rb +142 -2
- data/lib/num2words/locales/th.rb +133 -2
- data/lib/num2words/locales/tr.rb +141 -2
- data/lib/num2words/locales/uk.rb +134 -2
- data/lib/num2words/locales/ur.rb +130 -2
- data/lib/num2words/locales/vi.rb +142 -2
- data/lib/num2words/locales/zh.rb +172 -2
- data/lib/num2words/version.rb +1 -1
- data/num2words.gemspec +2 -2
- metadata +6 -6
- data/lib/num2words/locales/no.rb +0 -19
data/lib/num2words/converter.rb
CHANGED
|
@@ -57,23 +57,25 @@ module Num2words
|
|
|
57
57
|
|
|
58
58
|
decimal_amount = decimal_currency_amount(amount)
|
|
59
59
|
major_value, minor_value = format('%.2f', decimal_amount.abs).split('.').map(&:to_i)
|
|
60
|
+
major_feminine = locale_currency_major_feminine?(Locales[locale], currency)
|
|
61
|
+
minor_feminine = locale_currency_minor_feminine?(Locales[locale], currency)
|
|
60
62
|
|
|
61
63
|
parts = [
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
locale_currency_number_words(Locales[locale], major_value, currency, unit: :major, locale: locale, feminine: major_feminine),
|
|
65
|
+
locale_pluralize(Locales[locale], major_value, currency_data[:major_unit])
|
|
64
66
|
]
|
|
65
67
|
|
|
66
68
|
include_minor = minor == :always || (minor == :nonzero && minor_value.positive?)
|
|
67
69
|
if include_minor
|
|
68
70
|
parts.concat([
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
locale_currency_number_words(Locales[locale], minor_value, currency, unit: :minor, locale: locale, feminine: minor_feminine),
|
|
72
|
+
locale_pluralize(Locales[locale], minor_value, currency_data[:minor_unit])
|
|
71
73
|
])
|
|
72
74
|
end
|
|
73
75
|
|
|
74
|
-
parts.unshift(Locales[locale]
|
|
76
|
+
parts.unshift(locale_minus_word(Locales[locale])) if decimal_amount.negative?
|
|
75
77
|
|
|
76
|
-
apply_case(
|
|
78
|
+
apply_case(locale_join_currency_parts(Locales[locale], parts), word_case)
|
|
77
79
|
end
|
|
78
80
|
|
|
79
81
|
private
|
|
@@ -104,10 +106,103 @@ module Num2words
|
|
|
104
106
|
end
|
|
105
107
|
end
|
|
106
108
|
|
|
109
|
+
def locale_pluralize(locale_data, number, forms)
|
|
110
|
+
return locale_data.pluralize(number, *forms) if locale_data.respond_to?(:pluralize)
|
|
111
|
+
|
|
112
|
+
pluralize(number, *forms)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def locale_currency_major_feminine?(locale_data, currency)
|
|
116
|
+
return locale_data.currency_major_feminine?(currency) if locale_data.respond_to?(:currency_major_feminine?)
|
|
117
|
+
|
|
118
|
+
false
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def locale_currency_minor_feminine?(locale_data, currency)
|
|
122
|
+
return locale_data.currency_minor_feminine?(currency) if locale_data.respond_to?(:currency_minor_feminine?)
|
|
123
|
+
|
|
124
|
+
true
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def locale_currency_number_words(locale_data, number, currency, unit:, locale:, feminine:)
|
|
128
|
+
if locale_data.respond_to?(:currency_number_words)
|
|
129
|
+
return locale_data.currency_number_words(number, currency, unit: unit)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
to_words(number, locale: locale, feminine: feminine)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def locale_join_currency_parts(locale_data, parts)
|
|
136
|
+
return locale_data.join_currency_parts(parts) if locale_data.respond_to?(:join_currency_parts)
|
|
137
|
+
|
|
138
|
+
parts.join(" ").strip
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def locale_time_unit_feminine?(locale_data, unit, default)
|
|
142
|
+
return locale_data.time_unit_feminine?(unit) if locale_data.respond_to?(:time_unit_feminine?)
|
|
143
|
+
|
|
144
|
+
default
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def locale_time_number_words(locale_data, number, unit, locale, feminine)
|
|
148
|
+
if locale_data.respond_to?(:time_number_words)
|
|
149
|
+
return locale_data.time_number_words(number, unit: unit)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
to_words_integer(number, locale, feminine, locale_data)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def locale_join_time_words(locale_data, number_words, unit_words)
|
|
156
|
+
return locale_data.join_time_words(number_words, unit_words) if locale_data.respond_to?(:join_time_words)
|
|
157
|
+
|
|
158
|
+
[number_words, unit_words].join(" ")
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def locale_minus_word(locale_data)
|
|
162
|
+
return locale_data.minus_word if locale_data.respond_to?(:minus_word)
|
|
163
|
+
|
|
164
|
+
locale_data::GRAMMAR[:minus]
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def locale_fraction_joiner(locale_data, joiner)
|
|
168
|
+
return locale_data.fraction_joiner(joiner) if locale_data.respond_to?(:fraction_joiner)
|
|
169
|
+
|
|
170
|
+
locale_data::GRAMMAR[:conjunction]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def locale_default_fraction_word(locale_data)
|
|
174
|
+
return locale_data.default_fraction_word if locale_data.respond_to?(:default_fraction_word)
|
|
175
|
+
|
|
176
|
+
locale_data::GRAMMAR[:default_fraction]
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def locale_decimal_separator_word(locale_data)
|
|
180
|
+
return locale_data.decimal_separator_word if locale_data.respond_to?(:decimal_separator_word)
|
|
181
|
+
|
|
182
|
+
locale_data::GRAMMAR[:decimal_separator]
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def locale_join_decimal_words(locale_data, words)
|
|
186
|
+
return locale_data.join_decimal_words(words) if locale_data.respond_to?(:join_decimal_words)
|
|
187
|
+
|
|
188
|
+
words.compact.reject(&:empty?).join(" ")
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def locale_join_fraction_words(locale_data, words)
|
|
192
|
+
return locale_data.join_fraction_words(words) if locale_data.respond_to?(:join_fraction_words)
|
|
193
|
+
|
|
194
|
+
words.reject(&:empty?).join(" ")
|
|
195
|
+
end
|
|
196
|
+
|
|
107
197
|
# n — 0..999, scale_idx — индекс разряда (0 — единицы, 1 — тысячи, ...)
|
|
108
198
|
# feminine: true — использовать женский род для единиц (нужно для тысяч/копеек)
|
|
109
|
-
def triple_to_words(n, scale_idx, local_data, feminine: false)
|
|
199
|
+
def triple_to_words(n, scale_idx, local_data, feminine: false, locale: nil)
|
|
110
200
|
return [] if n.zero?
|
|
201
|
+
|
|
202
|
+
if local_data.respond_to?(:triple_to_words)
|
|
203
|
+
return local_data.triple_to_words(n, scale_idx, feminine: feminine)
|
|
204
|
+
end
|
|
205
|
+
|
|
111
206
|
words = []
|
|
112
207
|
|
|
113
208
|
words << local_data::HUNDREDS[n / 100] if n >= 100
|
|
@@ -121,14 +216,14 @@ module Num2words
|
|
|
121
216
|
words << (feminine ? local_data::ONES_FEM[ones] : local_data::ONES_MASC[ones]) if ones.positive?
|
|
122
217
|
end
|
|
123
218
|
|
|
124
|
-
words <<
|
|
219
|
+
words << locale_pluralize(local_data, n, local_data::SCALES[scale_idx]) unless scale_idx.zero?
|
|
125
220
|
words.compact
|
|
126
221
|
end
|
|
127
222
|
|
|
128
223
|
def to_words_fractional(number, locale, feminine, locale_data, style: :fraction, joiner: :default)
|
|
129
|
-
minus_word = locale_data
|
|
130
|
-
conjunction_word =
|
|
131
|
-
default_fraction = locale_data
|
|
224
|
+
minus_word = locale_minus_word(locale_data)
|
|
225
|
+
conjunction_word = locale_fraction_joiner(locale_data, joiner)
|
|
226
|
+
default_fraction = locale_default_fraction_word(locale_data)
|
|
132
227
|
fractions_data = locale_data::FRACTIONS || {}
|
|
133
228
|
|
|
134
229
|
negative = number.is_a?(String) ? number.start_with?("-") : number.negative?
|
|
@@ -146,24 +241,26 @@ module Num2words
|
|
|
146
241
|
|
|
147
242
|
integer_words = to_words_integer(integer_value, locale, feminine, locale_data)
|
|
148
243
|
|
|
149
|
-
if
|
|
150
|
-
fraction_digits =
|
|
151
|
-
|
|
152
|
-
return full_string
|
|
244
|
+
if style == :decimal && locale_data.respond_to?(:decimal_fraction_words)
|
|
245
|
+
fraction_digits = locale_data.decimal_fraction_words(fraction_string)
|
|
246
|
+
return locale_join_decimal_words(locale_data, [sign_word, integer_words, locale_decimal_separator_word(locale_data), fraction_digits])
|
|
153
247
|
end
|
|
154
248
|
|
|
155
|
-
|
|
249
|
+
numerator_feminine = locale_data.respond_to?(:fraction_numerator_feminine?) ? locale_data.fraction_numerator_feminine? : feminine
|
|
250
|
+
numerator_words = to_words_integer(numerator, locale, numerator_feminine, locale_data)
|
|
156
251
|
|
|
157
252
|
denom_forms = fractions_data[denominator] || fractions_data[denominator.to_s] # массив склонений
|
|
158
|
-
denominator_words = denom_forms.is_a?(Array) ?
|
|
253
|
+
denominator_words = denom_forms.is_a?(Array) ? locale_pluralize(locale_data, numerator, denom_forms) : default_fraction
|
|
159
254
|
|
|
160
|
-
[sign_word, integer_words, conjunction_word, numerator_words, denominator_words]
|
|
255
|
+
locale_join_fraction_words(locale_data, [sign_word, integer_words, conjunction_word, numerator_words, denominator_words])
|
|
161
256
|
end
|
|
162
257
|
|
|
163
258
|
def to_words_integer(number, locale, feminine, locale_data)
|
|
259
|
+
return locale_data.integer_to_words(number, feminine: feminine) if locale_data.respond_to?(:integer_to_words)
|
|
260
|
+
|
|
164
261
|
integer_value = Integer(number)
|
|
165
262
|
|
|
166
|
-
minus_word = locale_data
|
|
263
|
+
minus_word = locale_minus_word(locale_data)
|
|
167
264
|
negative = integer_value.negative?
|
|
168
265
|
integer_value = integer_value.abs
|
|
169
266
|
|
|
@@ -176,8 +273,8 @@ module Num2words
|
|
|
176
273
|
words = []
|
|
177
274
|
groups.each_with_index do |group_value, index|
|
|
178
275
|
scale_index = groups.size - index - 1
|
|
179
|
-
group_feminine = (
|
|
180
|
-
words.concat triple_to_words(group_value, scale_index, locale_data, feminine: group_feminine)
|
|
276
|
+
group_feminine = (locale_data.respond_to?(:feminine_group?) && locale_data.feminine_group?(scale_index)) || feminine
|
|
277
|
+
words.concat triple_to_words(group_value, scale_index, locale_data, feminine: group_feminine, locale: locale)
|
|
181
278
|
end
|
|
182
279
|
|
|
183
280
|
words.unshift(minus_word) if negative
|
|
@@ -198,9 +295,17 @@ module Num2words
|
|
|
198
295
|
raise ArgumentError, "Template not found for locale #{locale}" unless template
|
|
199
296
|
|
|
200
297
|
day_gender = date_case.to_sym == :genitive ? :masculine : :neuter
|
|
201
|
-
day_words =
|
|
298
|
+
day_words = if locale_data.respond_to?(:date_day)
|
|
299
|
+
locale_data.date_day(day, format: format, date_case: date_case)
|
|
300
|
+
else
|
|
301
|
+
to_words_ordinal(day, locale, format, locale_data, gender: day_gender)
|
|
302
|
+
end
|
|
202
303
|
month_words = months[month - 1]
|
|
203
|
-
year_words =
|
|
304
|
+
year_words = if locale_data.respond_to?(:date_year)
|
|
305
|
+
locale_data.date_year(year, format: format)
|
|
306
|
+
else
|
|
307
|
+
to_words_ordinal(year, locale, format, locale_data)
|
|
308
|
+
end
|
|
204
309
|
|
|
205
310
|
template % { day: day_words, month: month_words, year: year_words }
|
|
206
311
|
end
|
|
@@ -236,18 +341,22 @@ module Num2words
|
|
|
236
341
|
words = locale_data::TIME[:words]
|
|
237
342
|
template = locale_data::TIME_TEMPLATE
|
|
238
343
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
344
|
+
hour_feminine = locale_time_unit_feminine?(locale_data, :hour, false)
|
|
345
|
+
minute_feminine = locale_time_unit_feminine?(locale_data, :minute, true)
|
|
346
|
+
second_feminine = locale_time_unit_feminine?(locale_data, :second, true)
|
|
347
|
+
|
|
348
|
+
hours = locale_join_time_words(locale_data,
|
|
349
|
+
locale_time_number_words(locale_data, time.hour, :hour, locale, hour_feminine),
|
|
350
|
+
locale_pluralize(locale_data, time.hour, words[:hour])
|
|
351
|
+
)
|
|
352
|
+
minutes = locale_join_time_words(locale_data,
|
|
353
|
+
locale_time_number_words(locale_data, time.min, :minute, locale, minute_feminine),
|
|
354
|
+
locale_pluralize(locale_data, time.min, words[:minute])
|
|
355
|
+
)
|
|
356
|
+
seconds = locale_join_time_words(locale_data,
|
|
357
|
+
locale_time_number_words(locale_data, time.sec, :second, locale, second_feminine),
|
|
358
|
+
locale_pluralize(locale_data, time.sec, words[:second])
|
|
359
|
+
)
|
|
251
360
|
|
|
252
361
|
format = if short
|
|
253
362
|
time.min.zero? && time.sec.zero? ? :hours_only : :hours_minutes
|
data/lib/num2words/locales/ar.rb
CHANGED
|
@@ -20,6 +20,136 @@ module Num2words
|
|
|
20
20
|
DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :ar)
|
|
21
21
|
|
|
22
22
|
ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :ar)
|
|
23
|
+
|
|
24
|
+
module_function
|
|
25
|
+
|
|
26
|
+
def integer_to_words(number, feminine: false)
|
|
27
|
+
integer_value = Integer(number)
|
|
28
|
+
negative = integer_value.negative?
|
|
29
|
+
integer_value = integer_value.abs
|
|
30
|
+
|
|
31
|
+
return (feminine ? ONES_FEM[0] : ONES_MASC[0]) if integer_value.zero?
|
|
32
|
+
|
|
33
|
+
groups = integer_value.to_s
|
|
34
|
+
.chars.reverse.each_slice(3).map(&:reverse)
|
|
35
|
+
.map(&:join).map!(&:to_i).reverse
|
|
36
|
+
|
|
37
|
+
words = []
|
|
38
|
+
groups.each_with_index do |group_value, index|
|
|
39
|
+
next if group_value.zero?
|
|
40
|
+
|
|
41
|
+
scale_idx = groups.size - index - 1
|
|
42
|
+
words << scale_group_to_words(group_value, scale_idx, feminine: feminine && scale_idx.zero?)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
result = words.join(" و")
|
|
46
|
+
negative ? [minus_word, result].join(" ") : result
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def triple_to_words(number, scale_idx, feminine: false)
|
|
50
|
+
scale_group_to_words(number, scale_idx, feminine: feminine).split
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def scale_group_to_words(number, scale_idx, feminine: false)
|
|
54
|
+
return under_thousand(number, feminine: feminine) if scale_idx.zero?
|
|
55
|
+
|
|
56
|
+
scale_forms = SCALES[scale_idx]
|
|
57
|
+
case number
|
|
58
|
+
when 1
|
|
59
|
+
scale_forms[0]
|
|
60
|
+
when 2
|
|
61
|
+
scale_forms[1]
|
|
62
|
+
else
|
|
63
|
+
scale_form = number.between?(3, 10) ? scale_forms[2] : scale_forms[0]
|
|
64
|
+
[under_thousand(number), scale_form].join(" ")
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def under_thousand(number, feminine: false)
|
|
69
|
+
hundreds = number / 100
|
|
70
|
+
rest = number % 100
|
|
71
|
+
|
|
72
|
+
return under_hundred(rest, feminine: feminine) if hundreds.zero?
|
|
73
|
+
return HUNDREDS[hundreds] if rest.zero?
|
|
74
|
+
|
|
75
|
+
[HUNDREDS[hundreds], under_hundred(rest, feminine: feminine)].join(" و")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def under_hundred(number, feminine: false)
|
|
79
|
+
ones_data = feminine ? ONES_FEM : ONES_MASC
|
|
80
|
+
|
|
81
|
+
return ones_data[number] if number < 10
|
|
82
|
+
return TEENS[number - 10] if number < 20
|
|
83
|
+
|
|
84
|
+
tens = number / 10
|
|
85
|
+
ones = number % 10
|
|
86
|
+
|
|
87
|
+
return TENS[tens] if ones.zero?
|
|
88
|
+
|
|
89
|
+
[ones_data[ones], TENS[tens]].join(" و")
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def minus_word
|
|
93
|
+
GRAMMAR[:minus]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def fraction_joiner(joiner)
|
|
97
|
+
joiner.to_sym == :and ? "و" : GRAMMAR[:conjunction]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def default_fraction_word
|
|
101
|
+
GRAMMAR[:default_fraction]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def decimal_separator_word
|
|
105
|
+
GRAMMAR[:conjunction]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def decimal_fraction_words(fraction_string)
|
|
109
|
+
fraction_string.chars.map { |digit| integer_to_words(digit.to_i) }.join(" ")
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def join_fraction_words(words)
|
|
113
|
+
parts = words.reject(&:empty?)
|
|
114
|
+
joiner_index = parts.index("و")
|
|
115
|
+
|
|
116
|
+
if joiner_index && parts[joiner_index + 1]
|
|
117
|
+
parts[joiner_index + 1] = "و#{parts[joiner_index + 1]}"
|
|
118
|
+
parts.delete_at(joiner_index)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
parts.join(" ")
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def fraction_numerator_feminine?
|
|
125
|
+
false
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def date_day(day, format:, date_case:)
|
|
129
|
+
ordinal(day, format)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def date_year(year, format:)
|
|
133
|
+
integer_to_words(year)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def ordinal(value, format, gender: :masculine)
|
|
137
|
+
ordinals = ORDINALS[format] || ORDINALS[:default]
|
|
138
|
+
gender_data = ordinals[gender] || ordinals[:masculine]
|
|
139
|
+
|
|
140
|
+
return gender_data[value - 1] if value.between?(1, gender_data.length)
|
|
141
|
+
|
|
142
|
+
integer_to_words(value)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def pluralize(number, singular, dual, plural)
|
|
146
|
+
number = number.abs
|
|
147
|
+
return singular if number == 1
|
|
148
|
+
return dual if number == 2
|
|
149
|
+
return plural if number.zero? || number.between?(3, 10)
|
|
150
|
+
|
|
151
|
+
singular
|
|
152
|
+
end
|
|
23
153
|
end
|
|
24
154
|
|
|
25
155
|
register :ar, AR
|
data/lib/num2words/locales/be.rb
CHANGED
|
@@ -20,6 +20,107 @@ module Num2words
|
|
|
20
20
|
DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :be)
|
|
21
21
|
|
|
22
22
|
ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :be)
|
|
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
|
+
return ordinal(day, :default, gender: :masculine) if date_case.to_sym == :genitive
|
|
66
|
+
|
|
67
|
+
ordinal(day, :nominative, gender: :neuter)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def date_year(year, format:)
|
|
71
|
+
ordinal(year, :default)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def ordinal(value, format, gender: :masculine)
|
|
75
|
+
ordinals = ORDINALS[format]
|
|
76
|
+
gender_data = ordinals[gender] || ordinals[:masculine]
|
|
77
|
+
|
|
78
|
+
return gender_data[value - 1] if value.between?(1, gender_data.length)
|
|
79
|
+
|
|
80
|
+
if value > 31
|
|
81
|
+
thousands = (value / 100) * 100
|
|
82
|
+
last_two = value % 100
|
|
83
|
+
base_year = cardinal(thousands)
|
|
84
|
+
last_ordinal = gender_data[last_two - 1] || cardinal(last_two)
|
|
85
|
+
|
|
86
|
+
return [base_year, last_ordinal].join(" ")
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
cardinal(value)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def cardinal(number, feminine: false)
|
|
93
|
+
integer_value = Integer(number)
|
|
94
|
+
negative = integer_value.negative?
|
|
95
|
+
integer_value = integer_value.abs
|
|
96
|
+
|
|
97
|
+
return (feminine ? ONES_FEM[0] : ONES_MASC[0]) if integer_value.zero?
|
|
98
|
+
|
|
99
|
+
groups = integer_value.to_s
|
|
100
|
+
.chars.reverse.each_slice(3).map(&:reverse)
|
|
101
|
+
.map(&:join).map!(&:to_i).reverse
|
|
102
|
+
|
|
103
|
+
words = []
|
|
104
|
+
groups.each_with_index do |group_value, index|
|
|
105
|
+
scale_idx = groups.size - index - 1
|
|
106
|
+
group_feminine = feminine_group?(scale_idx) || feminine
|
|
107
|
+
words.concat triple_to_words(group_value, scale_idx, feminine: group_feminine)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
words.unshift(minus_word) if negative
|
|
111
|
+
words.join(" ")
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def pluralize(number, singular, few, plural)
|
|
115
|
+
number = number.abs
|
|
116
|
+
return plural if (11..14).include?(number % 100)
|
|
117
|
+
|
|
118
|
+
case number % 10
|
|
119
|
+
when 1 then singular
|
|
120
|
+
when 2..4 then few
|
|
121
|
+
else plural
|
|
122
|
+
end
|
|
123
|
+
end
|
|
23
124
|
end
|
|
24
125
|
|
|
25
126
|
register :be, BE
|
data/lib/num2words/locales/bg.rb
CHANGED
|
@@ -20,6 +20,133 @@ module Num2words
|
|
|
20
20
|
DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :bg)
|
|
21
21
|
|
|
22
22
|
ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :bg)
|
|
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 integer_to_words(number, feminine: false)
|
|
47
|
+
cardinal(number, feminine: feminine)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def triple_to_words(number, scale_idx, feminine: false)
|
|
51
|
+
words = []
|
|
52
|
+
|
|
53
|
+
hundreds = number / 100
|
|
54
|
+
rest = number % 100
|
|
55
|
+
|
|
56
|
+
words << HUNDREDS[hundreds] if hundreds.positive?
|
|
57
|
+
|
|
58
|
+
if rest.between?(10, 19)
|
|
59
|
+
words << join_with_conjunction(words, TEENS[rest - 10])
|
|
60
|
+
else
|
|
61
|
+
tens = rest / 10
|
|
62
|
+
ones = rest % 10
|
|
63
|
+
|
|
64
|
+
if tens >= 2
|
|
65
|
+
tens_words = TENS[tens]
|
|
66
|
+
words << (ones.positive? ? "#{tens_words} и #{feminine ? ONES_FEM[ones] : ONES_MASC[ones]}" : tens_words)
|
|
67
|
+
elsif ones.positive?
|
|
68
|
+
words << join_with_conjunction(words, feminine ? ONES_FEM[ones] : ONES_MASC[ones])
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
words << pluralize(number, *SCALES[scale_idx]) unless scale_idx.zero?
|
|
73
|
+
words.compact
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def date_day(day, format:, date_case:)
|
|
77
|
+
gender = date_case.to_sym == :genitive ? :masculine : :neuter
|
|
78
|
+
ordinal(day, :nominative, gender: gender)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def date_year(year, format:)
|
|
82
|
+
ordinal(year, :nominative)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def ordinal(value, format, gender: :masculine)
|
|
86
|
+
ordinals = ORDINALS[format]
|
|
87
|
+
gender_data = ordinals[gender] || ordinals[:masculine]
|
|
88
|
+
|
|
89
|
+
return gender_data[value - 1] if value.between?(1, gender_data.length)
|
|
90
|
+
|
|
91
|
+
if value > 31
|
|
92
|
+
thousands = (value / 100) * 100
|
|
93
|
+
last_two = value % 100
|
|
94
|
+
base_year = cardinal(thousands)
|
|
95
|
+
last_ordinal = gender_data[last_two - 1] || cardinal(last_two)
|
|
96
|
+
|
|
97
|
+
return [base_year, last_ordinal].join(" ")
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
cardinal(value)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def cardinal(number, feminine: false)
|
|
104
|
+
integer_value = Integer(number)
|
|
105
|
+
negative = integer_value.negative?
|
|
106
|
+
integer_value = integer_value.abs
|
|
107
|
+
|
|
108
|
+
return (feminine ? ONES_FEM[0] : ONES_MASC[0]) if integer_value.zero?
|
|
109
|
+
|
|
110
|
+
groups = integer_value.to_s
|
|
111
|
+
.chars.reverse.each_slice(3).map(&:reverse)
|
|
112
|
+
.map(&:join).map!(&:to_i).reverse
|
|
113
|
+
|
|
114
|
+
group_phrases = []
|
|
115
|
+
groups.each_with_index do |group_value, index|
|
|
116
|
+
next if group_value.zero?
|
|
117
|
+
|
|
118
|
+
scale_idx = groups.size - index - 1
|
|
119
|
+
group_feminine = feminine_group?(scale_idx) || feminine
|
|
120
|
+
group_words = triple_to_words(group_value, scale_idx, feminine: group_feminine)
|
|
121
|
+
group_words.shift if scale_idx == 1 && group_value == 1
|
|
122
|
+
group_phrases << [group_words.join(" "), scale_idx, group_value]
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
result = join_groups(group_phrases)
|
|
126
|
+
negative ? [minus_word, result].join(" ") : result
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def pluralize(number, singular, few, plural)
|
|
130
|
+
number.abs == 1 ? singular : few
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def join_with_conjunction(words, value)
|
|
134
|
+
words.empty? ? value : "и #{value}"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def join_groups(group_phrases)
|
|
138
|
+
return "" if group_phrases.empty?
|
|
139
|
+
|
|
140
|
+
phrases = group_phrases.map(&:first)
|
|
141
|
+
last_scale_idx = group_phrases[-1][1]
|
|
142
|
+
last_value = group_phrases[-1][2]
|
|
143
|
+
|
|
144
|
+
if group_phrases.size > 1 && last_scale_idx.zero? && last_value < 100
|
|
145
|
+
[phrases[0...-1].join(" "), phrases[-1]].join(" и ")
|
|
146
|
+
else
|
|
147
|
+
phrases.join(" ")
|
|
148
|
+
end
|
|
149
|
+
end
|
|
23
150
|
end
|
|
24
151
|
|
|
25
152
|
register :bg, BG
|