num2words 0.1.6 → 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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +65 -0
  3. data/README.md +145 -135
  4. data/bin/console +2 -14
  5. data/config/locales/ar.yml +5 -1
  6. data/config/locales/be.yml +4 -0
  7. data/config/locales/bg.yml +4 -0
  8. data/config/locales/bn.yml +4 -0
  9. data/config/locales/cs.yml +6 -2
  10. data/config/locales/da.yml +4 -0
  11. data/config/locales/de.yml +8 -4
  12. data/config/locales/el.yml +4 -0
  13. data/config/locales/en.yml +4 -0
  14. data/config/locales/es.yml +4 -0
  15. data/config/locales/et.yml +9 -5
  16. data/config/locales/fa.yml +15 -11
  17. data/config/locales/fi.yml +8 -4
  18. data/config/locales/fr.yml +4 -0
  19. data/config/locales/gu.yml +4 -0
  20. data/config/locales/he.yml +6 -2
  21. data/config/locales/hi.yml +4 -0
  22. data/config/locales/hr.yml +8 -4
  23. data/config/locales/hu.yml +6 -2
  24. data/config/locales/id.yml +4 -0
  25. data/config/locales/it.yml +4 -0
  26. data/config/locales/ja.yml +10 -5
  27. data/config/locales/kn.yml +186 -10
  28. data/config/locales/ko.yml +184 -13
  29. data/config/locales/kz.yml +183 -17
  30. data/config/locales/lt.yml +184 -8
  31. data/config/locales/lv.yml +188 -12
  32. data/config/locales/ml.yml +189 -13
  33. data/config/locales/mr.yml +184 -8
  34. data/config/locales/ms.yml +184 -8
  35. data/config/locales/nb.yml +183 -17
  36. data/config/locales/nl.yml +184 -13
  37. data/config/locales/pa.yml +189 -13
  38. data/config/locales/pl.yml +184 -13
  39. data/config/locales/pt.yml +184 -13
  40. data/config/locales/ro.yml +183 -12
  41. data/config/locales/ru.yml +8 -0
  42. data/config/locales/sk.yml +184 -8
  43. data/config/locales/sl.yml +185 -9
  44. data/config/locales/sr.yml +186 -15
  45. data/config/locales/sv.yml +182 -13
  46. data/config/locales/sw.yml +184 -8
  47. data/config/locales/ta.yml +184 -8
  48. data/config/locales/te.yml +185 -9
  49. data/config/locales/th.yml +183 -12
  50. data/config/locales/tr.yml +183 -12
  51. data/config/locales/uk.yml +189 -18
  52. data/config/locales/ur.yml +187 -11
  53. data/config/locales/vi.yml +183 -12
  54. data/config/locales/zh.yml +191 -9
  55. data/exe/num2words-console +7 -0
  56. data/lib/num2words/console.rb +16 -0
  57. data/lib/num2words/converter.rb +195 -47
  58. data/lib/num2words/locales/ar.rb +130 -0
  59. data/lib/num2words/locales/be.rb +101 -0
  60. data/lib/num2words/locales/bg.rb +127 -0
  61. data/lib/num2words/locales/bn.rb +112 -0
  62. data/lib/num2words/locales/cs.rb +106 -0
  63. data/lib/num2words/locales/da.rb +97 -0
  64. data/lib/num2words/locales/de.rb +119 -0
  65. data/lib/num2words/locales/el.rb +143 -0
  66. data/lib/num2words/locales/en.rb +90 -0
  67. data/lib/num2words/locales/es.rb +185 -0
  68. data/lib/num2words/locales/et.rb +104 -0
  69. data/lib/num2words/locales/fa.rb +104 -0
  70. data/lib/num2words/locales/fi.rb +104 -0
  71. data/lib/num2words/locales/fr.rb +121 -0
  72. data/lib/num2words/locales/gu.rb +112 -0
  73. data/lib/num2words/locales/he.rb +130 -0
  74. data/lib/num2words/locales/hi.rb +112 -0
  75. data/lib/num2words/locales/hr.rb +117 -0
  76. data/lib/num2words/locales/hu.rb +111 -0
  77. data/lib/num2words/locales/id.rb +104 -0
  78. data/lib/num2words/locales/it.rb +142 -0
  79. data/lib/num2words/locales/ja.rb +126 -0
  80. data/lib/num2words/locales/kn.rb +122 -2
  81. data/lib/num2words/locales/ko.rb +158 -2
  82. data/lib/num2words/locales/kz.rb +137 -2
  83. data/lib/num2words/locales/lt.rb +146 -2
  84. data/lib/num2words/locales/lv.rb +148 -2
  85. data/lib/num2words/locales/ml.rb +142 -2
  86. data/lib/num2words/locales/mr.rb +142 -2
  87. data/lib/num2words/locales/ms.rb +134 -2
  88. data/lib/num2words/locales/nb.rb +153 -0
  89. data/lib/num2words/locales/nl.rb +142 -2
  90. data/lib/num2words/locales/pa.rb +122 -2
  91. data/lib/num2words/locales/pl.rb +149 -2
  92. data/lib/num2words/locales/pt.rb +164 -2
  93. data/lib/num2words/locales/ro.rb +165 -2
  94. data/lib/num2words/locales/ru.rb +100 -0
  95. data/lib/num2words/locales/sk.rb +124 -2
  96. data/lib/num2words/locales/sl.rb +154 -2
  97. data/lib/num2words/locales/sr.rb +141 -3
  98. data/lib/num2words/locales/sv.rb +141 -2
  99. data/lib/num2words/locales/sw.rb +133 -2
  100. data/lib/num2words/locales/ta.rb +142 -2
  101. data/lib/num2words/locales/te.rb +142 -2
  102. data/lib/num2words/locales/th.rb +133 -2
  103. data/lib/num2words/locales/tr.rb +141 -2
  104. data/lib/num2words/locales/uk.rb +134 -2
  105. data/lib/num2words/locales/ur.rb +130 -2
  106. data/lib/num2words/locales/vi.rb +142 -2
  107. data/lib/num2words/locales/zh.rb +172 -2
  108. data/lib/num2words/version.rb +1 -1
  109. data/num2words.gemspec +3 -3
  110. metadata +13 -10
  111. data/lib/num2words/locales/no.rb +0 -19
