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,191 @@ module Num2words
20
20
  DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :es)
21
21
 
22
22
  ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :es)
23
+
24
+ HUNDREDS_FEM = ["", "cien", "doscientas", "trescientas", "cuatrocientas", "quinientas", "seiscientas", "setecientas", "ochocientas", "novecientas"].freeze
25
+ VEINTI = {
26
+ 21 => { masculine: "veintiuno", feminine: "veintiuna", apocopated: "veintiún" },
27
+ 22 => "veintidós",
28
+ 23 => "veintitrés",
29
+ 24 => "veinticuatro",
30
+ 25 => "veinticinco",
31
+ 26 => "veintiséis",
32
+ 27 => "veintisiete",
33
+ 28 => "veintiocho",
34
+ 29 => "veintinueve"
35
+ }.freeze
36
+ FEMININE_MAJOR_CURRENCIES = %i[GBP TRY UAH CZK SEK NOK DKK INR IDR PKR].freeze
37
+ FEMININE_MINOR_CURRENCIES = %i[BGN ILS SAR].freeze
38
+
39
+ module_function
40
+
41
+ def integer_to_words(number, feminine: false)
42
+ cardinal(number, gender: feminine ? :feminine : :masculine)
43
+ end
44
+
45
+ def triple_to_words(number, scale_idx, feminine: false)
46
+ return [] if number.zero?
47
+
48
+ gender = feminine ? :feminine : :masculine
49
+ words = []
50
+
51
+ case scale_idx
52
+ when 0
53
+ words << under_thousand(number, gender: gender)
54
+ when 1
55
+ words << under_thousand(number, gender: :masculine, apocopate: true) unless number == 1
56
+ words << SCALES[1][0]
57
+ when 2, 4
58
+ words << (number == 1 ? "un" : under_thousand(number, gender: :masculine, apocopate: true))
59
+ words << pluralize(number, *SCALES[scale_idx])
60
+ when 3
61
+ words << under_thousand(number, gender: :masculine, apocopate: true) unless number == 1
62
+ words << SCALES[3][0]
63
+ else
64
+ words << under_thousand(number, gender: :masculine, apocopate: true)
65
+ words << pluralize(number, *SCALES[scale_idx])
66
+ end
67
+
68
+ words
69
+ end
70
+
71
+ def minus_word
72
+ GRAMMAR[:minus]
73
+ end
74
+
75
+ def fraction_joiner(joiner)
76
+ joiner.to_sym == :and ? "y" : GRAMMAR[:conjunction]
77
+ end
78
+
79
+ def default_fraction_word
80
+ GRAMMAR[:default_fraction]
81
+ end
82
+
83
+ def fraction_numerator_feminine?
84
+ true
85
+ end
86
+
87
+ def decimal_separator_word
88
+ "coma"
89
+ end
90
+
91
+ def decimal_fraction_words(fraction_string)
92
+ fraction_string.chars.map { |digit| cardinal(digit.to_i) }.join(" ")
93
+ end
94
+
95
+ def time_unit_feminine?(unit)
96
+ unit == :hour
97
+ end
98
+
99
+ def time_number_words(number, unit:)
100
+ gender = unit == :hour ? :feminine : :masculine
101
+
102
+ cardinal(number, gender: gender, apocopate: gender == :masculine)
103
+ end
104
+
105
+ def currency_number_words(number, currency, unit:)
106
+ gender = if unit == :major
107
+ FEMININE_MAJOR_CURRENCIES.include?(currency) ? :feminine : :masculine
108
+ else
109
+ FEMININE_MINOR_CURRENCIES.include?(currency) ? :feminine : :masculine
110
+ end
111
+
112
+ cardinal(number, gender: gender, apocopate: gender == :masculine)
113
+ end
114
+
115
+ def date_day(day, format:, date_case:)
116
+ day == 1 ? "primero" : cardinal(day)
117
+ end
118
+
119
+ def date_year(year, format:)
120
+ cardinal(year)
121
+ end
122
+
123
+ def ordinal(value, format, gender: :masculine)
124
+ ordinals = ORDINALS[format] || ORDINALS[:nominative]
125
+ gender_data = ordinals[gender] || ordinals[:masculine]
126
+
127
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
128
+
129
+ cardinal(value, gender: gender)
130
+ end
131
+
132
+ def cardinal(number, gender: :masculine, apocopate: false)
133
+ integer_value = Integer(number)
134
+ negative = integer_value.negative?
135
+ integer_value = integer_value.abs
136
+
137
+ return ONES_MASC[0] if integer_value.zero?
138
+
139
+ groups = integer_value.to_s
140
+ .chars.reverse.each_slice(3).map(&:reverse)
141
+ .map(&:join).map!(&:to_i).reverse
142
+
143
+ words = []
144
+ groups.each_with_index do |group_value, index|
145
+ next if group_value.zero?
146
+
147
+ scale_idx = groups.size - index - 1
148
+ words.concat triple_to_words(group_value, scale_idx, feminine: gender == :feminine)
149
+ end
150
+
151
+ words.unshift(minus_word) if negative
152
+ result = words.join(" ")
153
+ apocopate ? apocopate_one(result) : result
154
+ end
155
+
156
+ def under_thousand(number, gender: :masculine, apocopate: false)
157
+ return under_hundred(number, gender: gender, apocopate: apocopate) if number < 100
158
+ return "cien" if number == 100
159
+
160
+ hundreds = number / 100
161
+ rest = number % 100
162
+ hundreds_words = hundreds == 1 ? "ciento" : hundreds_data(gender)[hundreds]
163
+
164
+ return hundreds_words if rest.zero?
165
+
166
+ [hundreds_words, under_hundred(rest, gender: gender, apocopate: apocopate)].join(" ")
167
+ end
168
+
169
+ def under_hundred(number, gender: :masculine, apocopate: false)
170
+ return one_word(number, gender: gender, apocopate: apocopate) if number < 10
171
+ return TEENS[number - 10] if number < 20
172
+ return TENS[2] if number == 20
173
+
174
+ if number < 30
175
+ value = VEINTI[number]
176
+ return value unless value.is_a?(Hash)
177
+
178
+ return value[:apocopated] if apocopate
179
+
180
+ value[gender] || value[:masculine]
181
+ else
182
+ tens = number / 10
183
+ ones = number % 10
184
+ return TENS[tens] if ones.zero?
185
+
186
+ [TENS[tens], one_word(ones, gender: gender, apocopate: apocopate)].join(" y ")
187
+ end
188
+ end
189
+
190
+ def one_word(number, gender:, apocopate:)
191
+ return "un" if number == 1 && apocopate
192
+ return ONES_FEM[number] if gender == :feminine
193
+
194
+ ONES_MASC[number]
195
+ end
196
+
197
+ def apocopate_one(words)
198
+ words.sub(/veintiuno\z/, "veintiún").sub(/ y uno\z/, " y un").sub(/uno\z/, "un")
199
+ end
200
+
201
+ def hundreds_data(gender)
202
+ gender == :feminine ? HUNDREDS_FEM : HUNDREDS
203
+ end
204
+
205
+ def pluralize(number, singular, _few, plural)
206
+ number.abs == 1 ? singular : plural
207
+ end
23
208
  end
