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
@@ -10,8 +10,128 @@ module Num2words
10
10
  HUNDREDS = I18n.t("num2words.hundreds", locale: :pa)
11
11
  SCALES = I18n.t("num2words.scales", locale: :pa)
12
12
 
13
- MAJOR_UNIT = I18n.t("num2words.currencies.INR.name", locale: :pa)
14
- MINOR_UNIT = I18n.t("num2words.currencies.INR.fractional.name", locale: :pa)
13
+ FRACTIONS = I18n.t("num2words.fractions", locale: :pa)
14
+ GRAMMAR = I18n.t("num2words.grammar", locale: :pa)
15
+
16
+ DATE = I18n.t("num2words.date", locale: :pa)
17
+ DATE_TEMPLATE = I18n.t("num2words.date.template", locale: :pa)
18
+ TIME = I18n.t("num2words.time", locale: :pa)
19
+ TIME_TEMPLATE = I18n.t("num2words.time.template", locale: :pa)
20
+ DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :pa)
21
+
22
+ ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :pa)
23
+
24
+ UNDER_HUNDRED = [
25
+ "ਜ਼ੀਰੋ", "ਇੱਕ", "ਦੋ", "ਤਿੰਨ", "ਚਾਰ", "ਪੰਜ", "ਛੇ", "ਸੱਤ", "ਅੱਠ", "ਨੌਂ",
26
+ "ਦਸ", "ਗਿਆਰਾਂ", "ਬਾਰਾਂ", "ਤੇਰਾਂ", "ਚੌਦਾਂ", "ਪੰਦਰਾਂ", "ਸੋਲ੍ਹਾਂ", "ਸਤਾਰਾਂ", "ਅਠਾਰਾਂ", "ਉੱਨੀ",
27
+ "ਵੀਹ", "ਇੱਕੀ", "ਬਾਈ", "ਤੇਈ", "ਚੌਵੀ", "ਪੱਚੀ", "ਛੱਬੀ", "ਸਤਾਈ", "ਅਠਾਈ", "ਉਨੱਤੀ",
28
+ "ਤੀਹ", "ਇਕੱਤੀ", "ਬੱਤੀ", "ਤੇਤੀ", "ਚੌਂਤੀ", "ਪੈਂਤੀ", "ਛੱਤੀ", "ਸੈਂਤੀ", "ਅਠੱਤੀ", "ਉਨਤਾਲੀ",
29
+ "ਚਾਲੀ", "ਇਕਤਾਲੀ", "ਬਿਆਲੀ", "ਤਿਰਤਾਲੀ", "ਚੁਤਾਲੀ", "ਪੰਤਾਲੀ", "ਛਿਆਲੀ", "ਸੰਤਾਲੀ", "ਅਠਤਾਲੀ", "ਉਨੰਜਾ",
30
+ "ਪੰਜਾਹ", "ਇਕਵੰਜਾ", "ਬਵੰਜਾ", "ਤਰਵੰਜਾ", "ਚੁਰੰਜਾ", "ਪਚਵੰਜਾ", "ਛਪੰਜਾ", "ਸਤਵੰਜਾ", "ਅਠਵੰਜਾ", "ਉਨਾਹਠ",
31
+ "ਸੱਠ", "ਇਕਾਹਠ", "ਬਾਹਠ", "ਤਰੇਂਹਠ", "ਚੌਂਹਠ", "ਪੈਂਹਠ", "ਛਿਆਹਠ", "ਸਤਾਹਠ", "ਅਠਾਹਠ", "ਉਨੱਤਰ",
32
+ "ਸੱਤਰ", "ਇਕਹੱਤਰ", "ਬਹੱਤਰ", "ਤਿਹੱਤਰ", "ਚੌਹੱਤਰ", "ਪਚਹੱਤਰ", "ਛਿਹੱਤਰ", "ਸਤੱਤਰ", "ਅਠੱਤਰ", "ਉਨਾਸੀ",
33
+ "ਅੱਸੀ", "ਇਕਿਆਸੀ", "ਬਿਆਸੀ", "ਤਿਰਾਸੀ", "ਚੌਰਾਸੀ", "ਪਚਾਸੀ", "ਛਿਆਸੀ", "ਸਤਾਸੀ", "ਅਠਾਸੀ", "ਨਵਾਸੀ",
34
+ "ਨੱਬੇ", "ਇਕਾਨਵੇਂ", "ਬਾਨਵੇਂ", "ਤਰਾਨਵੇਂ", "ਚੁਰਾਨਵੇਂ", "ਪਚਾਨਵੇਂ", "ਛਿਆਨਵੇਂ", "ਸਤਾਨਵੇਂ", "ਅਠਾਨਵੇਂ", "ਨੜਿਨਵੇਂ"
35
+ ].freeze
36
+
37
+ INDIAN_SCALES = [
38
+ [1_000_000_000, "ਅਰਬ"],
39
+ [10_000_000, "ਕਰੋੜ"],
40
+ [100_000, "ਲੱਖ"],
41
+ [1_000, "ਹਜ਼ਾਰ"]
42
+ ].freeze
43
+
44
+ module_function
45
+
46
+ def integer_to_words(number, feminine: false)
47
+ cardinal(number)
48
+ end
49
+
50
+ def triple_to_words(number, scale_idx, feminine: false)
51
+ return [] if number.zero?
52
+
53
+ words = [under_thousand(number)]
54
+ words << SCALES[scale_idx][0] unless scale_idx.zero?
55
+ words
56
+ end
57
+
58
+ def minus_word
59
+ GRAMMAR[:minus]
60
+ end
61
+
62
+ def fraction_joiner(joiner)
63
+ joiner.to_sym == :and ? "ਅਤੇ" : GRAMMAR[:conjunction]
64
+ end
65
+
66
+ def default_fraction_word
67
+ GRAMMAR[:default_fraction]
68
+ end
69
+
70
+ def fraction_numerator_feminine?
71
+ false
72
+ end
73
+
74
+ def decimal_separator_word
75
+ "ਦਸ਼ਮਲਵ"
76
+ end
77
+
78
+ def decimal_fraction_words(fraction_string)
79
+ fraction_string.chars.map { |digit| cardinal(digit.to_i) }.join(" ")
80
+ end
81
+
82
+ def date_day(day, format:, date_case:)
83
+ ordinal(day, :default)
84
+ end
85
+
86
+ def date_year(year, format:)
87
+ cardinal(year)
88
+ end
89
+
90
+ def ordinal(value, format, gender: :masculine)
91
+ ordinals = ORDINALS[format] || ORDINALS[:default] || ORDINALS[:nominative]
92
+ gender_data = ordinals[gender] || ordinals[:masculine]
93
+
94
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
95
+
96
+ cardinal(value)
97
+ end
98
+
99
+ def cardinal(number)
100
+ integer_value = Integer(number)
101
+ negative = integer_value.negative?
102
+ integer_value = integer_value.abs
103
+
104
+ return UNDER_HUNDRED[0] if integer_value.zero?
105
+
106
+ remainder = integer_value
107
+ words = []
108
+
109
+ INDIAN_SCALES.each do |value, label|
110
+ next if remainder < value
111
+
112
+ group_value = remainder / value
113
+ remainder %= value
114
+ words << [cardinal(group_value), label].join(" ")
115
+ end
116
+
117
+ words << under_thousand(remainder) if remainder.positive?
118
+ words.unshift(minus_word) if negative
119
+ words.join(" ")
120
+ end
121
+
122
+ def under_thousand(number)
123
+ return UNDER_HUNDRED[number] if number < 100
124
+
125
+ hundreds = number / 100
126
+ rest = number % 100
127
+ words = [HUNDREDS[hundreds]]
128
+ words << UNDER_HUNDRED[rest] if rest.positive?
129
+ words.join(" ")
130
+ end
131
+
132
+ def pluralize(number, singular, few, _plural)
133
+ number.abs == 1 ? singular : few
134
+ end
15
135
  end
