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,110 @@ module Num2words
20
20
  DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :id)
21
21
 
22
22
  ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :id)
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
+ joiner.to_sym == :and ? "dan" : 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
+ GRAMMAR[:conjunction]
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
+ cardinal(day)
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 == 1 ? "seratus" : "#{ONES_MASC[hundreds]} ratus"]
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 = ["#{ONES_MASC[tens]} puluh"]
120
+ words << ONES_MASC[ones] if ones.positive?
121
+ words.join(" ")
122
+ end
123
+
124
+ def pluralize(_number, singular, _few, _plural)
125
+ singular
126
+ end
23
127
  end
24
128
 
25
129
  register :id, ID
@@ -20,6 +20,148 @@ module Num2words
20
20
  DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :it)
21
21
 
22
22
  ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :it)
23
+
24
+ FEMININE_MAJOR_CURRENCIES = %i[GBP TRY UAH CZK SEK NOK DKK INR IDR PKR].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
+
37
+ if scale_idx == 1
38
+ return [SCALES[1][0]] if number == 1
39
+
40
+ words << under_thousand(number, feminine: feminine)
41
+ words << SCALES[1][1]
42
+ elsif scale_idx.positive?
43
+ words << (number == 1 ? "un" : under_thousand(number, feminine: feminine))
44
+ words << pluralize(number, *SCALES[scale_idx])
45
+ else
46
+ words << under_thousand(number, feminine: feminine)
47
+ end
48
+
49
+ words
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
+ GRAMMAR[:conjunction]
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 currency_major_feminine?(currency)
81
+ FEMININE_MAJOR_CURRENCIES.include?(currency)
82
+ end
83
+
84
+ def currency_minor_feminine?(_currency)
85
+ false
86
+ end
87
+
88
+ def currency_number_words(number, currency, unit:)
89
+ feminine = unit == :major ? currency_major_feminine?(currency) : currency_minor_feminine?(currency)
90
+
91
+ cardinal(number, feminine: feminine)
92
+ end
93
+
94
+ def date_day(day, format:, date_case:)
95
+ cardinal(day)
96
+ end
97
+
98
+ def date_year(year, format:)
99
+ cardinal(year)
100
+ end
101
+
102
+ def ordinal(value, format, gender: :masculine)
103
+ ordinals = ORDINALS[format] || ORDINALS[:default] || ORDINALS[:nominative]
104
+ gender_data = ordinals[gender] || ordinals[:masculine]
105
+
106
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
107
+
108
+ cardinal(value)
109
+ end
110
+
111
+ def cardinal(number, feminine: false)
112
+ integer_value = Integer(number)
113
+ negative = integer_value.negative?
114
+ integer_value = integer_value.abs
115
+
116
+ return ONES_MASC[0] if integer_value.zero?
117
+
118
+ groups = integer_value.to_s
119
+ .chars.reverse.each_slice(3).map(&:reverse)
120
+ .map(&:join).map!(&:to_i).reverse
121
+
122
+ words = []
123
+ groups.each_with_index do |group_value, index|
124
+ next if group_value.zero?
125
+
126
+ scale_idx = groups.size - index - 1
127
+ words.concat triple_to_words(group_value, scale_idx, feminine: feminine)
128
+ end
129
+
130
+ words.unshift(minus_word) if negative
131
+ words.join(" ")
132
+ end
133
+
134
+ def under_thousand(number, feminine: false)
135
+ return under_hundred(number, feminine: feminine) if number < 100
136
+
137
+ hundreds = number / 100
138
+ rest = number % 100
139
+ hundred_word = HUNDREDS[hundreds]
140
+ hundred_word = hundred_word[0...-1] if rest.between?(80, 89)
141
+
142
+ return hundred_word if rest.zero?
143
+
144
+ "#{hundred_word}#{under_hundred(rest, feminine: feminine)}"
145
+ end
146
+
147
+ def under_hundred(number, feminine: false)
148
+ ones_data = feminine ? ONES_FEM : ONES_MASC
149
+
150
+ return ones_data[number] if number < 10
151
+ return TEENS[number - 10] if number < 20
152
+
153
+ tens = number / 10
154
+ ones = number % 10
155
+ return TENS[tens] if ones.zero?
156
+
157
+ tens_word = TENS[tens]
158
+ tens_word = tens_word[0...-1] if [1, 8].include?(ones)
159
+ "#{tens_word}#{ones_data[ones]}"
160
+ end
161
+
162
+ def pluralize(number, singular, _few, plural)
163
+ number.abs == 1 ? singular : plural
164
+ end
23
165
  end