24
209
 
25
210
  register :es, ES
@@ -20,6 +20,110 @@ module Num2words
20
20
  DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :et)
21
21
 
22
22
  ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :et)
23
+
24
+ module_function
25
+
26
+ def integer_to_words(number, feminine: false)
27
+ cardinal(number)
28
+ end
29
+
30
+ def triple_to_words(number, scale_idx, feminine: false)
31
+ return [] if number.zero?
32
+
33
+ words = []
34
+ words << under_thousand(number) unless scale_idx == 1 && number == 1
35
+ words << pluralize(number, *SCALES[scale_idx]) unless scale_idx.zero?
36
+ words
37
+ end
38
+
39
+ def minus_word
40
+ GRAMMAR[:minus]
41
+ end
42
+
43
+ def fraction_joiner(_joiner)
44
+ GRAMMAR[:conjunction]
45
+ end
46
+
47
+ def default_fraction_word
48
+ GRAMMAR[:default_fraction]
49
+ end
50
+
51
+ def fraction_numerator_feminine?
52
+ false
53
+ end
54
+
55
+ def decimal_separator_word
56
+ "koma"
57
+ end
58
+
59
+ def decimal_fraction_words(fraction_string)
60
+ fraction_string.chars.map { |digit| cardinal(digit.to_i) }.join(" ")
61
+ end
62
+
63
+ def date_day(day, format:, date_case:)
64
+ ordinal(day, :default)
65
+ end
66
+
67
+ def date_year(year, format:)
68
+ cardinal(year)
69
+ end
70
+
71
+ def ordinal(value, format, gender: :masculine)
72
+ ordinals = ORDINALS[format] || ORDINALS[:default] || ORDINALS[:nominative]
73
+ gender_data = ordinals[gender] || ordinals[:masculine]
74
+
75
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
76
+
77
+ cardinal(value)
78
+ end
79
+
80
+ def cardinal(number)
81
+ integer_value = Integer(number)
82
+ negative = integer_value.negative?
83
+ integer_value = integer_value.abs
84
+
85
+ return ONES_MASC[0] if integer_value.zero?
86
+
87
+ groups = integer_value.to_s
88
+ .chars.reverse.each_slice(3).map(&:reverse)
89
+ .map(&:join).map!(&:to_i).reverse
90
+
91
+ words = []
92
+ groups.each_with_index do |group_value, index|
93
+ next if group_value.zero?
94
+
95
+ scale_idx = groups.size - index - 1
96
+ words.concat triple_to_words(group_value, scale_idx)
97
+ end
98
+
99
+ words.unshift(minus_word) if negative
100
+ words.join(" ")
101
+ end
102
+
103
+ def under_thousand(number)
104
+ return under_hundred(number) if number < 100
105
+
106
+ hundreds = number / 100
107
+ rest = number % 100
108
+ words = [HUNDREDS[hundreds]]
109
+ words << under_hundred(rest) if rest.positive?
110
+ words.join(" ")
111
+ end
112
+
113
+ def under_hundred(number)
114
+ return ONES_MASC[number] if number < 10
115
+ return TEENS[number - 10] if number < 20
116
+
117
+ tens = number / 10
118
+ ones = number % 10
119
+ words = [TENS[tens]]
120
+ words << ONES_MASC[ones] if ones.positive?
121
+ words.join(" ")
122
+ end
123
+
124
+ def pluralize(number, singular, _few, plural)
125
+ number.abs == 1 ? singular : plural
126
+ end
23
127
  end