16
136
 
17
137
  register :pa, PA
@@ -10,8 +10,155 @@ module Num2words
10
10
  HUNDREDS = I18n.t("num2words.hundreds", locale: :pl)
11
11
  SCALES = I18n.t("num2words.scales", locale: :pl)
12
12
 
13
- MAJOR_UNIT = I18n.t("num2words.currencies.PLN.name", locale: :pl)
14
- MINOR_UNIT = I18n.t("num2words.currencies.PLN.fractional.name", locale: :pl)
13
+ FRACTIONS = I18n.t("num2words.fractions", locale: :pl)
14
+ GRAMMAR = I18n.t("num2words.grammar", locale: :pl)
15
+
16
+ DATE = I18n.t("num2words.date", locale: :pl)
17
+ DATE_TEMPLATE = I18n.t("num2words.date.template", locale: :pl)
18
+ TIME = I18n.t("num2words.time", locale: :pl)
19
+ TIME_TEMPLATE = I18n.t("num2words.time.template", locale: :pl)
20
+ DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :pl)
21
+
22
+ ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :pl)
23
+
24
+ FEMININE_CURRENCIES = %i[BDT CZK DKK IDR INR NOK PKR SEK TRY UAH].freeze
25
+
26
+ module_function
27
+
28
+ def integer_to_words(number, feminine: false)
29
+ cardinal(number, feminine: feminine)
30
+ end
31
+
32
+ def triple_to_words(number, scale_idx, feminine: false)
33
+ return [] if number.zero?
34
+
35
+ words = []
36
+ words << HUNDREDS[number / 100] if number >= 100
37
+ rest = number % 100
38
+
39
+ if rest.between?(10, 19)
40
+ words << TEENS[rest - 10]
41
+ else
42
+ words << TENS[rest / 10] if rest >= 20
43
+ ones = rest % 10
44
+ words << (feminine ? ONES_FEM[ones] : ONES_MASC[ones]) if ones.positive?
45
+ end
46
+
47
+ words << pluralize(number, *SCALES[scale_idx]) unless scale_idx.zero?
48
+ words.compact
49
+ end
50
+
51
+ def minus_word
52
+ GRAMMAR[:minus]
53
+ end
54
+
55
+ def fraction_joiner(joiner)
56
+ joiner.to_sym == :and ? "i" : GRAMMAR[:conjunction]
57
+ end
58
+
59
+ def default_fraction_word
60
+ GRAMMAR[:default_fraction]
61
+ end
62
+
63
+ def fraction_numerator_feminine?
64
+ true
65
+ end
66
+
67
+ def decimal_separator_word
68
+ "przecinek"
69
+ end
70
+
71
+ def decimal_fraction_words(fraction_string)
72
+ fraction_string.chars.map { |digit| cardinal(digit.to_i) }.join(" ")
73
+ end
74
+
75
+ def date_day(day, format:, date_case:)
76
+ ordinal(day, :default)
77
+ end
78
+
79
+ def date_year(year, format:)
80
+ return ordinal(year, :default) if year <= 31
81
+
82
+ thousands = year / 1000
83
+ rest = year % 1000
84
+ words = [cardinal(thousands), pluralize(thousands, *SCALES[1])]
85
+ words << ordinal(rest, :default) if rest.positive?
86
+ words.join(" ")
87
+ end
88
+
89
+ def time_unit_feminine?(_unit)
90
+ true
91
+ end
92
+
93
+ def time_number_words(number, unit:)
94
+ cardinal(number, feminine: true)
95
+ end
96
+
97
+ def currency_major_feminine?(currency)
98
+ FEMININE_CURRENCIES.include?(currency)
99
+ end
100
+
101
+ def currency_minor_feminine?(currency)
102
+ %i[BGN BYN RUB UAH].include?(currency)
103
+ end
104
+
105
+ def currency_number_words(number, currency, unit:)
106
+ feminine = unit == :major ? currency_major_feminine?(currency) : currency_minor_feminine?(currency)
107
+ cardinal(number, feminine: feminine)
108
+ end
109
+
110
+ def ordinal(value, format, gender: :masculine)
111
+ ordinals = ORDINALS[format] || ORDINALS[:default] || ORDINALS[:nominative]
112
+ gender_data = ordinals[gender] || ordinals[:masculine]
113
+
114
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
115
+
116
+ ordinal_compound(value, gender: gender)
117
+ end
118
+
119
+ def cardinal(number, feminine: false)
120
+ integer_value = Integer(number)
121
+ negative = integer_value.negative?
122
+ integer_value = integer_value.abs
123
+
124
+ return (feminine ? ONES_FEM[0] : ONES_MASC[0]) if integer_value.zero?
125
+
126
+ groups = integer_value.to_s
127
+ .chars.reverse.each_slice(3).map(&:reverse)
128
+ .map(&:join).map!(&:to_i).reverse
129
+
130
+ words = []
131
+ groups.each_with_index do |group_value, index|
132
+ next if group_value.zero?
133
+
134
+ scale_idx = groups.size - index - 1
135
+ group_words = triple_to_words(group_value, scale_idx, feminine: feminine && scale_idx.zero?)
136
+ group_words.shift if scale_idx == 1 && group_value == 1
137
+ words.concat(group_words)
138
+ end
139
+
140
+ words.unshift(minus_word) if negative
141
+ words.join(" ")
142
+ end
143
+
144
+ def ordinal_compound(value, gender:)
145
+ base = value - (value % 100)
146
+ rest = value % 100
147
+ return cardinal(value) if rest.zero?
148
+
149
+ [cardinal(base), ordinal(rest, :default, gender: gender)].join(" ")
150
+ end
151
+
152
+ def pluralize(number, singular, few, plural)
153
+ number = number.abs
154
+ return plural if (11..14).include?(number % 100)
155
+
156
+ case number % 10
157
+ when 1 then number == 1 ? singular : plural
158
+ when 2..4 then few
159
+ else plural
160
+ end
161
+ end
15
162
  end