24
166
 
25
167
  register :it, IT
@@ -20,6 +20,132 @@ module Num2words
20
20
  DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :ja)
21
21
 
22
22
  ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :ja)
23
+
24
+ THOUSANDS = ["", "せん", "にせん", "さんぜん", "よんせん", "ごせん", "ろくせん", "ななせん", "はっせん", "きゅうせん"].freeze
25
+ LARGE_SCALES = ["", "まん", "おく", "ちょう", "けい"].freeze
26
+
27
+ class << self
28
+ def integer_to_words(number, feminine: false)
29
+ integer_value = Integer(number)
30
+ negative = integer_value.negative?
31
+ integer_value = integer_value.abs
32
+
33
+ return ONES_MASC[0] if integer_value.zero?
34
+
35
+ groups = integer_value.to_s
36
+ .chars.reverse.each_slice(4).map(&:reverse)
37
+ .map(&:join).map!(&:to_i).reverse
38
+
39
+ words = groups.each_with_index.filter_map do |group_value, index|
40
+ next if group_value.zero?
41
+
42
+ scale_index = groups.size - index - 1
43
+ "#{under_ten_thousand(group_value)}#{LARGE_SCALES.fetch(scale_index, '')}"
44
+ end.join
45
+
46
+ negative ? "#{minus_word}#{words}" : words
47
+ end
48
+
49
+ def triple_to_words(number, scale_idx, feminine: false)
50
+ words = under_ten_thousand(number)
51
+ scale = LARGE_SCALES[scale_idx].to_s
52
+ ["#{words}#{scale}"]
53
+ end
54
+
55
+ def minus_word
56
+ GRAMMAR[:minus]
57
+ end
58
+
59
+ def fraction_joiner(joiner)
60
+ joiner.to_sym == :and ? "と" : GRAMMAR[:conjunction]
61
+ end
62
+
63
+ def default_fraction_word
64
+ GRAMMAR[:default_fraction]
65
+ end
66
+
67
+ def decimal_separator_word
68
+ GRAMMAR[:decimal_separator] || "てん"
69
+ end
70
+
71
+ def decimal_fraction_words(fraction_string)
72
+ fraction_string.chars.map { |digit| ONES_MASC[digit.to_i] }.join
73
+ end
74
+
75
+ def join_decimal_words(words)
76
+ words.compact.reject(&:empty?).join
77
+ end
78
+
79
+ def join_fraction_words(words)
80
+ sign_word, integer_words, conjunction_word, numerator_words, denominator_words = words
81
+ numerator_words = "" if denominator_words.to_s.end_with?(numerator_words.to_s)
82
+
83
+ [sign_word, integer_words, conjunction_word, denominator_words, numerator_words].compact.reject(&:empty?).join
84
+ end
85
+
86
+ def pluralize(number, singular, few, _plural)
87
+ number == 1 ? singular : few
88
+ end
89
+
90
+ def currency_major_feminine?(_currency)
91
+ false
92
+ end
93
+
94
+ def currency_minor_feminine?(_currency)
95
+ false
96
+ end
97
+
98
+ def currency_number_words(number, _currency, unit:)
99
+ integer_to_words(number)
100
+ end
101
+
102
+ def join_currency_parts(parts)
103
+ parts.join
104
+ end
105
+
106
+ def time_unit_feminine?(_unit)
107
+ false
108
+ end
109
+
110
+ def time_number_words(number, unit:)
111
+ integer_to_words(number)
112
+ end
113
+
114
+ def join_time_words(number_words, unit_words)
115
+ "#{number_words}#{unit_words}"
116
+ end
117
+
118
+ def date_day(day, format: :default, date_case: :default)
119
+ integer_to_words(day)
120
+ end
121
+
122
+ def date_year(year, format: :default)
123
+ integer_to_words(year)
124
+ end
125
+
126
+ def under_ten_thousand(number)
127
+ thousands = number / 1000
128
+ remainder = number % 1000
129
+ hundreds = remainder / 100
130
+ rest = remainder % 100
131
+
132
+ [
133
+ THOUSANDS[thousands],
134
+ HUNDREDS[hundreds],
135
+ under_hundred(rest)
136
+ ].compact.reject(&:empty?).join
137
+ end
138
+
139
+ def under_hundred(number)
140
+ return "" if number.zero?
141
+ return TEENS[number - 10] if number.between?(10, 19)
142
+
143
+ tens = number / 10
144
+ ones = number % 10
145
+
146
+ [TENS[tens], ones.positive? ? ONES_MASC[ones] : nil].compact.reject(&:empty?).join
147
+ end
148
+ end
23
149
  end