24
128
 
25
129
  register :et, ET
@@ -20,6 +20,110 @@ module Num2words
20
20
  DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :fa)
21
21
 
22
22
  ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :fa)
23
+
24
+ module_function
25
+
26
+ def integer_to_words(number, feminine: false)
27
+ cardinal(number)
28
+ end
29
+
30
+ def triple_to_words(number, scale_idx, feminine: false)
31
+ return [] if number.zero?
32
+
33
+ words = []
34
+ words << under_thousand(number) unless scale_idx == 1 && number == 1
35
+ words << SCALES[scale_idx][0] unless scale_idx.zero?
36
+ words
37
+ end
38
+
39
+ def minus_word
40
+ GRAMMAR[:minus]
41
+ end
42
+
43
+ def fraction_joiner(_joiner)
44
+ GRAMMAR[:conjunction]
45
+ end
46
+
47
+ def default_fraction_word
48
+ GRAMMAR[:default_fraction]
49
+ end
50
+
51
+ def fraction_numerator_feminine?
52
+ false
53
+ end
54
+
55
+ def decimal_separator_word
56
+ "ممیز"
57
+ end
58
+
59
+ def decimal_fraction_words(fraction_string)
60
+ fraction_string.chars.map { |digit| cardinal(digit.to_i) }.join(" ")
61
+ end
62
+
63
+ def date_day(day, format:, date_case:)
64
+ ordinal(day, :default)
65
+ end
66
+
67
+ def date_year(year, format:)
68
+ cardinal(year)
69
+ end
70
+
71
+ def ordinal(value, format, gender: :masculine)
72
+ ordinals = ORDINALS[format] || ORDINALS[:default] || ORDINALS[:nominative]
73
+ gender_data = ordinals[gender] || ordinals[:masculine]
74
+
75
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
76
+
77
+ cardinal(value)
78
+ end
79
+
80
+ def cardinal(number)
81
+ integer_value = Integer(number)
82
+ negative = integer_value.negative?
83
+ integer_value = integer_value.abs
84
+
85
+ return ONES_MASC[0] if integer_value.zero?
86
+
87
+ groups = integer_value.to_s
88
+ .chars.reverse.each_slice(3).map(&:reverse)
89
+ .map(&:join).map!(&:to_i).reverse
90
+
91
+ words = []
92
+ groups.each_with_index do |group_value, index|
93
+ next if group_value.zero?
94
+
95
+ scale_idx = groups.size - index - 1
96
+ words.concat triple_to_words(group_value, scale_idx)
97
+ end
98
+
99
+ words.unshift(minus_word) if negative
100
+ words.join(" ")
101
+ end
102
+
103
+ def under_thousand(number)
104
+ return under_hundred(number) if number < 100
105
+
106
+ hundreds = number / 100
107
+ rest = number % 100
108
+ words = [HUNDREDS[hundreds]]
109
+ words << under_hundred(rest) if rest.positive?
110
+ words.join(" و ")
111
+ end
112
+
113
+ def under_hundred(number)
114
+ return ONES_MASC[number] if number < 10
115
+ return TEENS[number - 10] if number < 20
116
+
117
+ tens = number / 10
118
+ ones = number % 10
119
+ words = [TENS[tens]]
120
+ words << ONES_MASC[ones] if ones.positive?
121
+ words.join(" و ")
122
+ end
123
+
124
+ def pluralize(number, singular, few, _plural)
125
+ number.abs == 1 ? singular : few
126
+ end
23
127
  end