16
163
 
17
164
  register :pl, PL
@@ -10,8 +10,170 @@ module Num2words
10
10
  HUNDREDS = I18n.t("num2words.hundreds", locale: :pt)
11
11
  SCALES = I18n.t("num2words.scales", locale: :pt)
12
12
 
13
- MAJOR_UNIT = I18n.t("num2words.currencies.EUR.name", locale: :pt)
14
- MINOR_UNIT = I18n.t("num2words.currencies.EUR.fractional.name", locale: :pt)
13
+ FRACTIONS = I18n.t("num2words.fractions", locale: :pt)
14
+ GRAMMAR = I18n.t("num2words.grammar", locale: :pt)
15
+
16
+ DATE = I18n.t("num2words.date", locale: :pt)
17
+ DATE_TEMPLATE = I18n.t("num2words.date.template", locale: :pt)
18
+ TIME = I18n.t("num2words.time", locale: :pt)
19
+ TIME_TEMPLATE = I18n.t("num2words.time.template", locale: :pt)
20
+ DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :pt)
21
+
22
+ ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :pt)
23
+
24
+ HUNDREDS_FEM = ["", "cento", "duzentas", "trezentas", "quatrocentas", "quinhentas", "seiscentas", "setecentas", "oitocentas", "novecentas"].freeze
25
+ FEMININE_MAJOR_CURRENCIES = %i[GBP TRY UAH CZK SEK NOK DKK INR IDR PKR BDT ILS].freeze
26
+ FEMININE_MINOR_CURRENCIES = %i[ILS SAR].freeze
27
+
28
+ module_function
29
+
30
+ def integer_to_words(number, feminine: false)
31
+ cardinal(number, gender: feminine ? :feminine : :masculine)
32
+ end
33
+
34
+ def triple_to_words(number, scale_idx, feminine: false)
35
+ return [] if number.zero?
36
+
37
+ gender = feminine ? :feminine : :masculine
38
+
39
+ case scale_idx
40
+ when 0
41
+ [under_thousand(number, gender: gender)]
42
+ when 1
43
+ words = []
44
+ words << under_thousand(number, gender: :masculine) unless number == 1
45
+ words << SCALES[1][0]
46
+ words
47
+ else
48
+ [under_thousand(number, gender: :masculine), pluralize(number, *SCALES[scale_idx])]
49
+ end
50
+ end
51
+
52
+ def minus_word
53
+ GRAMMAR[:minus]
54
+ end
55
+
56
+ def fraction_joiner(joiner)
57
+ joiner.to_sym == :and ? "e" : GRAMMAR[:conjunction]
58
+ end
59
+
60
+ def default_fraction_word
61
+ GRAMMAR[:default_fraction]
62
+ end
63
+
64
+ def fraction_numerator_feminine?
65
+ false
66
+ end
67
+
68
+ def decimal_separator_word
69
+ "vírgula"
70
+ end
71
+
72
+ def decimal_fraction_words(fraction_string)
73
+ fraction_string.chars.map { |digit| cardinal(digit.to_i) }.join(" ")
74
+ end
75
+
76
+ def time_unit_feminine?(unit)
77
+ unit == :hour
78
+ end
79
+
80
+ def time_number_words(number, unit:)
81
+ cardinal(number, gender: unit == :hour ? :feminine : :masculine)
82
+ end
83
+
84
+ def currency_major_feminine?(currency)
85
+ FEMININE_MAJOR_CURRENCIES.include?(currency)
86
+ end
87
+
88
+ def currency_minor_feminine?(currency)
89
+ FEMININE_MINOR_CURRENCIES.include?(currency)
90
+ end
91
+
92
+ def currency_number_words(number, currency, unit:)
93
+ gender = if unit == :major
94
+ currency_major_feminine?(currency) ? :feminine : :masculine
95
+ else
96
+ currency_minor_feminine?(currency) ? :feminine : :masculine
97
+ end
98
+
99
+ cardinal(number, gender: gender)
100
+ end
101
+
102
+ def date_day(day, format:, date_case:)
103
+ day == 1 ? "primeiro" : cardinal(day)
104
+ end
105
+
106
+ def date_year(year, format:)
107
+ cardinal(year)
108
+ end
109
+
110
+ def ordinal(value, format, gender: :masculine)
111
+ ordinals = ORDINALS[format] || ORDINALS[:nominative]
112
+ gender_data = ordinals[gender] || ordinals[:masculine]
113
+
114
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
115
+
116
+ cardinal(value, gender: gender)
117
+ end
118
+
119
+ def cardinal(number, gender: :masculine)
120
+ integer_value = Integer(number)
121
+ negative = integer_value.negative?
122
+ integer_value = integer_value.abs
123
+
124
+ return ONES_MASC[0] if integer_value.zero?
125
+
126
+ groups = integer_value.to_s
127
+ .chars.reverse.each_slice(3).map(&:reverse)
128
+ .map(&:join).map!(&:to_i).reverse
129
+
130
+ words = []
131
+ groups.each_with_index do |group_value, index|
132
+ next if group_value.zero?
133
+
134
+ scale_idx = groups.size - index - 1
135
+ words.concat triple_to_words(group_value, scale_idx, feminine: gender == :feminine && scale_idx.zero?)
136
+ end
137
+
138
+ words.unshift(minus_word) if negative
139
+ words.join(" ")
140
+ end
141
+
142
+ def under_thousand(number, gender: :masculine)
143
+ return under_hundred(number, gender: gender) if number < 100
144
+ return "cem" if number == 100
145
+
146
+ hundreds = number / 100
147
+ rest = number % 100
148
+ words = [hundreds_data(gender)[hundreds]]
149
+ words << under_hundred(rest, gender: gender) if rest.positive?
150
+ words.join(" e ")
151
+ end
152
+
153
+ def under_hundred(number, gender: :masculine)
154
+ return one_word(number, gender: gender) if number < 10
155
+ return TEENS[number - 10] if number < 20
156
+
157
+ tens = number / 10
158
+ ones = number % 10
159
+ return TENS[tens] if ones.zero?
160
+
161
+ [TENS[tens], one_word(ones, gender: gender)].join(" e ")
162
+ end
163
+
164
+ def one_word(number, gender:)
165
+ return ONES_FEM[number] if gender == :feminine
166
+
167
+ ONES_MASC[number]
168
+ end
169
+
170
+ def hundreds_data(gender)
171
+ gender == :feminine ? HUNDREDS_FEM : HUNDREDS
172
+ end
173
+
174
+ def pluralize(number, singular, _few, plural)
175
+ number.abs == 1 ? singular : plural
176
+ end
15
177
  end
