lib/figures/german.rb in figures-0.1.0 vs lib/figures/german.rb in figures-0.2.0

- old
+ new

@@ -1,89 +1,137 @@ module Figures class German - WORDS = { - copula: "und", - digits: %w[null ein zwei drei vier fünf sechs sieben acht neun], - tens: %w[eine zehn zwanzig dreißig vierzig fünfzig sechzig siebzig achzig neunzig], - exponents: %w[hundert tausend million milliarden billion billiarden trillion trilliarden quadrillionen quadrilliarden quintillion sextillion sextilliarden] + UNITS = %w{ eins zwei drei vier fünf sechs sieben acht neun }.freeze + + PREFIXES = { + units: %w{ tausend mi bi tri quadri quinti sexti septi okti noni }, + union_units: %w{ un duo tre quattuor quinqua se septe okto nove }, + union_tens: %w{ dezi viginti triginta quadraginta quinquaginta sexaginta septuaginta oktoginta nonaginta }, + union_hundreds: %w{ zenti duzenti trezenti quadringenti quingenti seszenti septingenti oktingenti nongenti } }.freeze + EXCEPTIONS = { + /^eins(hundert|tausend)/ => 'ein\1', + /^eins\s/ => 'eine ', + /einsund/ => 'einund', + 'einszehn' => 'elf', + 'zweizehn' => 'zwölf', + 'sechszehn' => 'sechzehn', + 'siebenzehn' => 'siebzehn', + 'zweizig' => 'zwanzig', + 'dreizig' => 'dreißig', + 'sechszig' => 'sechzig', + 'siebenzig' => 'siebzig' + }.freeze + + attr_reader :number + def initialize(number) @number = number.to_i end def parse - return WORDS[:digits][@number] if @number < 10 && @number >= 0 + return 'null' if number == 0 - number_string = @number.to_s.reverse.scan(/.{1,3}/).map.with_index{ |number_part, index| - parse_triple(number_part.reverse.to_i, index) - }.reverse.join + triples = split_into_reverse_triples(number) - number_string.sub! /^und/, '' # TODO investigate + word = triples.each_with_index.reduce('') do |result, (triple, index)| + triple_word = triple_to_word(triple, index) + result.prepend(triple_word) + end.strip - if @number < 0 - "minus #{number_string}" - else - number_string - end + number < 0 ? "minus #{word}" : word end - def parse_triple(number, triple_index) - temp_number = number.abs - number_word = "" - temp_tens = "" + private - return 'eins' if temp_number == 1 && triple_index == 0 # FIXME + def triples_count + @triples_count ||= split_into_reverse_triples(number).count + end - while temp_number > 0 - decimal_power = Math.log10(temp_number).floor - number_base = 10 ** decimal_power - number_tail = temp_number - (temp_number % number_base) - digit = number_tail / number_base + def split_into_reverse_triples(number) + @reverse_triples ||= number.abs.to_s.reverse.scan(/.{1,3}/).map(&:reverse) + end - copula = ((digit > 1) ? WORDS[:copula] : "") - leading_single = (triple_index >= 2 && digit == 1) ? WORDS[:tens][0].to_s : WORDS[:digits][digit].to_s + def triple_to_word(triple, triple_index) + hundred_digit, ten_digit, unit_digit = split_triple(triple) - if decimal_power == 2 - number_word << WORDS[:digits][digit].to_s << WORDS[:exponents][0].to_s - end + word = [ + hundred(hundred_digit), + unit(unit_digit), + copula(unit_digit, ten_digit), + ten(ten_digit) + ].join - if decimal_power == 0 && !temp_tens.empty? - number_word << WORDS[:digits][digit].to_s - end + word = append_exponent_identifier(word, triple_index) + apply_exceptions(word) + end - if decimal_power == 0 && temp_tens.empty? - number_word << copula << leading_single - end + # splits up a triple into hundreds, tens and unit position + def split_triple(triple) + triple.match(/\A(\d)??(\d)??(\d)\z/).captures.map(&:to_i) + end - if decimal_power == 1 - temp_tens << copula << WORDS[:tens][digit].to_s - end + # returns the word for the given unit number + def unit(digit) + return '' if digit.zero? + UNITS[digit - 1] + end - temp_number = temp_number - number_tail + # returns the copula between unit position and tens + def copula(unit_digit, ten_digit) + 'und' if ten_digit > 1 && !unit_digit.zero? + end + + # returns the word for the given tens digit + def ten(digit) + case digit + when 0 then '' + when 1 then 'zehn' + else unit(digit) + 'zig' end + end - number_word << temp_tens - - if triple_index > 0 - number_word << WORDS[:exponents][triple_index].to_s + # returns the word for the given hundreds number + def hundred(digit) + case digit + when 0 then '' + else unit(digit) + 'hundert' end + end - number_word = handle_exceptions(number_word) - number_word = remove_wrong_leadings(number_word) + # adds the exponent word to the triple word + # e.g. tausend for the second triple (index = 1) + # Million for the third triple (index = 2) + # Milliarde for the fourth triple (index = 3) + # + # indexes => PREFIXES index + # 2,3 => 1; 4,5 => 2; 6,7 => 3; ... : floored division by 2 + # etc. + def append_exponent_identifier(word, index) + return word if word.empty? || index.zero? || triples_count == 1 - number_word + if index == 1 + word + PREFIXES[:units][0] + elsif index.even? + pluralize_if_plural(word + ' ' + (PREFIXES[:units][index / 2] + "llion ").capitalize) + elsif index.odd? + pluralize_if_plural(word + ' ' + (PREFIXES[:units][index / 2] + "lliarde ").capitalize) + end end - def handle_exceptions(word) - word = word.gsub('einzehn', 'elf') - word = word.gsub('zweizehn', 'zwölf') - word = word.gsub('sechszehn', 'sechzehn') - word = word.gsub('siebenzehn', 'siebzehn') + # pluralizes exponent identifiers + def pluralize_if_plural(word) + word =~ /^eins / ? word : word.sub(/e? $/, 'en ') end - def remove_wrong_leadings(word) - word.gsub(/^(und|null)/, '') + # replaces all exceptions in the number word + def apply_exceptions(word) + EXCEPTIONS.each do |exception, replacement| + word.sub!(exception, replacement) + end + + word end - + end -end \ No newline at end of file +end