module English # = English Nouns Number Inflection. # # This module provides english singular <-> plural noun inflections. module Inflect @singular_of = {} @plural_of = {} @singular_rules = [] @plural_rules = [] # This class provides the DSL for creating inflections, you can add additional rules. # Examples: # # word "ox", "oxen" # word "octopus", "octopi" # word "man", "men" # # rule "lf", "lves" # # word "equipment" # # Rules are evaluated by size, so rules you add to override specific cases should be longer than the rule # it overrides. For instance, if you want "pta" to pluralize to "ptas", even though a general purpose rule # for "ta" => "tum" already exists, simply add a new rule for "pta" => "ptas", and it will automatically win # since it is longer than the old rule. # # Also, single-word exceptions win over general words ("ox" pluralizes to "oxen", because it's a single word # exception, even though "fox" pluralizes to "foxes") class << self # Define a general two-way exception. # # This also defines a general rule, so foo_child will correctly become # foo_children. # # Whole words also work if they are capitalized (Goose => Geese). def word(singular, plural=nil) plural = singular unless plural singular_word(singular, plural) plural_word(singular, plural) rule(singular, plural) end # Define a singularization exception. def singular_word(singular, plural) @singular_of[plural] = singular @singular_of[plural.capitalize] = singular.capitalize end # Define a pluralization exception. def plural_word(singular, plural) @plural_of[singular] = plural @plural_of[singular.capitalize] = plural.capitalize end # Define a general rule. def rule(singular, plural) singular_rule(singular, plural) plural_rule(singular, plural) end # Define a singularization rule. def singular_rule(singular, plural) @singular_rules << [singular, plural] end # Define a plurualization rule. def plural_rule(singular, plural) @plural_rules << [singular, plural] end # Read prepared singularization rules. def singularization_rules if defined?(@singularization_regex) && @singularization_regex return [@singularization_regex, @singularization_hash] end # No sorting needed: Regexen match on longest string @singularization_regex = Regexp.new("(" + @singular_rules.map {|s,p| p}.join("|") + ")$", "i") @singularization_hash = Hash[*@singular_rules.flatten].invert [@singularization_regex, @singularization_hash] end # Read prepared singularization rules. #def singularization_rules # return @singularization_rules if @singularization_rules # sorted = @singular_rules.sort_by{ |s, p| "#{p}".size }.reverse # @singularization_rules = sorted.collect do |s, p| # [ /#{p}$/, "#{s}" ] # end #end # Read prepared pluralization rules. def pluralization_rules if defined?(@pluralization_regex) && @pluralization_regex return [@pluralization_regex, @pluralization_hash] end @pluralization_regex = Regexp.new("(" + @plural_rules.map {|s,p| s}.join("|") + ")$", "i") @pluralization_hash = Hash[*@plural_rules.flatten] [@pluralization_regex, @pluralization_hash] end # Read prepared pluralization rules. #def pluralization_rules # return @pluralization_rules if @pluralization_rules # sorted = @plural_rules.sort_by{ |s, p| "#{s}".size }.reverse # @pluralization_rules = sorted.collect do |s, p| # [ /#{s}$/, "#{p}" ] # end #end # def singular_of ; @singular_of ; end # def plural_of ; @plural_of ; end # Convert an English word from plurel to singular. # # "boys".singular #=> boy # "tomatoes".singular #=> tomato # def singular(word) return "" if word == "" if result = singular_of[word] return result.dup end result = word.dup regex, hash = singularization_rules result.sub!(regex) {|m| hash[m]} singular_of[word] = result return result #singularization_rules.each do |(match, replacement)| # break if result.gsub!(match, replacement) #end #return result end # Alias for #singular (a Railism). # alias_method(:singularize, :singular) # Convert an English word from singular to plurel. # # "boy".plural #=> boys # "tomato".plural #=> tomatoes # def plural(word) return "" if word == "" if result = plural_of[word] return result.dup end #return self.dup if /s$/ =~ self # ??? result = word.dup regex, hash = pluralization_rules result.sub!(regex) {|m| hash[m]} plural_of[word] = result return result #pluralization_rules.each do |(match, replacement)| # break if result.gsub!(match, replacement) #end #return result end # Alias for #plural (a Railism). alias_method(:pluralize, :plural) # Clear all rules. def clear(type = :all) if type == :singular || type == :all @singular_of = {} @singular_rules = [] @singularization_rules, @singularization_regex = nil, nil end if type == :plural || type == :all @singular_of = {} @singular_rules = [] @singularization_rules, @singularization_regex = nil, nil end end end # One argument means singular and plural are the same. word 'equipment' word 'information' word 'money' word 'species' word 'series' word 'fish' word 'sheep' word 'moose' word 'hovercraft' word 'news' word 'rice' word 'plurals' # Two arguments defines a singular and plural exception. word 'Swiss' , 'Swiss' word 'alias' , 'aliases' word 'analysis' , 'analyses' #word 'axis' , 'axes' word 'basis' , 'bases' word 'buffalo' , 'buffaloes' word 'child' , 'children' #word 'cow' , 'kine' word 'crisis' , 'crises' word 'criterion' , 'criteria' word 'datum' , 'data' word 'goose' , 'geese' word 'hive' , 'hives' word 'index' , 'indices' word 'life' , 'lives' word 'louse' , 'lice' word 'man' , 'men' word 'matrix' , 'matrices' word 'medium' , 'media' word 'mouse' , 'mice' word 'movie' , 'movies' word 'octopus' , 'octopi' word 'ox' , 'oxen' word 'person' , 'people' word 'potato' , 'potatoes' word 'quiz' , 'quizzes' word 'shoe' , 'shoes' word 'status' , 'statuses' word 'testis' , 'testes' word 'thesis' , 'theses' word 'thief' , 'thieves' word 'tomato' , 'tomatoes' word 'torpedo' , 'torpedoes' word 'vertex' , 'vertices' word 'virus' , 'viri' word 'wife' , 'wives' # One-way singularization exception (convert plural to singular). singular_word 'cactus', 'cacti' # One-way pluralizaton exception (convert singular to plural). plural_word 'axis', 'axes' # General rules. rule 'rf' , 'rves' rule 'ero' , 'eroes' rule 'ch' , 'ches' rule 'sh' , 'shes' rule 'ss' , 'sses' #rule 'ess' , 'esses' rule 'ta' , 'tum' rule 'ia' , 'ium' rule 'ra' , 'rum' rule 'ay' , 'ays' rule 'ey' , 'eys' rule 'oy' , 'oys' rule 'uy' , 'uys' rule 'y' , 'ies' rule 'x' , 'xes' rule 'lf' , 'lves' rule 'ffe' , 'ffes' rule 'af' , 'aves' rule 'us' , 'uses' rule 'ouse' , 'ouses' rule 'osis' , 'oses' rule 'ox' , 'oxes' rule '' , 's' # One-way singular rules. singular_rule 'of' , 'ofs' # proof singular_rule 'o' , 'oes' # hero, heroes #singular_rule 'f' , 'ves' # One-way plural rules. plural_rule 's' , 'ses' plural_rule 'ive' , 'ives' # don't want to snag wife plural_rule 'fe' , 'ves' # don't want to snag perspectives end end class String def singular English::Inflect.singular(self) end alias_method(:singularize, :singular) def plural English::Inflect.plural(self) end alias_method(:pluralize, :plural) end