16
178
 
17
179
  register :pt, PT
@@ -10,8 +10,171 @@ module Num2words
10
10
  HUNDREDS = I18n.t("num2words.hundreds", locale: :ro)
11
11
  SCALES = I18n.t("num2words.scales", locale: :ro)
12
12
 
13
- MAJOR_UNIT = I18n.t("num2words.currencies.RON.name", locale: :ro)
14
- MINOR_UNIT = I18n.t("num2words.currencies.RON.fractional.name", locale: :ro)
13
+ FRACTIONS = I18n.t("num2words.fractions", locale: :ro)
14
+ GRAMMAR = I18n.t("num2words.grammar", locale: :ro)
15
+
16
+ DATE = I18n.t("num2words.date", locale: :ro)
17
+ DATE_TEMPLATE = I18n.t("num2words.date.template", locale: :ro)
18
+ TIME = I18n.t("num2words.time", locale: :ro)
19
+ TIME_TEMPLATE = I18n.t("num2words.time.template", locale: :ro)
20
+ DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :ro)
21
+
22
+ ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :ro)
23
+
24
+ FEMININE_MAJOR_CURRENCIES = %i[GBP RUB TRY UAH CZK BYN SEK NOK DKK INR IDR PKR].freeze
25
+ FEMININE_MINOR_CURRENCIES = %i[RUB UAH BYN ILS SAR].freeze
26
+
27
+ module_function
28
+
29
+ def integer_to_words(number, feminine: false)
30
+ cardinal(number, gender: feminine ? :feminine : :masculine)
31
+ end
32
+
33
+ def triple_to_words(number, scale_idx, feminine: false)
34
+ return [] if number.zero?
35
+
36
+ return [under_thousand(number, gender: feminine ? :feminine : :masculine)] if scale_idx.zero?
37
+
38
+ scale = pluralize(number, *SCALES[scale_idx])
39
+ words = scale_number_words(number, scale_idx)
40
+ words << "de" if needs_de_before_scale?(number)
41
+ words << scale
42
+ words
43
+ end
44
+
45
+ def minus_word
46
+ GRAMMAR[:minus]
47
+ end
48
+
49
+ def fraction_joiner(joiner)
50
+ joiner.to_sym == :and ? "și" : GRAMMAR[:conjunction]
51
+ end
52
+
53
+ def default_fraction_word
54
+ GRAMMAR[:default_fraction]
55
+ end
56
+
57
+ def fraction_numerator_feminine?
58
+ true
59
+ end
60
+
61
+ def decimal_separator_word
62
+ "virgulă"
63
+ end
64
+
65
+ def decimal_fraction_words(fraction_string)
66
+ fraction_string.chars.map { |digit| cardinal(digit.to_i) }.join(" ")
67
+ end
68
+
69
+ def time_unit_feminine?(unit)
70
+ %i[hour second].include?(unit)
71
+ end
72
+
73
+ def time_number_words(number, unit:)
74
+ gender = time_unit_feminine?(unit) ? :feminine : :masculine
75
+ cardinal(number, gender: gender)
76
+ end
77
+
78
+ def currency_major_feminine?(currency)
79
+ FEMININE_MAJOR_CURRENCIES.include?(currency)
80
+ end
81
+
82
+ def currency_minor_feminine?(currency)
83
+ FEMININE_MINOR_CURRENCIES.include?(currency)
84
+ end
85
+
86
+ def currency_number_words(number, currency, unit:)
87
+ gender = if unit == :major
88
+ currency_major_feminine?(currency) ? :feminine : :masculine
89
+ else
90
+ currency_minor_feminine?(currency) ? :feminine : :masculine
91
+ end
92
+
93
+ cardinal(number, gender: gender)
94
+ end
95
+
96
+ def date_day(day, format:, date_case:)
97
+ ordinal(day, :default)
98
+ end
99
+
100
+ def date_year(year, format:)
101
+ cardinal(year)
102
+ end
103
+
104
+ def ordinal(value, format, gender: :masculine)
105
+ ordinals = ORDINALS[format] || ORDINALS[:default] || ORDINALS[:nominative]
106
+ gender_data = ordinals[gender] || ordinals[:masculine]
107
+
108
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
109
+
110
+ cardinal(value, gender: gender)
111
+ end
112
+
113
+ def cardinal(number, gender: :masculine)
114
+ integer_value = Integer(number)
115
+ negative = integer_value.negative?
116
+ integer_value = integer_value.abs
117
+
118
+ return ONES_MASC[0] if integer_value.zero?
119
+
120
+ groups = integer_value.to_s
121
+ .chars.reverse.each_slice(3).map(&:reverse)
122
+ .map(&:join).map!(&:to_i).reverse
123
+
124
+ words = []
125
+ groups.each_with_index do |group_value, index|
126
+ next if group_value.zero?
127
+
128
+ scale_idx = groups.size - index - 1
129
+ words.concat triple_to_words(group_value, scale_idx, feminine: gender == :feminine && scale_idx.zero?)
130
+ end
131
+
132
+ words.unshift(minus_word) if negative
133
+ words.join(" ")
134
+ end
135
+
136
+ def scale_number_words(number, scale_idx)
137
+ return ["o"] if scale_idx == 1 && number == 1
138
+ return ["un"] if scale_idx > 1 && number == 1
139
+ return ["două"] if number == 2
140
+
141
+ [under_thousand(number, gender: :masculine)]
142
+ end
143
+
144
+ def needs_de_before_scale?(number)
145
+ number >= 20
146
+ end
147
+
148
+ def under_thousand(number, gender: :masculine)
149
+ return under_hundred(number, gender: gender) if number < 100
150
+
151
+ hundreds = number / 100
152
+ rest = number % 100
153
+ words = [HUNDREDS[hundreds]]
154
+ words << under_hundred(rest, gender: gender) if rest.positive?
155
+ words.join(" ")
156
+ end
157
+
158
+ def under_hundred(number, gender: :masculine)
159
+ return one_word(number, gender: gender) if number < 10
160
+ return TEENS[number - 10] if number < 20
161
+
162
+ tens = number / 10
163
+ ones = number % 10
164
+ return TENS[tens] if ones.zero?
165
+
166
+ [TENS[tens], one_word(ones, gender: gender)].join(" și ")
167
+ end
168
+
169
+ def one_word(number, gender:)
170
+ return ONES_FEM[number] if gender == :feminine
171
+
172
+ ONES_MASC[number]
173
+ end
174
+
175
+ def pluralize(number, singular, few, _plural)
176
+ number.abs == 1 ? singular : few
177
+ end
15
178
  end
16
179
 
17
180
  register :ro, RO