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/locales/ru.rb
CHANGED
|
@@ -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
|
data/lib/num2words/locales/sk.rb
CHANGED
|
@@ -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
|
-
|
|
14
|
-
|
|
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
|
data/lib/num2words/locales/sl.rb
CHANGED
|
@@ -10,8 +10,160 @@ module Num2words
|
|
|
10
10
|
HUNDREDS = I18n.t("num2words.hundreds", locale: :sl)
|
|
11
11
|
SCALES = I18n.t("num2words.scales", locale: :sl)
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
FRACTIONS = I18n.t("num2words.fractions", locale: :sl)
|
|
14
|
+
GRAMMAR = I18n.t("num2words.grammar", locale: :sl)
|
|
15
|
+
|
|
16
|
+
DATE = I18n.t("num2words.date", locale: :sl)
|
|
17
|
+
DATE_TEMPLATE = I18n.t("num2words.date.template", locale: :sl)
|
|
18
|
+
TIME = I18n.t("num2words.time", locale: :sl)
|
|
19
|
+
TIME_TEMPLATE = I18n.t("num2words.time.template", locale: :sl)
|
|
20
|
+
DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :sl)
|
|
21
|
+
|
|
22
|
+
ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :sl)
|
|
23
|
+
|
|
24
|
+
FEMININE_MAJOR_CURRENCIES = %i[CZK GBP RUB TRY UAH SEK NOK DKK BGN BYN INR IDR BDT PKR].freeze
|
|
25
|
+
FEMININE_MINOR_CURRENCIES = %i[RUB UAH BGN BYN ILS SAR RSD BDT].freeze
|
|
26
|
+
|
|
27
|
+
module_function
|
|
28
|
+
|
|
29
|
+
def feminine_group?(scale_idx)
|
|
30
|
+
scale_idx == 3
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def fraction_numerator_feminine?
|
|
34
|
+
true
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def currency_major_feminine?(currency)
|
|
38
|
+
FEMININE_MAJOR_CURRENCIES.include?(currency)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def currency_minor_feminine?(currency)
|
|
42
|
+
FEMININE_MINOR_CURRENCIES.include?(currency)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def minus_word
|
|
46
|
+
GRAMMAR[:minus]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def fraction_joiner(joiner)
|
|
50
|
+
joiner.to_sym == :and ? "in" : GRAMMAR[:conjunction]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def default_fraction_word
|
|
54
|
+
GRAMMAR[:default_fraction]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def decimal_separator_word
|
|
58
|
+
"vejica"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def decimal_fraction_words(fraction_string)
|
|
62
|
+
fraction_string.chars.map { |digit| cardinal(digit.to_i) }.join(" ")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def time_unit_feminine?(unit)
|
|
66
|
+
%i[hour minute second].include?(unit)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def time_number_words(number, unit:)
|
|
70
|
+
cardinal(number, feminine: time_unit_feminine?(unit))
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def currency_number_words(number, currency, unit:)
|
|
74
|
+
feminine = unit == :major ? currency_major_feminine?(currency) : currency_minor_feminine?(currency)
|
|
75
|
+
cardinal(number, feminine: feminine)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def integer_to_words(number, feminine: false)
|
|
79
|
+
cardinal(number, feminine: feminine)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def triple_to_words(number, scale_idx, feminine: false)
|
|
83
|
+
return [] if number.zero?
|
|
84
|
+
|
|
85
|
+
words = [under_thousand(number, feminine: feminine)]
|
|
86
|
+
words << pluralize(number, *SCALES[scale_idx]) unless scale_idx.zero?
|
|
87
|
+
words
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def date_day(day, format:, date_case:)
|
|
91
|
+
ordinal(day, :default)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def date_year(year, format:)
|
|
95
|
+
cardinal(year)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def ordinal(value, format, gender: :masculine)
|
|
99
|
+
ordinals = ORDINALS[format] || ORDINALS[:default] || ORDINALS[:nominative]
|
|
100
|
+
gender_data = ordinals[gender] || ordinals[:masculine]
|
|
101
|
+
|
|
102
|
+
return gender_data[value - 1] if value.between?(1, gender_data.length)
|
|
103
|
+
|
|
104
|
+
cardinal(value)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def cardinal(number, feminine: false)
|
|
108
|
+
integer_value = Integer(number)
|
|
109
|
+
negative = integer_value.negative?
|
|
110
|
+
integer_value = integer_value.abs
|
|
111
|
+
|
|
112
|
+
return (feminine ? ONES_FEM[0] : ONES_MASC[0]) if integer_value.zero?
|
|
113
|
+
|
|
114
|
+
groups = integer_value.to_s
|
|
115
|
+
.chars.reverse.each_slice(3).map(&:reverse)
|
|
116
|
+
.map(&:join).map!(&:to_i).reverse
|
|
117
|
+
|
|
118
|
+
words = []
|
|
119
|
+
groups.each_with_index do |group_value, index|
|
|
120
|
+
next if group_value.zero?
|
|
121
|
+
|
|
122
|
+
scale_idx = groups.size - index - 1
|
|
123
|
+
group_feminine = feminine_group?(scale_idx) || (feminine && scale_idx.zero?)
|
|
124
|
+
group_words = triple_to_words(group_value, scale_idx, feminine: group_feminine)
|
|
125
|
+
group_words.shift if scale_idx == 1 && group_value == 1
|
|
126
|
+
words.concat(group_words)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
words.unshift(minus_word) if negative
|
|
130
|
+
words.join(" ")
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def under_thousand(number, feminine: false)
|
|
134
|
+
return under_hundred(number, feminine: feminine) if number < 100
|
|
135
|
+
|
|
136
|
+
hundreds = number / 100
|
|
137
|
+
rest = number % 100
|
|
138
|
+
words = [HUNDREDS[hundreds]]
|
|
139
|
+
words << under_hundred(rest, feminine: feminine) if rest.positive?
|
|
140
|
+
words.join(" ")
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def under_hundred(number, feminine: false)
|
|
144
|
+
return one_word(number, feminine: feminine) if number < 10
|
|
145
|
+
return TEENS[number - 10] if number < 20
|
|
146
|
+
return TENS[number / 10] if (number % 10).zero?
|
|
147
|
+
|
|
148
|
+
ones = number % 10
|
|
149
|
+
tens = number / 10
|
|
150
|
+
"#{compound_one(ones)}in#{TENS[tens]}"
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def one_word(number, feminine:)
|
|
154
|
+
feminine ? ONES_FEM[number] : ONES_MASC[number]
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def compound_one(number)
|
|
158
|
+
number == 1 ? "ena" : ONES_MASC[number]
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def pluralize(number, singular, few, plural)
|
|
162
|
+
number = number.abs
|
|
163
|
+
return plural if (11..14).include?(number % 100)
|
|
164
|
+
|
|
165
|
+
number == 1 ? singular : (number == 2 ? few : plural)
|
|
166
|
+
end
|
|
15
167
|
end
|
|
16
168
|
|
|
17
169
|
register :sl, SL
|
data/lib/num2words/locales/sr.rb
CHANGED
|
@@ -10,10 +10,148 @@ module Num2words
|
|
|
10
10
|
HUNDREDS = I18n.t("num2words.hundreds", locale: :sr)
|
|
11
11
|
SCALES = I18n.t("num2words.scales", locale: :sr)
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
FRACTIONS = I18n.t("num2words.fractions", locale: :sr)
|
|
14
|
+
GRAMMAR = I18n.t("num2words.grammar", locale: :sr)
|
|
15
|
+
|
|
16
|
+
DATE = I18n.t("num2words.date", locale: :sr)
|
|
17
|
+
DATE_TEMPLATE = I18n.t("num2words.date.template", locale: :sr)
|
|
18
|
+
TIME = I18n.t("num2words.time", locale: :sr)
|
|
19
|
+
TIME_TEMPLATE = I18n.t("num2words.time.template", locale: :sr)
|
|
20
|
+
DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :sr)
|
|
21
|
+
|
|
22
|
+
ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :sr)
|
|
23
|
+
|
|
24
|
+
FEMININE_MAJOR_CURRENCIES = %i[GBP RUB TRY UAH CZK SEK NOK DKK BYN INR IDR BDT PKR HUF].freeze
|
|
25
|
+
FEMININE_MINOR_CURRENCIES = %i[RUB UAH BGN ILS SAR RSD BDT].freeze
|
|
26
|
+
|
|
27
|
+
module_function
|
|
28
|
+
|
|
29
|
+
def feminine_group?(scale_idx)
|
|
30
|
+
scale_idx == 1 || scale_idx == 3
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def fraction_numerator_feminine?
|
|
34
|
+
true
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def currency_major_feminine?(currency)
|
|
38
|
+
FEMININE_MAJOR_CURRENCIES.include?(currency)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def currency_minor_feminine?(currency)
|
|
42
|
+
FEMININE_MINOR_CURRENCIES.include?(currency)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def minus_word
|
|
46
|
+
GRAMMAR[:minus]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def fraction_joiner(joiner)
|
|
50
|
+
joiner.to_sym == :and ? "и" : GRAMMAR[:conjunction]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def default_fraction_word
|
|
54
|
+
GRAMMAR[:default_fraction]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def decimal_separator_word
|
|
58
|
+
"зарез"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def decimal_fraction_words(fraction_string)
|
|
62
|
+
fraction_string.chars.map { |digit| cardinal(digit.to_i) }.join(" ")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def time_unit_feminine?(unit)
|
|
66
|
+
unit == :second
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def time_number_words(number, unit:)
|
|
70
|
+
cardinal(number, feminine: time_unit_feminine?(unit))
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def currency_number_words(number, currency, unit:)
|
|
74
|
+
feminine = unit == :major ? currency_major_feminine?(currency) : currency_minor_feminine?(currency)
|
|
75
|
+
cardinal(number, feminine: feminine)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def integer_to_words(number, feminine: false)
|
|
79
|
+
cardinal(number, feminine: feminine)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def triple_to_words(number, scale_idx, feminine: false)
|
|
83
|
+
return [] if number.zero?
|
|
84
|
+
|
|
85
|
+
words = []
|
|
86
|
+
words << HUNDREDS[number / 100] if number >= 100
|
|
87
|
+
rest = number % 100
|
|
88
|
+
|
|
89
|
+
if rest.between?(10, 19)
|
|
90
|
+
words << TEENS[rest - 10]
|
|
91
|
+
else
|
|
92
|
+
words << TENS[rest / 10] if rest >= 20
|
|
93
|
+
ones = rest % 10
|
|
94
|
+
words << (feminine ? ONES_FEM[ones] : ONES_MASC[ones]) if ones.positive?
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
words << pluralize(number, *SCALES[scale_idx]) unless scale_idx.zero?
|
|
98
|
+
words.compact
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def date_day(day, format:, date_case:)
|
|
102
|
+
ordinal(day, :default)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def date_year(year, format:)
|
|
106
|
+
cardinal(year)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def ordinal(value, format, gender: :masculine)
|
|
110
|
+
ordinals = ORDINALS[format] || ORDINALS[:default] || ORDINALS[:nominative]
|
|
111
|
+
gender_data = ordinals[gender] || ordinals[:masculine]
|
|
112
|
+
|
|
113
|
+
return gender_data[value - 1] if value.between?(1, gender_data.length)
|
|
114
|
+
|
|
115
|
+
cardinal(value)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def cardinal(number, feminine: false)
|
|
119
|
+
integer_value = Integer(number)
|
|
120
|
+
negative = integer_value.negative?
|
|
121
|
+
integer_value = integer_value.abs
|
|
122
|
+
|
|
123
|
+
return (feminine ? ONES_FEM[0] : ONES_MASC[0]) if integer_value.zero?
|
|
124
|
+
|
|
125
|
+
groups = integer_value.to_s
|
|
126
|
+
.chars.reverse.each_slice(3).map(&:reverse)
|
|
127
|
+
.map(&:join).map!(&:to_i).reverse
|
|
128
|
+
|
|
129
|
+
words = []
|
|
130
|
+
groups.each_with_index do |group_value, index|
|
|
131
|
+
next if group_value.zero?
|
|
132
|
+
|
|
133
|
+
scale_idx = groups.size - index - 1
|
|
134
|
+
group_feminine = feminine_group?(scale_idx) || (feminine && scale_idx.zero?)
|
|
135
|
+
group_words = triple_to_words(group_value, scale_idx, feminine: group_feminine)
|
|
136
|
+
words.concat(group_words)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
words.unshift(minus_word) if negative
|
|
140
|
+
words.join(" ")
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def pluralize(number, singular, few, plural)
|
|
144
|
+
number = number.abs
|
|
145
|
+
return plural if (11..14).include?(number % 100)
|
|
146
|
+
|
|
147
|
+
case number % 10
|
|
148
|
+
when 1 then singular
|
|
149
|
+
when 2..4 then few
|
|
150
|
+
else plural
|
|
151
|
+
end
|
|
152
|
+
end
|
|
15
153
|
end
|
|
16
154
|
|
|
17
|
-
register :sr,
|
|
155
|
+
register :sr, SR
|
|
18
156
|
end
|
|
19
157
|
end
|
data/lib/num2words/locales/sv.rb
CHANGED
|
@@ -10,8 +10,147 @@ module Num2words
|
|
|
10
10
|
HUNDREDS = I18n.t("num2words.hundreds", locale: :sv)
|
|
11
11
|
SCALES = I18n.t("num2words.scales", locale: :sv)
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
FRACTIONS = I18n.t("num2words.fractions", locale: :sv)
|
|
14
|
+
GRAMMAR = I18n.t("num2words.grammar", locale: :sv)
|
|
15
|
+
|
|
16
|
+
DATE = I18n.t("num2words.date", locale: :sv)
|
|
17
|
+
DATE_TEMPLATE = I18n.t("num2words.date.template", locale: :sv)
|
|
18
|
+
TIME = I18n.t("num2words.time", locale: :sv)
|
|
19
|
+
TIME_TEMPLATE = I18n.t("num2words.time.template", locale: :sv)
|
|
20
|
+
DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :sv)
|
|
21
|
+
|
|
22
|
+
ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :sv)
|
|
23
|
+
|
|
24
|
+
COMMON_GENDER_MAJOR_CURRENCIES = %i[
|
|
25
|
+
BDT BGN BRL BYN CNY CZK DKK EUR HUF IDR ILS INR IRR JPY KES KRW
|
|
26
|
+
KZT MYR NOK PKR PLN RON RSD RUB SAR SEK THB TRY UAH USD VND
|
|
27
|
+
].freeze
|
|
28
|
+
|
|
29
|
+
module_function
|
|
30
|
+
|
|
31
|
+
def minus_word
|
|
32
|
+
GRAMMAR[:minus]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def fraction_joiner(joiner)
|
|
36
|
+
joiner.to_sym == :and ? "och" : GRAMMAR[:conjunction]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def default_fraction_word
|
|
40
|
+
GRAMMAR[:default_fraction]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def fraction_numerator_feminine?
|
|
44
|
+
false
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def decimal_separator_word
|
|
48
|
+
"komma"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def decimal_fraction_words(fraction_string)
|
|
52
|
+
fraction_string.chars.map { |digit| cardinal(digit.to_i) }.join(" ")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def currency_major_feminine?(currency)
|
|
56
|
+
COMMON_GENDER_MAJOR_CURRENCIES.include?(currency)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def currency_minor_feminine?(_currency)
|
|
60
|
+
false
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def currency_number_words(number, currency, unit:)
|
|
64
|
+
feminine = unit == :major && currency_major_feminine?(currency)
|
|
65
|
+
cardinal(number, feminine: feminine)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def integer_to_words(number, feminine: false)
|
|
69
|
+
cardinal(number, feminine: feminine)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def triple_to_words(number, scale_idx, feminine: false)
|
|
73
|
+
return [] if number.zero?
|
|
74
|
+
|
|
75
|
+
words = []
|
|
76
|
+
words << scale_number_words(number, scale_idx, feminine: feminine)
|
|
77
|
+
words << pluralize(number, *SCALES[scale_idx]) unless scale_idx.zero?
|
|
78
|
+
words
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def date_day(day, format:, date_case:)
|
|
82
|
+
ordinal(day, :nominative)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def date_year(year, format:)
|
|
86
|
+
cardinal(year)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def ordinal(value, format, gender: :masculine)
|
|
90
|
+
ordinals = ORDINALS[format] || ORDINALS[:nominative]
|
|
91
|
+
gender_data = ordinals[gender] || ordinals[:masculine]
|
|
92
|
+
|
|
93
|
+
return gender_data[value - 1] if value.between?(1, gender_data.length)
|
|
94
|
+
|
|
95
|
+
cardinal(value)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def cardinal(number, feminine: false)
|
|
99
|
+
integer_value = Integer(number)
|
|
100
|
+
negative = integer_value.negative?
|
|
101
|
+
integer_value = integer_value.abs
|
|
102
|
+
|
|
103
|
+
return (feminine ? ONES_FEM[0] : ONES_MASC[0]) if integer_value.zero?
|
|
104
|
+
|
|
105
|
+
groups = integer_value.to_s
|
|
106
|
+
.chars.reverse.each_slice(3).map(&:reverse)
|
|
107
|
+
.map(&:join).map!(&:to_i).reverse
|
|
108
|
+
|
|
109
|
+
words = []
|
|
110
|
+
groups.each_with_index do |group_value, index|
|
|
111
|
+
next if group_value.zero?
|
|
112
|
+
|
|
113
|
+
scale_idx = groups.size - index - 1
|
|
114
|
+
group_words = triple_to_words(group_value, scale_idx, feminine: feminine && scale_idx.zero?)
|
|
115
|
+
words.concat(group_words)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
words.unshift(minus_word) if negative
|
|
119
|
+
words.join(" ")
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def scale_number_words(number, scale_idx, feminine:)
|
|
123
|
+
return "ett" if scale_idx == 1 && number == 1
|
|
124
|
+
return "en" if scale_idx > 1 && number == 1
|
|
125
|
+
|
|
126
|
+
under_thousand(number, feminine: feminine)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def under_thousand(number, feminine: false)
|
|
130
|
+
return under_hundred(number, feminine: feminine) if number < 100
|
|
131
|
+
|
|
132
|
+
hundreds = number / 100
|
|
133
|
+
rest = number % 100
|
|
134
|
+
words = [HUNDREDS[hundreds]]
|
|
135
|
+
words << under_hundred(rest, feminine: feminine) if rest.positive?
|
|
136
|
+
words.join(" ")
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def under_hundred(number, feminine: false)
|
|
140
|
+
return one_word(number, feminine: feminine) if number < 10
|
|
141
|
+
return TEENS[number - 10] if number < 20
|
|
142
|
+
return TENS[number / 10] if (number % 10).zero?
|
|
143
|
+
|
|
144
|
+
"#{TENS[number / 10]}#{one_word(number % 10, feminine: feminine)}"
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def one_word(number, feminine:)
|
|
148
|
+
feminine ? ONES_FEM[number] : ONES_MASC[number]
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def pluralize(number, singular, _few, plural)
|
|
152
|
+
number.abs == 1 ? singular : plural
|
|
153
|
+
end
|
|
15
154
|
end
|
|
16
155
|
|
|
17
156
|
register :sv, SV
|