@@ -20,6 +20,133 @@ module Num2words
20
20
  DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :bg)
21
21
 
22
22
  ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :bg)
23
+
24
+ module_function
25
+
26
+ def feminine_group?(scale_idx)
27
+ scale_idx == 1
28
+ end
29
+
30
+ def fraction_numerator_feminine?
31
+ true
32
+ end
33
+
34
+ def minus_word
35
+ GRAMMAR[:minus]
36
+ end
37
+
38
+ def fraction_joiner(joiner)
39
+ joiner.to_sym == :and ? "и" : GRAMMAR[:conjunction]
40
+ end
41
+
42
+ def default_fraction_word
43
+ GRAMMAR[:default_fraction]
44
+ end
45
+
46
+ def integer_to_words(number, feminine: false)
47
+ cardinal(number, feminine: feminine)
48
+ end
49
+
50
+ def triple_to_words(number, scale_idx, feminine: false)
51
+ words = []
52
+
53
+ hundreds = number / 100
54
+ rest = number % 100
55
+
56
+ words << HUNDREDS[hundreds] if hundreds.positive?
57
+
58
+ if rest.between?(10, 19)
59
+ words << join_with_conjunction(words, TEENS[rest - 10])
60
+ else
61
+ tens = rest / 10
62
+ ones = rest % 10
63
+
64
+ if tens >= 2
65
+ tens_words = TENS[tens]
66
+ words << (ones.positive? ? "#{tens_words} и #{feminine ? ONES_FEM[ones] : ONES_MASC[ones]}" : tens_words)
67
+ elsif ones.positive?
68
+ words << join_with_conjunction(words, feminine ? ONES_FEM[ones] : ONES_MASC[ones])
69
+ end
70
+ end
71
+
72
+ words << pluralize(number, *SCALES[scale_idx]) unless scale_idx.zero?
73
+ words.compact
74
+ end
75
+
76
+ def date_day(day, format:, date_case:)
77
+ gender = date_case.to_sym == :genitive ? :masculine : :neuter
78
+ ordinal(day, :nominative, gender: gender)
79
+ end
80
+
81
+ def date_year(year, format:)
82
+ ordinal(year, :nominative)
83
+ end
84
+
85
+ def ordinal(value, format, gender: :masculine)
86
+ ordinals = ORDINALS[format]
87
+ gender_data = ordinals[gender] || ordinals[:masculine]
88
+
89
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
90
+
91
+ if value > 31
92
+ thousands = (value / 100) * 100
93
+ last_two = value % 100
94
+ base_year = cardinal(thousands)
95
+ last_ordinal = gender_data[last_two - 1] || cardinal(last_two)
96
+
97
+ return [base_year, last_ordinal].join(" ")
98
+ end
99
+
100
+ cardinal(value)
101
+ end
102
+
103
+ def cardinal(number, feminine: false)
104
+ integer_value = Integer(number)
105
+ negative = integer_value.negative?
106
+ integer_value = integer_value.abs
107
+
108
+ return (feminine ? ONES_FEM[0] : ONES_MASC[0]) if integer_value.zero?
109
+
110
+ groups = integer_value.to_s
111
+ .chars.reverse.each_slice(3).map(&:reverse)
112
+ .map(&:join).map!(&:to_i).reverse
113
+
114
+ group_phrases = []
115
+ groups.each_with_index do |group_value, index|
116
+ next if group_value.zero?
117
+
118
+ scale_idx = groups.size - index - 1
119
+ group_feminine = feminine_group?(scale_idx) || feminine
120
+ group_words = triple_to_words(group_value, scale_idx, feminine: group_feminine)
121
+ group_words.shift if scale_idx == 1 && group_value == 1
122
+ group_phrases << [group_words.join(" "), scale_idx, group_value]
123
+ end
124
+
125
+ result = join_groups(group_phrases)
126
+ negative ? [minus_word, result].join(" ") : result
127
+ end
128
+
129
+ def pluralize(number, singular, few, plural)
130
+ number.abs == 1 ? singular : few
131
+ end
132
+
133
+ def join_with_conjunction(words, value)
134
+ words.empty? ? value : "и #{value}"
135
+ end
136
+
137
+ def join_groups(group_phrases)
138
+ return "" if group_phrases.empty?
139
+
140
+ phrases = group_phrases.map(&:first)
141
+ last_scale_idx = group_phrases[-1][1]
142
+ last_value = group_phrases[-1][2]
143
+
144
+ if group_phrases.size > 1 && last_scale_idx.zero? && last_value < 100
145
+ [phrases[0...-1].join(" "), phrases[-1]].join(" и ")
146
+ else
147
+ phrases.join(" ")
148
+ end
149
+ end
23
150
  end
