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
@@ -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
@@ -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
- MAJOR_UNIT = I18n.t("num2words.currencies.EUR.name", locale: :sl)
14
- MINOR_UNIT = I18n.t("num2words.currencies.EUR.fractional.name", locale: :sl)
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
@@ -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
- MAJOR_UNIT = I18n.t("num2words.currencies.RSD.name", locale: :sr)
14
- MINOR_UNIT = I18n.t("num2words.currencies.RSD.fractional.name", locale: :sr)
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, Sr
155
+ register :sr, SR
18
156
  end
19
157
  end
@@ -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
- MAJOR_UNIT = I18n.t("num2words.currencies.SEK.name", locale: :sv)
14
- MINOR_UNIT = I18n.t("num2words.currencies.SEK.fractional.name", locale: :sv)
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