module RuPropisju
Самый лучший, прекрасный, кривой и неотразимый суперпечататель суммы прописью для Ruby.
RuPropisju.rublej(123) # "сто двадцать три рубля"
Constants
- CURRENCIES
www.xe.com/symbols.php (лица, приближенные форексам и всяким там валютам и курсам) говорят, что код валюты российского рубля - rub
- DECIMALS
- MONEY_GENDERS
- SUPPORTED_CURRENCIES
- TRANSLATIONS
- VERSION
Public Class Methods
Cоставляет число прописью для чисел до тысячи
# File lib/ru_propisju.rb, line 602 def compose_ordinal(remaining_amount_or_nil, gender, item_forms = [], locale = :ru) remaining_amount = remaining_amount_or_nil.to_i locale = locale.to_s # Ноль чего-то # return "ноль %s" % item_forms[3] if remaining_amount_or_nil.zero? rest, rest1, chosen_ordinal, ones, tens, hundreds = [nil]*6 rest = remaining_amount % 1000 remaining_amount = remaining_amount / 1000 if rest.zero? # последние три знака нулевые return item_forms[2] end locale_root = pick_locale(TRANSLATIONS, locale) # начинаем подсчет с Rest # сотни hundreds = locale_root[(rest / 100).to_i * 100] # десятки rest = rest % 100 rest1 = rest / 10 # единички ones = "" tens = locale_root[rest1 == 1 ? rest : rest1 * 10] # индекс выбранной формы chosen_ordinal = 2 if rest1 < 1 || rest1 > 1 # единицы value = locale_root[rest % 10] # если попался хэш, делаем выбор согласно рода value = value[gender] if value.kind_of? Hash ones = value case rest % 10 when 1 then chosen_ordinal = 0 # индекс формы меняется when 2..4 then chosen_ordinal = 1 # индекс формы меняется end end plural = [ hundreds, tens, ones, item_forms[chosen_ordinal], ].compact.reject(&:empty?).join(' ').strip return plural end
# File lib/ru_propisju.rb, line 816 def delimited_number(number, delimiter) return number.to_s if delimiter == '' number.to_s.gsub!(/(\d)(?=(\d\d\d)+(?!\d))/) do |digit_to_delimit| "#{digit_to_delimit}#{delimiter}" end || number.to_s end
# File lib/ru_propisju.rb, line 811 def format_integral(number, options) formatted_number = format(options[:integrals_formatter], number) delimited_number(formatted_number, options[:integrals_delimiter]) end
# File lib/ru_propisju.rb, line 824 def pick_locale(from_hash, locale) return from_hash[locale.to_s] if from_hash.has_key?(locale.to_s) raise UnknownLocale, "Unknown locale #{locale.inspect}" end
Выдает сумму прописью с учетом дробной доли. Дробная доля округляется до миллионной, или (если дробная доля оканчивается на нули) до ближайшей доли или точки ( 500 тысячных округляется до 5 десятых, 30.0000 до 30).
# File lib/ru_propisju.rb, line 736 def propisju_float(num, locale = :ru) locale_root = pick_locale(DECIMALS, locale) source_expression = locale_root[:prefix][0] target_prefix = locale_root[:prefix][1] words = locale_root[:source_words].map do |e| [ e, e.gsub(/#{source_expression}$/, target_prefix), e.gsub(/#{source_expression}$/, target_prefix), ] end.freeze # Укорачиваем до триллионной доли formatted = num.to_s[/^\d+(\.\d{0,#{words.length}})?/].gsub(/0+$/, '') wholes, decimals = formatted.split(".") return propisju_int(wholes.to_i, 1, [], locale) if decimals.to_i.zero? whole_st = propisju_shtuk(wholes.to_i, 2, words[0], locale) rem_st = propisju_shtuk(decimals.to_i, 2, words[decimals.length], locale) [whole_st, rem_st].compact.join(" ") end
Выполняет преобразование числа из цифрого вида в символьное
amount - числительное gender = 1 - мужской, = 2 - женский, = 3 - средний one_item - именительный падеж единственного числа (= 1) two_items - родительный падеж единственного числа (= 2-4) five_items - родительный падеж множественного числа ( = 5-10)
Примерно так:
propisju(42, 1, ["сволочь", "сволочи", "сволочей"]) # => "сорок две сволочи"
# File lib/ru_propisju.rb, line 770 def propisju_int(amount, gender = 1, item_forms = [], locale = :ru) locale_root = pick_locale(TRANSLATIONS, locale) # zero! if amount.zero? return [locale_root['0'], item_forms[-1]].compact.join(' ') end fractions = [ [:trillions, 1_000_000_000_000], [:billions, 1_000_000_000], [:millions, 1_000_000], [:thousands, 1_000], ] parts = fractions.map do | name, multiplier | [name, fraction = (amount / multiplier) % 1000] end if amount / fractions.first.last >= 1000 raise "Amount is too large" end # Единицы обрабатываем отдельно ones = amount % 1000 # Составляем простые тысячные доли parts_in_writing = parts.reject do | part | part[1].zero? end.map do | name, fraction | thousandth_gender = (name == :thousands) ? 2 : 1 compose_ordinal(fraction, thousandth_gender, locale_root[name], locale) end # И только единицы обрабатываем с переданными параметрами parts_in_writing.push(compose_ordinal(ones, gender, item_forms, locale)) parts_in_writing.compact.join(' ') end
# File lib/ru_propisju.rb, line 585 def zero(locale_data, integrals, fractions, fraction_as_number, integrals_as_number, options) integ = integrals_as_number ? format(options[:integrals_formatter], 0) : locale_data['0'] frac = fraction_as_number ? format(options[:fraction_formatter], 0) : locale_data['0'] parts = [integ , integrals[-1], frac, fractions[-1]] parts.join(' ') end
# File lib/ru_propisju.rb, line 592 def zero_fraction(locale, money_gender, fractions, fraction_as_number, options) if fraction_as_number [format(options[:fraction_formatter], 0), choose_plural(0, fractions)] else propisju_int(0, money_gender, fractions, locale) end end
Public Instance Methods
Выводит сумму прописью в зависимости от выбранной валюты. Поддерживаемые валюты: rur, usd, uah, eur
amount_in_words(345.2, 'rur') #=> "триста сорок пять рублей 20 копеек"
Опции¶ ↑
-
:always_show_fraction
- true/false. позволяет принудительно отображать 0 в качестве дробной части для целого числа -
:fraction_formatter
- строка. формат отображения числа после точки, например '%d'
# File lib/ru_propisju.rb, line 379 def amount_in_words(amount, currency, locale = :ru, options = {}) currency = currency.to_s.downcase unless CURRENCIES.has_key? currency raise UnknownCurrency, "Unsupported currency #{currency}, the following are supported: #{SUPPORTED_CURRENCIES}" end method(CURRENCIES[currency]).call(amount, locale, options) end
Выбирает нужный падеж существительного в зависимости от числа
choose_plural(3, "штука", "штуки", "штук") #=> "штуки"
# File lib/ru_propisju.rb, line 354 def choose_plural(amount, *variants) variants = variants.flatten mod_ten = amount % 10 mod_hundred = amount % 100 variant = if (mod_ten == 1 && mod_hundred != 11) 1 else if mod_ten >= 2 && mod_ten <= 4 && (mod_hundred <10 || mod_hundred % 100>=20) 2 else 3 end end variants[variant-1] end
Выводит целое или дробное число как сумму в рублях и копейках не прописью
digit_rublej(345.2) #=> "345 рублей 20 копеек"
Опции¶ ↑
-
:always_show_fraction
- true/false. позволяет принудительно отображать 0 в качестве дробной части для целого числа -
:fraction_formatter
- строка. формат отображения числа после точки, например '%d' -
:integrals_formatter
- строка. формат отображения целого числа, например '%d' -
:integrals_delimiter
- строка. разделитель разрядов для целой части числа, например ' '
# File lib/ru_propisju.rb, line 424 def digit_rublej(amount, locale = :ru, options = {}) integrals_key = :rub_integral fractions_key = :rub_fraction money_gender = MONEY_GENDERS[:rub] money(amount, locale, integrals_key, fractions_key, money_gender, true, true, options) end
Выводит целое или дробное число как сумму в долларах прописью
dollarov(32) #=> "тридцать два доллара"
Опции¶ ↑
-
:always_show_fraction
- true/false. позволяет принудительно отображать 0 в качестве дробной части для целого числа
# File lib/ru_propisju.rb, line 463 def dollarov(amount, locale = :ru, options = {}) integrals_key = :usd_integral fractions_key = :usd_fraction money_gender = MONEY_GENDERS[:usd] money(amount, locale, integrals_key, fractions_key, money_gender, false, false, options) end
Выводит целое или дробное число как сумму в евро прописью
evro(32) #=> "тридцать два евро"
Опции¶ ↑
-
:always_show_fraction
- true/false. позволяет принудительно отображать 0 в качестве дробной части для целого числа
# File lib/ru_propisju.rb, line 477 def evro(amount, locale = :ru, options = {}) integrals_key = :eur_integral fractions_key = :eur_fraction money_gender = MONEY_GENDERS[:eur] money(amount, locale, integrals_key, fractions_key, money_gender, false, false, options) end
Выводит целое или дробное число как сумму в гривнах прописью
griven(32) #=> "тридцать две гривны"
Опции¶ ↑
-
:always_show_fraction
- true/false. позволяет принудительно отображать 0 в качестве дробной части для целого числа
# File lib/ru_propisju.rb, line 449 def griven(amount, locale = :ru, options = {}) integrals_key = :uah_integral fractions_key = :uah_fraction money_gender = MONEY_GENDERS[:uah] money(amount, locale, integrals_key, fractions_key, money_gender, false, false, options) end
Выводит сумму прописью в рублях по количеству копеек
kopeek(343) #=> "три рубля 43 копейки"
Опции¶ ↑
-
:always_show_fraction
- true/false. позволяет принудительно отображать 0 в качестве дробной части для целого числа -
:fraction_formatter
- строка. формат отображения числа после точки, например '%d'
# File lib/ru_propisju.rb, line 520 def kopeek(amount, locale = :ru, options = {}) rublej(amount / 100.0, locale, options) end
# File lib/ru_propisju.rb, line 538 def money(amount, locale, integrals_key, fractions_key, money_gender, fraction_as_number = false, integrals_as_number = false, options = {}) options[:integrals_formatter] ||= '%d' options[:fraction_formatter] ||= '%d' options[:integrals_delimiter] ||= '' options[:always_show_fraction] ||= false locale_data = pick_locale(TRANSLATIONS, locale) integrals = locale_data[integrals_key] fractions = locale_data[fractions_key] return zero(locale_data, integrals, fractions, fraction_as_number, integrals_as_number, options) if amount.zero? parts = [] unless amount.to_i == 0 if integrals_as_number parts << format_integral(amount.to_i, options) << choose_plural(amount.to_i, integrals) else parts << propisju_int(amount.to_i, money_gender, integrals, locale) end end if amount.kind_of?(Float) || amount.kind_of?(BigDecimal) remainder = (amount.divmod(1)[1]*100).round if remainder == 100 parts = [propisju_int(amount.to_i + 1, money_gender, integrals, locale)] parts << zero_fraction(locale, money_gender, fractions, fraction_as_number, options) if options[:always_show_fraction] else if fraction_as_number kop = remainder.to_i if (!kop.zero? || options[:always_show_fraction]) parts << format(options[:fraction_formatter], kop) << choose_plural(kop, fractions) end else parts << propisju_int(remainder.to_i, money_gender, fractions, locale) end end else parts << zero_fraction(locale, money_gender, fractions, fraction_as_number, options) if options[:always_show_fraction] end parts.join(' ') end
Выбирает корректный вариант числительного в зависимости от рода и числа и оформляет сумму прописью
propisju(243, 1) => "двести сорок три" propisju(221, 2) => "двести двадцать одна"
# File lib/ru_propisju.rb, line 435 def propisju(amount, gender, locale = :ru) if amount.kind_of?(Integer) propisju_int(amount, gender, [], locale) else # также сработает для Decimal, дробные десятичные числительные в долях поэтому женского рода propisju_float(amount, locale) end end
Выводит сумму данного существительного прописью и выбирает правильное число и падеж
RuPropisju.propisju_shtuk(21, 3, "колесо", "колеса", "колес") #=> "двадцать одно колесо" RuPropisju.propisju_shtuk(21, 1, "мужик", "мужика", "мужиков") #=> "двадцать один мужик"
# File lib/ru_propisju.rb, line 528 def propisju_shtuk(items, gender, forms, locale = :ru) elements = if (items == items.to_i) [propisju(items, gender, locale), choose_plural(items, forms)] else [propisju(items, gender, locale), forms[1]] end elements.join(" ") end
Выводит целое или дробное число как сумму в рублях прописью
rublej(345.2) #=> "триста сорок пять рублей 20 копеек"
Опции¶ ↑
-
:always_show_fraction
- true/false. позволяет принудительно отображать 0 в качестве дробной части для целого числа -
:fraction_formatter
- строка. формат отображения числа после точки, например '%d'
# File lib/ru_propisju.rb, line 394 def rublej(amount, locale = :ru, options = {}) integrals_key = :rub_integral fractions_key = :rub_fraction money_gender = MONEY_GENDERS[:rub] money(amount, locale, integrals_key, fractions_key, money_gender, true, false, options) end
Выводит целое или дробное число как сумму в расширенном формате
rublej_extended_format(345.2) #=> "345 рублей 20 копеек (триста сорок пять рублей 20 копеек)"
Опции¶ ↑
-
:always_show_fraction
- true/false. позволяет принудительно отображать 0 в качестве дробной части для целого числа -
:fraction_formatter
- строка. формат отображения числа после точки, например '%d' -
:integrals_formatter
- строка. формат отображения целого числа, например '%d' -
:integrals_delimiter
- строка. разделитель разрядов для целой части числа, например ' '
# File lib/ru_propisju.rb, line 411 def rublej_extended_format(amount, locale = :ru, options = {}) "#{digit_rublej(amount, locale, options)} (#{rublej(amount, locale, options)})" end
Выводит целое или дробное число как сумму в сомах прописью
som(32) #=> "тридцать два сома"
Опции¶ ↑
-
:always_show_fraction
- true/false. позволяет принудительно отображать 0 в качестве дробной части для целого числа
# File lib/ru_propisju.rb, line 505 def som(amount, locale = :ru, options = {}) integrals_key = :kgs_integral fractions_key = :kgs_fraction money_gender = MONEY_GENDERS[:kgs] money(amount, locale, integrals_key, fractions_key, money_gender, false, false, options) end
Выводит целое или дробное число как сумму в тенге прописью
tenge(32) #=> "тридцать два тенге"
Опции¶ ↑
-
:always_show_fraction
- true/false. позволяет принудительно отображать 0 в качестве дробной части для целого числа
# File lib/ru_propisju.rb, line 491 def tenge(amount, locale = :ru, options = {}) integrals_key = :kzt_integral fractions_key = :kzt_fraction money_gender = MONEY_GENDERS[:kzt] money(amount, locale, integrals_key, fractions_key, money_gender, false, false, options) end
Private Instance Methods
Cоставляет число прописью для чисел до тысячи
# File lib/ru_propisju.rb, line 602 def compose_ordinal(remaining_amount_or_nil, gender, item_forms = [], locale = :ru) remaining_amount = remaining_amount_or_nil.to_i locale = locale.to_s # Ноль чего-то # return "ноль %s" % item_forms[3] if remaining_amount_or_nil.zero? rest, rest1, chosen_ordinal, ones, tens, hundreds = [nil]*6 rest = remaining_amount % 1000 remaining_amount = remaining_amount / 1000 if rest.zero? # последние три знака нулевые return item_forms[2] end locale_root = pick_locale(TRANSLATIONS, locale) # начинаем подсчет с Rest # сотни hundreds = locale_root[(rest / 100).to_i * 100] # десятки rest = rest % 100 rest1 = rest / 10 # единички ones = "" tens = locale_root[rest1 == 1 ? rest : rest1 * 10] # индекс выбранной формы chosen_ordinal = 2 if rest1 < 1 || rest1 > 1 # единицы value = locale_root[rest % 10] # если попался хэш, делаем выбор согласно рода value = value[gender] if value.kind_of? Hash ones = value case rest % 10 when 1 then chosen_ordinal = 0 # индекс формы меняется when 2..4 then chosen_ordinal = 1 # индекс формы меняется end end plural = [ hundreds, tens, ones, item_forms[chosen_ordinal], ].compact.reject(&:empty?).join(' ').strip return plural end
# File lib/ru_propisju.rb, line 816 def delimited_number(number, delimiter) return number.to_s if delimiter == '' number.to_s.gsub!(/(\d)(?=(\d\d\d)+(?!\d))/) do |digit_to_delimit| "#{digit_to_delimit}#{delimiter}" end || number.to_s end
# File lib/ru_propisju.rb, line 811 def format_integral(number, options) formatted_number = format(options[:integrals_formatter], number) delimited_number(formatted_number, options[:integrals_delimiter]) end
# File lib/ru_propisju.rb, line 824 def pick_locale(from_hash, locale) return from_hash[locale.to_s] if from_hash.has_key?(locale.to_s) raise UnknownLocale, "Unknown locale #{locale.inspect}" end
Выдает сумму прописью с учетом дробной доли. Дробная доля округляется до миллионной, или (если дробная доля оканчивается на нули) до ближайшей доли или точки ( 500 тысячных округляется до 5 десятых, 30.0000 до 30).
# File lib/ru_propisju.rb, line 736 def propisju_float(num, locale = :ru) locale_root = pick_locale(DECIMALS, locale) source_expression = locale_root[:prefix][0] target_prefix = locale_root[:prefix][1] words = locale_root[:source_words].map do |e| [ e, e.gsub(/#{source_expression}$/, target_prefix), e.gsub(/#{source_expression}$/, target_prefix), ] end.freeze # Укорачиваем до триллионной доли formatted = num.to_s[/^\d+(\.\d{0,#{words.length}})?/].gsub(/0+$/, '') wholes, decimals = formatted.split(".") return propisju_int(wholes.to_i, 1, [], locale) if decimals.to_i.zero? whole_st = propisju_shtuk(wholes.to_i, 2, words[0], locale) rem_st = propisju_shtuk(decimals.to_i, 2, words[decimals.length], locale) [whole_st, rem_st].compact.join(" ") end
Выполняет преобразование числа из цифрого вида в символьное
amount - числительное gender = 1 - мужской, = 2 - женский, = 3 - средний one_item - именительный падеж единственного числа (= 1) two_items - родительный падеж единственного числа (= 2-4) five_items - родительный падеж множественного числа ( = 5-10)
Примерно так:
propisju(42, 1, ["сволочь", "сволочи", "сволочей"]) # => "сорок две сволочи"
# File lib/ru_propisju.rb, line 770 def propisju_int(amount, gender = 1, item_forms = [], locale = :ru) locale_root = pick_locale(TRANSLATIONS, locale) # zero! if amount.zero? return [locale_root['0'], item_forms[-1]].compact.join(' ') end fractions = [ [:trillions, 1_000_000_000_000], [:billions, 1_000_000_000], [:millions, 1_000_000], [:thousands, 1_000], ] parts = fractions.map do | name, multiplier | [name, fraction = (amount / multiplier) % 1000] end if amount / fractions.first.last >= 1000 raise "Amount is too large" end # Единицы обрабатываем отдельно ones = amount % 1000 # Составляем простые тысячные доли parts_in_writing = parts.reject do | part | part[1].zero? end.map do | name, fraction | thousandth_gender = (name == :thousands) ? 2 : 1 compose_ordinal(fraction, thousandth_gender, locale_root[name], locale) end # И только единицы обрабатываем с переданными параметрами parts_in_writing.push(compose_ordinal(ones, gender, item_forms, locale)) parts_in_writing.compact.join(' ') end
# File lib/ru_propisju.rb, line 585 def zero(locale_data, integrals, fractions, fraction_as_number, integrals_as_number, options) integ = integrals_as_number ? format(options[:integrals_formatter], 0) : locale_data['0'] frac = fraction_as_number ? format(options[:fraction_formatter], 0) : locale_data['0'] parts = [integ , integrals[-1], frac, fractions[-1]] parts.join(' ') end
# File lib/ru_propisju.rb, line 592 def zero_fraction(locale, money_gender, fractions, fraction_as_number, options) if fraction_as_number [format(options[:fraction_formatter], 0), choose_plural(0, fractions)] else propisju_int(0, money_gender, fractions, locale) end end