24
128
 
25
129
  register :fa, FA
@@ -20,6 +20,110 @@ module Num2words
20
20
  DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :fi)
21
21
 
22
22
  ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :fi)
23
+
24
+ module_function
25
+
26
+ def integer_to_words(number, feminine: false)
27
+ cardinal(number)
28
+ end
29
+
30
+ def triple_to_words(number, scale_idx, feminine: false)
31
+ return [] if number.zero?
32
+
33
+ words = []
34
+ words << under_thousand(number) unless scale_idx == 1 && number == 1
35
+ words << pluralize(number, *SCALES[scale_idx]) unless scale_idx.zero?
36
+ words
37
+ end
38
+
39
+ def minus_word
40
+ GRAMMAR[:minus]
41
+ end
42
+
43
+ def fraction_joiner(joiner)
44
+ joiner.to_sym == :and ? "ja" : GRAMMAR[:conjunction]
45
+ end
46
+
47
+ def default_fraction_word
48
+ GRAMMAR[:default_fraction]
49
+ end
50
+
51
+ def fraction_numerator_feminine?
52
+ false
53
+ end
54
+
55
+ def decimal_separator_word
56
+ "pilkku"
57
+ end
58
+
59
+ def decimal_fraction_words(fraction_string)
60
+ fraction_string.chars.map { |digit| cardinal(digit.to_i) }.join(" ")
61
+ end
62
+
63
+ def date_day(day, format:, date_case:)
64
+ ordinal(day, :default)
65
+ end
66
+
67
+ def date_year(year, format:)
68
+ cardinal(year)
69
+ end
70
+
71
+ def ordinal(value, format, gender: :masculine)
72
+ ordinals = ORDINALS[format] || ORDINALS[:default] || ORDINALS[:nominative]
73
+ gender_data = ordinals[gender] || ordinals[:masculine]
74
+
75
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
76
+
77
+ cardinal(value)
78
+ end
79
+
80
+ def cardinal(number)
81
+ integer_value = Integer(number)
82
+ negative = integer_value.negative?
83
+ integer_value = integer_value.abs
84
+
85
+ return ONES_MASC[0] if integer_value.zero?
86
+
87
+ groups = integer_value.to_s
88
+ .chars.reverse.each_slice(3).map(&:reverse)
89
+ .map(&:join).map!(&:to_i).reverse
90
+
91
+ words = []
92
+ groups.each_with_index do |group_value, index|
93
+ next if group_value.zero?
94
+
95
+ scale_idx = groups.size - index - 1
96
+ words.concat triple_to_words(group_value, scale_idx)
97
+ end
98
+
99
+ words.unshift(minus_word) if negative
100
+ words.join(" ")
101
+ end
102
+
103
+ def under_thousand(number)
104
+ return under_hundred(number) if number < 100
105
+
106
+ hundreds = number / 100
107
+ rest = number % 100
108
+ words = [HUNDREDS[hundreds]]
109
+ words << under_hundred(rest) if rest.positive?
110
+ words.join
111
+ end
112
+
113
+ def under_hundred(number)
114
+ return ONES_MASC[number] if number < 10
115
+ return TEENS[number - 10] if number < 20
116
+
117
+ tens = number / 10
118
+ ones = number % 10
119
+ words = [TENS[tens]]
120
+ words << ONES_MASC[ones] if ones.positive?
121
+ words.join
122
+ end
123
+
124
+ def pluralize(number, singular, few, _plural)
125
+ number.abs == 1 ? singular : few
126
+ end
23
127
  end