24
151
 
25
152
  register :bg, BG
@@ -20,6 +20,118 @@ module Num2words
20
20
  DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :bn)
21
21
 
22
22
  ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :bn)
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
+ GRAMMAR[:conjunction]
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, :nominative, gender: :neuter)
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[: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 = ["#{UNDER_HUNDRED[hundreds]}শ"]
128
+ words << UNDER_HUNDRED[rest] if rest.positive?
129
+ words.join(" ")
130
+ end
131
+
132
+ def pluralize(_number, singular, _few, _plural)
133
+ singular
134
+ end
23
135
  end
24
136
 
25
137
  register :bn, BN
@@ -20,6 +20,112 @@ module Num2words
20
20
  DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :cs)
21
21
 
22
22
  ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :cs)
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].include?(currency)
36
+ end
37
+
38
+ def currency_minor_feminine?(currency)
39
+ false
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 integer_to_words(number, feminine: false)
55
+ cardinal(number, feminine: feminine)
56
+ end
57
+
58
+ def triple_to_words(number, scale_idx, feminine: false)
59
+ return [] if number.zero?
60
+
61
+ words = []
62
+ words << HUNDREDS[number / 100] if number >= 100
63
+ rest = number % 100
64
+
65
+ if rest.between?(10, 19)
66
+ words << TEENS[rest - 10]
67
+ else
68
+ words << TENS[rest / 10] if rest >= 20
69
+ ones = rest % 10
70
+ words << (feminine ? ONES_FEM[ones] : ONES_MASC[ones]) if ones.positive?
71
+ end
72
+
73
+ words << pluralize(number, *SCALES[scale_idx]) unless scale_idx.zero?
74
+ words.compact
75
+ end
76
+
77
+ def date_day(day, format:, date_case:)
78
+ ordinal(day, :default)
79
+ end
80
+
81
+ def date_year(year, format:)
82
+ cardinal(year)
83
+ end
84
+
85
+ def ordinal(value, format, gender: :masculine)
86
+ ordinals = ORDINALS[format]
87
+ gender_data = ordinals[gender] || ordinals[:masculine]
88
+
89
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
90
+
91
+ cardinal(value)
92
+ end
93
+
94
+ def cardinal(number, feminine: false)
95
+ integer_value = Integer(number)
96
+ negative = integer_value.negative?
97
+ integer_value = integer_value.abs
98
+
99
+ return (feminine ? ONES_FEM[0] : ONES_MASC[0]) if integer_value.zero?
100
+
101
+ groups = integer_value.to_s
102
+ .chars.reverse.each_slice(3).map(&:reverse)
103
+ .map(&:join).map!(&:to_i).reverse
104
+
105
+ words = []
106
+ groups.each_with_index do |group_value, index|
107
+ next if group_value.zero?
108
+
109
+ scale_idx = groups.size - index - 1
110
+ group_words = triple_to_words(group_value, scale_idx, feminine: feminine_group?(scale_idx) || (feminine && scale_idx.zero?))
111
+ group_words.shift if scale_idx == 1 && group_value == 1
112
+ words.concat group_words
113
+ end
114
+
115
+ words.unshift(minus_word) if negative
116
+ words.join(" ")
117
+ end
118
+
119
+ def pluralize(number, singular, few, plural)
120
+ number = number.abs
121
+ return plural if (11..14).include?(number % 100)
122
+
123
+ case number % 10
124
+ when 1 then singular
125
+ when 2..4 then few
126
+ else plural
127
+ end
128
+ end
23
129
  end
