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