24
128
 
25
129
  register :fi, FI
@@ -20,6 +20,127 @@ module Num2words
20
20
  DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :fr)
21
21
 
22
22
  ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :fr)
23
+
24
+ module_function
25
+
26
+ def minus_word
27
+ GRAMMAR[:minus]
28
+ end
29
+
30
+ def fraction_joiner(_joiner)
31
+ GRAMMAR[:conjunction]
32
+ end
33
+
34
+ def default_fraction_word
35
+ GRAMMAR[:default_fraction]
36
+ end
37
+
38
+ def fraction_numerator_feminine?
39
+ false
40
+ end
41
+
42
+ def decimal_separator_word
43
+ "virgule"
44
+ end
45
+
46
+ def decimal_fraction_words(fraction_string)
47
+ fraction_string.chars.map { |digit| under_thousand(digit.to_i) }.join(" ")
48
+ end
49
+
50
+ def time_unit_feminine?(unit)
51
+ unit == :hour || unit == :minute || unit == :second
52
+ end
53
+
54
+ def currency_major_feminine?(currency)
55
+ %i[GBP TRY UAH CZK SEK NOK DKK INR IDR PKR].include?(currency)
56
+ end
57
+
58
+ def currency_minor_feminine?(currency)
59
+ %i[ILS BGN SAR].include?(currency)
60
+ end
61
+
62
+ def triple_to_words(number, scale_idx, feminine: false)
63
+ words = scale_idx == 1 && number == 1 ? [] : under_thousand(number, feminine: feminine).split
64
+
65
+ unless scale_idx.zero?
66
+ scale_forms = SCALES[scale_idx]
67
+ words << (number == 1 ? scale_forms[0] : scale_forms[2])
68
+ end
69
+
70
+ words
71
+ end
72
+
73
+ def date_day(day, format:, date_case:)
74
+ return "premier" if day == 1
75
+
76
+ under_thousand(day)
77
+ end
78
+
79
+ def date_year(year, format:)
80
+ groups = year.to_s
81
+ .chars.reverse.each_slice(3).map(&:reverse)
82
+ .map(&:join).map!(&:to_i).reverse
83
+
84
+ words = []
85
+ groups.each_with_index do |group_value, index|
86
+ scale_index = groups.size - index - 1
87
+ words.concat triple_to_words(group_value, scale_index)
88
+ end
89
+
90
+ words.join(" ")
91
+ end
92
+
93
+ def under_thousand(number, feminine: false)
94
+ hundreds = number / 100
95
+ rest = number % 100
96
+
97
+ return under_hundred(rest, feminine: feminine) if hundreds.zero?
98
+
99
+ hundred_words = if hundreds == 1
100
+ HUNDREDS[1]
101
+ elsif rest.zero?
102
+ HUNDREDS[hundreds]
103
+ else
104
+ HUNDREDS[hundreds].sub(/s\z/, "")
105
+ end
106
+
107
+ return hundred_words if rest.zero?
108
+
109
+ [hundred_words, under_hundred(rest, feminine: feminine)].join(" ")
110
+ end
111
+
112
+ def under_hundred(number, feminine: false)
113
+ ones_data = feminine ? ONES_FEM : ONES_MASC
114
+
115
+ return ones_data[number] if number < 10
116
+ return TEENS[number - 10] if number < 20
117
+
118
+ case number
119
+ when 20..69
120
+ tens = number / 10
121
+ ones = number % 10
122
+ return TENS[tens] if ones.zero?
123
+ return "#{TENS[tens]} et #{ones_data[ones]}" if ones == 1
124
+
125
+ "#{TENS[tens]} #{ones_data[ones]}"
126
+ when 70..79
127
+ teen = number - 60
128
+ return "soixante et #{TEENS[1]}" if teen == 11
129
+
130
+ "soixante #{under_hundred(teen, feminine: feminine)}"
131
+ when 80
132
+ "quatre-vingts"
133
+ else
134
+ rest = number - 80
135
+ return "quatre-vingt" if rest.zero?
136
+
137
+ "quatre-vingt #{under_hundred(rest, feminine: feminine)}"
138
+ end
139
+ end
140
+
141
+ def pluralize(number, singular, _few, plural)
142
+ number.abs == 1 ? singular : plural
143
+ end
23
144
  end
24
145
 
25
146
  register :fr, FR