24
130
 
25
131
  register :cs, CS
@@ -20,6 +20,103 @@ module Num2words
20
20
  DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :da)
21
21
 
22
22
  ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :da)
23
+
24
+ module_function
25
+
26
+ def minus_word
27
+ GRAMMAR[:minus]
28
+ end
29
+
30
+ def fraction_joiner(joiner)
31
+ joiner.to_sym == :and ? "og" : 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 integer_to_words(number, feminine: false)
43
+ cardinal(number, feminine: feminine)
44
+ end
45
+
46
+ def date_day(day, format:, date_case:)
47
+ ordinal(day, :nominative)
48
+ end
49
+
50
+ def triple_to_words(number, scale_idx, feminine: false)
51
+ triple_words(number, feminine: feminine).tap do |words|
52
+ words << pluralize(number, *SCALES[scale_idx]) unless scale_idx.zero?
53
+ end
54
+ end
55
+
56
+ def date_year(year, format:)
57
+ cardinal(year)
58
+ end
59
+
60
+ def ordinal(value, format, gender: :masculine)
61
+ ordinals = ORDINALS[format] || ORDINALS[:nominative]
62
+ gender_data = ordinals[gender] || ordinals[:masculine]
63
+
64
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
65
+
66
+ cardinal(value)
67
+ end
68
+
69
+ def pluralize(number, singular, few, plural)
70
+ number.abs == 1 ? singular : plural
71
+ end
72
+
73
+ def cardinal(number, feminine: false)
74
+ integer_value = Integer(number)
75
+ negative = integer_value.negative?
76
+ integer_value = integer_value.abs
77
+
78
+ return (feminine ? ONES_FEM[0] : ONES_MASC[0]) if integer_value.zero?
79
+
80
+ groups = integer_value.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
+ next if group_value.zero?
87
+
88
+ scale_idx = groups.size - index - 1
89
+ group_words = triple_to_words(group_value, scale_idx, feminine: feminine && scale_idx.zero?)
90
+ group_words[0] = "et" if scale_idx == 1 && group_value == 1
91
+ group_words.unshift("og") if scale_idx.zero? && words.any? && group_value < 100
92
+ words.concat(group_words)
93
+ end
94
+
95
+ words.unshift(minus_word) if negative
96
+ words.join(" ")
97
+ end
98
+
99
+ def triple_words(number, feminine: false)
100
+ return [] if number.zero?
101
+
102
+ words = []
103
+ hundreds = number / 100
104
+ rest = number % 100
105
+
106
+ words << HUNDREDS[hundreds] if hundreds.positive?
107
+ words << "og" if hundreds.positive? && rest.positive?
108
+ words << under_hundred(rest, feminine: feminine) if rest.positive?
109
+ words.compact
110
+ end
111
+
112
+ def under_hundred(number, feminine: false)
113
+ return TEENS[number - 10] if number.between?(10, 19)
114
+ return TENS[number / 10] if (number % 10).zero?
115
+ return (feminine ? ONES_FEM[number] : ONES_MASC[number]) if number < 10
116
+
117
+ ones = feminine ? ONES_FEM[number % 10] : ONES_MASC[number % 10]
118
+ "#{ones}og#{TENS[number / 10]}"
119
+ end
23
120
  end
24
121
 
25
122
  register :da, DA