24
150
 
25
151
  register :ja, JA
@@ -10,8 +10,128 @@ module Num2words
10
10
  HUNDREDS = I18n.t("num2words.hundreds", locale: :kn)
11
11
  SCALES = I18n.t("num2words.scales", locale: :kn)
12
12
 
13
- MAJOR_UNIT = I18n.t("num2words.currencies.INR.name", locale: :kn)
14
- MINOR_UNIT = I18n.t("num2words.currencies.INR.fractional.name", locale: :kn)
13
+ FRACTIONS = I18n.t("num2words.fractions", locale: :kn)
14
+ GRAMMAR = I18n.t("num2words.grammar", locale: :kn)
15
+
16
+ DATE = I18n.t("num2words.date", locale: :kn)
17
+ DATE_TEMPLATE = I18n.t("num2words.date.template", locale: :kn)
18
+ TIME = I18n.t("num2words.time", locale: :kn)
19
+ TIME_TEMPLATE = I18n.t("num2words.time.template", locale: :kn)
20
+ DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :kn)
21
+
22
+ ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :kn)
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 :kn, KN
@@ -10,8 +10,164 @@ module Num2words
10
10
  HUNDREDS = I18n.t("num2words.hundreds", locale: :ko)
11
11
  SCALES = I18n.t("num2words.scales", locale: :ko)
12
12
 