@@ -20,6 +20,125 @@ module Num2words
20
20
  DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :de)
21
21
 
22
22
  ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :de)
23
+
24
+ module_function
25
+
26
+ def feminine_group?(scale_idx)
27
+ scale_idx >= 2
28
+ end
29
+
30
+ def fraction_numerator_feminine?
31
+ false
32
+ end
33
+
34
+ def minus_word
35
+ GRAMMAR[:minus]
36
+ end
37
+
38
+ def fraction_joiner(joiner)
39
+ joiner.to_sym == :and ? "und" : GRAMMAR[:conjunction]
40
+ end
41
+
42
+ def default_fraction_word
43
+ GRAMMAR[:default_fraction]
44
+ end
45
+
46
+ def integer_to_words(number, feminine: false)
47
+ cardinal(number, feminine: feminine)
48
+ end
49
+
50
+ def currency_major_feminine?(currency)
51
+ %i[UAH CZK SEK NOK DKK INR IDR PKR].include?(currency)
52
+ end
53
+
54
+ def currency_minor_feminine?(currency)
55
+ false
56
+ end
57
+
58
+ def currency_number_words(number, currency, unit:)
59
+ return cardinal(number, feminine: currency_major_feminine?(currency)) unless Integer(number) == 1
60
+
61
+ unit == :major && currency_major_feminine?(currency) ? "eine" : "ein"
62
+ end
63
+
64
+ def triple_to_words(number, scale_idx, feminine: false)
65
+ return [] if number.zero?
66
+
67
+ words = [under_thousand(number, feminine: feminine)]
68
+ words << pluralize(number, *SCALES[scale_idx]) unless scale_idx.zero?
69
+ words
70
+ end
71
+
72
+ def date_day(day, format:, date_case:)
73
+ ordinal(day, :default)
74
+ end
75
+
76
+ def date_year(year, format:)
77
+ cardinal(year).delete(" ")
78
+ end
79
+
80
+ def ordinal(value, format, gender: :masculine)
81
+ ordinals = ORDINALS[format] || ORDINALS[:nominative]
82
+ gender_data = ordinals[gender] || ordinals[:masculine]
83
+
84
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
85
+
86
+ cardinal(value)
87
+ end
88
+
89
+ def cardinal(number, feminine: false)
90
+ integer_value = Integer(number)
91
+ negative = integer_value.negative?
92
+ integer_value = integer_value.abs
93
+
94
+ return (feminine ? ONES_FEM[0] : ONES_MASC[0]) if integer_value.zero?
95
+
96
+ groups = integer_value.to_s
97
+ .chars.reverse.each_slice(3).map(&:reverse)
98
+ .map(&:join).map!(&:to_i).reverse
99
+
100
+ words = []
101
+ groups.each_with_index do |group_value, index|
102
+ next if group_value.zero?
103
+
104
+ scale_idx = groups.size - index - 1
105
+ group_feminine = feminine_group?(scale_idx) || (feminine && scale_idx.zero?)
106
+ group_words = triple_to_words(group_value, scale_idx, feminine: group_feminine)
107
+ group_words.shift if scale_idx == 1 && group_value == 1
108
+ words.concat(group_words)
109
+ end
110
+
111
+ words.unshift(minus_word) if negative
112
+ words.join(" ")
113
+ end
114
+
115
+ def under_thousand(number, feminine: false)
116
+ return under_hundred(number, feminine: feminine) if number < 100
117
+
118
+ hundreds = number / 100
119
+ rest = number % 100
120
+ hundred_words = hundreds == 1 ? "einhundert" : "#{under_hundred(hundreds)}hundert"
121
+
122
+ return hundred_words if rest.zero?
123
+
124
+ "#{hundred_words}#{under_hundred(rest, feminine: feminine)}"
125
+ end
126
+
127
+ def under_hundred(number, feminine: false)
128
+ return feminine ? ONES_FEM[number] : ONES_MASC[number] if number < 10
129
+ return TEENS[number - 10] if number < 20
130
+ return TENS[number / 10] if (number % 10).zero?
131
+
132
+ ones = number % 10
133
+ tens = number / 10
134
+ ones_word = ones == 1 ? "ein" : ONES_MASC[ones]
135
+
136
+ "#{ones_word}und#{TENS[tens]}"
137
+ end
138
+
139
+ def pluralize(number, singular, few, plural)
140
+ number.abs == 1 ? singular : plural
141
+ end
23
142
  end
24
143
 
25
144
  register :de, DE