13
- MAJOR_UNIT = I18n.t("num2words.currencies.KRW.name", locale: :ko)
14
- MINOR_UNIT = I18n.t("num2words.currencies.KRW.fractional.name", locale: :ko)
13
+ FRACTIONS = I18n.t("num2words.fractions", locale: :ko)
14
+ GRAMMAR = I18n.t("num2words.grammar", locale: :ko)
15
+
16
+ DATE = I18n.t("num2words.date", locale: :ko)
17
+ DATE_TEMPLATE = I18n.t("num2words.date.template", locale: :ko)
18
+ TIME = I18n.t("num2words.time", locale: :ko)
19
+ TIME_TEMPLATE = I18n.t("num2words.time.template", locale: :ko)
20
+ DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :ko)
21
+
22
+ ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :ko)
23
+
24
+ LARGE_SCALES = ["", "만", "억", "조", "경"].freeze
25
+
26
+ module_function
27
+
28
+ def integer_to_words(number, feminine: false)
29
+ cardinal(number)
30
+ end
31
+
32
+ def triple_to_words(number, scale_idx, feminine: false)
33
+ return [] if number.zero?
34
+
35
+ words = under_ten_thousand(number)
36
+ scale = LARGE_SCALES[scale_idx].to_s
37
+ ["#{words}#{scale}"]
38
+ end
39
+
40
+ def minus_word
41
+ GRAMMAR[:minus]
42
+ end
43
+
44
+ def fraction_joiner(joiner)
45
+ joiner.to_sym == :and ? "와" : GRAMMAR[:conjunction]
46
+ end
47
+
48
+ def default_fraction_word
49
+ GRAMMAR[:default_fraction]
50
+ end
51
+
52
+ def fraction_numerator_feminine?
53
+ false
54
+ end
55
+
56
+ def decimal_separator_word
57
+ "점"
58
+ end
59
+
60
+ def decimal_fraction_words(fraction_string)
61
+ fraction_string.chars.map { |digit| cardinal(digit.to_i) }.join(" ")
62
+ end
63
+
64
+ def join_fraction_words(words)
65
+ sign_word, integer_words, conjunction_word, numerator_words, denominator_words = words
66
+ [sign_word, integer_words, conjunction_word, denominator_words, numerator_words].compact.reject(&:empty?).join(" ")
67
+ end
68
+
69
+ def date_day(day, format:, date_case:)
70
+ cardinal(day)
71
+ end
72
+
73
+ def date_year(year, format:)
74
+ cardinal(year)
75
+ end
76
+
77
+ def time_unit_feminine?(_unit)
78
+ false
79
+ end
80
+
81
+ def time_number_words(number, unit:)
82
+ cardinal(number)
83
+ end
84
+
85
+ def join_time_words(number_words, unit_words)
86
+ "#{number_words} #{unit_words}"
87
+ end
88
+
89
+ def currency_major_feminine?(_currency)
90
+ false
91
+ end
92
+
93
+ def currency_minor_feminine?(_currency)
94
+ false
95
+ end
96
+
97
+ def currency_number_words(number, _currency, unit:)
98
+ cardinal(number)
99
+ end
100
+
101
+ def join_currency_parts(parts)
102
+ parts.join(" ").strip
103
+ end
104
+
105
+ def ordinal(value, format, gender: :masculine)
106
+ ordinals = ORDINALS[format] || ORDINALS[:default] || ORDINALS[:nominative]
107
+ gender_data = ordinals[gender] || ordinals[:masculine]
108
+
109
+ return gender_data[value - 1] if value.between?(1, gender_data.length)
110
+
111
+ cardinal(value)
112
+ end
113
+
114
+ def cardinal(number)
115
+ integer_value = Integer(number)
116
+ negative = integer_value.negative?
117
+ integer_value = integer_value.abs
118
+
119
+ return ONES_MASC[0] if integer_value.zero?
120
+
121
+ groups = integer_value.to_s
122
+ .chars.reverse.each_slice(4).map(&:reverse)
123
+ .map(&:join).map!(&:to_i).reverse
124
+
125
+ words = groups.each_with_index.filter_map do |group_value, index|
126
+ next if group_value.zero?
127
+
128
+ scale_index = groups.size - index - 1
129
+ scale = LARGE_SCALES.fetch(scale_index, "")
130
+ group_words = under_ten_thousand(group_value)
131
+ scale.empty? ? group_words : "#{group_words}#{scale}"
132
+ end
133
+
134
+ words.unshift(minus_word) if negative
135
+ words.join(" ")
136
+ end
137
+
138
+ def under_ten_thousand(number)
139
+ thousands = number / 1000
140
+ remainder = number % 1000
141
+ hundreds = remainder / 100
142
+ rest = remainder % 100
143
+
144
+ [
145
+ unit_with_scale(thousands, "천"),
146
+ unit_with_scale(hundreds, "백"),
147
+ under_hundred(rest)
148
+ ].compact.reject(&:empty?).join
149
+ end
150
+
151
+ def under_hundred(number)
152
+ return "" if number.zero?
153
+ return TEENS[number - 10] if number.between?(10, 19)
154
+ return ONES_MASC[number] if number < 10
155
+
156
+ tens = number / 10
157
+ ones = number % 10
158
+ [TENS[tens], ones.positive? ? ONES_MASC[ones] : nil].compact.reject(&:empty?).join
159
+ end
160
+
161
+ def unit_with_scale(value, scale)
162
+ return "" if value.zero?
163
+ return scale if value == 1
164
+
165
+ "#{ONES_MASC[value]}#{scale}"
166
+ end
167
+
168
+ def pluralize(_number, singular, _few, _plural)
169
+ singular
170
+ end
15
171
  end
16
172
 
17
173
  register :ko, KO