inflections.go (8234B)
1 /* 2 Package inflection pluralizes and singularizes English nouns. 3 4 inflection.Plural("person") => "people" 5 inflection.Plural("Person") => "People" 6 inflection.Plural("PERSON") => "PEOPLE" 7 8 inflection.Singular("people") => "person" 9 inflection.Singular("People") => "Person" 10 inflection.Singular("PEOPLE") => "PERSON" 11 12 inflection.Plural("FancyPerson") => "FancydPeople" 13 inflection.Singular("FancyPeople") => "FancydPerson" 14 15 Standard rules are from Rails's ActiveSupport (https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflections.rb) 16 17 If you want to register more rules, follow: 18 19 inflection.AddUncountable("fish") 20 inflection.AddIrregular("person", "people") 21 inflection.AddPlural("(bu)s$", "${1}ses") # "bus" => "buses" / "BUS" => "BUSES" / "Bus" => "Buses" 22 inflection.AddSingular("(bus)(es)?$", "${1}") # "buses" => "bus" / "Buses" => "Bus" / "BUSES" => "BUS" 23 */ 24 package inflection 25 26 import ( 27 "regexp" 28 "strings" 29 ) 30 31 type inflection struct { 32 regexp *regexp.Regexp 33 replace string 34 } 35 36 // Regular is a regexp find replace inflection 37 type Regular struct { 38 find string 39 replace string 40 } 41 42 // Irregular is a hard replace inflection, 43 // containing both singular and plural forms 44 type Irregular struct { 45 singular string 46 plural string 47 } 48 49 // RegularSlice is a slice of Regular inflections 50 type RegularSlice []Regular 51 52 // IrregularSlice is a slice of Irregular inflections 53 type IrregularSlice []Irregular 54 55 var pluralInflections = RegularSlice{ 56 {"([a-z])$", "${1}s"}, 57 {"s$", "s"}, 58 {"^(ax|test)is$", "${1}es"}, 59 {"(octop|vir)us$", "${1}i"}, 60 {"(octop|vir)i$", "${1}i"}, 61 {"(alias|status)$", "${1}es"}, 62 {"(bu)s$", "${1}ses"}, 63 {"(buffal|tomat)o$", "${1}oes"}, 64 {"([ti])um$", "${1}a"}, 65 {"([ti])a$", "${1}a"}, 66 {"sis$", "ses"}, 67 {"(?:([^f])fe|([lr])f)$", "${1}${2}ves"}, 68 {"(hive)$", "${1}s"}, 69 {"([^aeiouy]|qu)y$", "${1}ies"}, 70 {"(x|ch|ss|sh)$", "${1}es"}, 71 {"(matr|vert|ind)(?:ix|ex)$", "${1}ices"}, 72 {"^(m|l)ouse$", "${1}ice"}, 73 {"^(m|l)ice$", "${1}ice"}, 74 {"^(ox)$", "${1}en"}, 75 {"^(oxen)$", "${1}"}, 76 {"(quiz)$", "${1}zes"}, 77 } 78 79 var singularInflections = RegularSlice{ 80 {"s$", ""}, 81 {"(ss)$", "${1}"}, 82 {"(n)ews$", "${1}ews"}, 83 {"([ti])a$", "${1}um"}, 84 {"((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$", "${1}sis"}, 85 {"(^analy)(sis|ses)$", "${1}sis"}, 86 {"([^f])ves$", "${1}fe"}, 87 {"(hive)s$", "${1}"}, 88 {"(tive)s$", "${1}"}, 89 {"([lr])ves$", "${1}f"}, 90 {"([^aeiouy]|qu)ies$", "${1}y"}, 91 {"(s)eries$", "${1}eries"}, 92 {"(m)ovies$", "${1}ovie"}, 93 {"(c)ookies$", "${1}ookie"}, 94 {"(x|ch|ss|sh)es$", "${1}"}, 95 {"^(m|l)ice$", "${1}ouse"}, 96 {"(bus)(es)?$", "${1}"}, 97 {"(o)es$", "${1}"}, 98 {"(shoe)s$", "${1}"}, 99 {"(cris|test)(is|es)$", "${1}is"}, 100 {"^(a)x[ie]s$", "${1}xis"}, 101 {"(octop|vir)(us|i)$", "${1}us"}, 102 {"(alias|status)(es)?$", "${1}"}, 103 {"^(ox)en", "${1}"}, 104 {"(vert|ind)ices$", "${1}ex"}, 105 {"(matr)ices$", "${1}ix"}, 106 {"(quiz)zes$", "${1}"}, 107 {"(database)s$", "${1}"}, 108 } 109 110 var irregularInflections = IrregularSlice{ 111 {"person", "people"}, 112 {"man", "men"}, 113 {"child", "children"}, 114 {"sex", "sexes"}, 115 {"move", "moves"}, 116 {"mombie", "mombies"}, 117 } 118 119 var uncountableInflections = []string{"equipment", "information", "rice", "money", "species", "series", "fish", "sheep", "jeans", "police"} 120 121 var compiledPluralMaps []inflection 122 var compiledSingularMaps []inflection 123 124 func compile() { 125 compiledPluralMaps = []inflection{} 126 compiledSingularMaps = []inflection{} 127 for _, uncountable := range uncountableInflections { 128 inf := inflection{ 129 regexp: regexp.MustCompile("^(?i)(" + uncountable + ")$"), 130 replace: "${1}", 131 } 132 compiledPluralMaps = append(compiledPluralMaps, inf) 133 compiledSingularMaps = append(compiledSingularMaps, inf) 134 } 135 136 for _, value := range irregularInflections { 137 infs := []inflection{ 138 inflection{regexp: regexp.MustCompile(strings.ToUpper(value.singular) + "$"), replace: strings.ToUpper(value.plural)}, 139 inflection{regexp: regexp.MustCompile(strings.Title(value.singular) + "$"), replace: strings.Title(value.plural)}, 140 inflection{regexp: regexp.MustCompile(value.singular + "$"), replace: value.plural}, 141 } 142 compiledPluralMaps = append(compiledPluralMaps, infs...) 143 } 144 145 for _, value := range irregularInflections { 146 infs := []inflection{ 147 inflection{regexp: regexp.MustCompile(strings.ToUpper(value.plural) + "$"), replace: strings.ToUpper(value.singular)}, 148 inflection{regexp: regexp.MustCompile(strings.Title(value.plural) + "$"), replace: strings.Title(value.singular)}, 149 inflection{regexp: regexp.MustCompile(value.plural + "$"), replace: value.singular}, 150 } 151 compiledSingularMaps = append(compiledSingularMaps, infs...) 152 } 153 154 for i := len(pluralInflections) - 1; i >= 0; i-- { 155 value := pluralInflections[i] 156 infs := []inflection{ 157 inflection{regexp: regexp.MustCompile(strings.ToUpper(value.find)), replace: strings.ToUpper(value.replace)}, 158 inflection{regexp: regexp.MustCompile(value.find), replace: value.replace}, 159 inflection{regexp: regexp.MustCompile("(?i)" + value.find), replace: value.replace}, 160 } 161 compiledPluralMaps = append(compiledPluralMaps, infs...) 162 } 163 164 for i := len(singularInflections) - 1; i >= 0; i-- { 165 value := singularInflections[i] 166 infs := []inflection{ 167 inflection{regexp: regexp.MustCompile(strings.ToUpper(value.find)), replace: strings.ToUpper(value.replace)}, 168 inflection{regexp: regexp.MustCompile(value.find), replace: value.replace}, 169 inflection{regexp: regexp.MustCompile("(?i)" + value.find), replace: value.replace}, 170 } 171 compiledSingularMaps = append(compiledSingularMaps, infs...) 172 } 173 } 174 175 func init() { 176 compile() 177 } 178 179 // AddPlural adds a plural inflection 180 func AddPlural(find, replace string) { 181 pluralInflections = append(pluralInflections, Regular{find, replace}) 182 compile() 183 } 184 185 // AddSingular adds a singular inflection 186 func AddSingular(find, replace string) { 187 singularInflections = append(singularInflections, Regular{find, replace}) 188 compile() 189 } 190 191 // AddIrregular adds an irregular inflection 192 func AddIrregular(singular, plural string) { 193 irregularInflections = append(irregularInflections, Irregular{singular, plural}) 194 compile() 195 } 196 197 // AddUncountable adds an uncountable inflection 198 func AddUncountable(values ...string) { 199 uncountableInflections = append(uncountableInflections, values...) 200 compile() 201 } 202 203 // GetPlural retrieves the plural inflection values 204 func GetPlural() RegularSlice { 205 plurals := make(RegularSlice, len(pluralInflections)) 206 copy(plurals, pluralInflections) 207 return plurals 208 } 209 210 // GetSingular retrieves the singular inflection values 211 func GetSingular() RegularSlice { 212 singulars := make(RegularSlice, len(singularInflections)) 213 copy(singulars, singularInflections) 214 return singulars 215 } 216 217 // GetIrregular retrieves the irregular inflection values 218 func GetIrregular() IrregularSlice { 219 irregular := make(IrregularSlice, len(irregularInflections)) 220 copy(irregular, irregularInflections) 221 return irregular 222 } 223 224 // GetUncountable retrieves the uncountable inflection values 225 func GetUncountable() []string { 226 uncountables := make([]string, len(uncountableInflections)) 227 copy(uncountables, uncountableInflections) 228 return uncountables 229 } 230 231 // SetPlural sets the plural inflections slice 232 func SetPlural(inflections RegularSlice) { 233 pluralInflections = inflections 234 compile() 235 } 236 237 // SetSingular sets the singular inflections slice 238 func SetSingular(inflections RegularSlice) { 239 singularInflections = inflections 240 compile() 241 } 242 243 // SetIrregular sets the irregular inflections slice 244 func SetIrregular(inflections IrregularSlice) { 245 irregularInflections = inflections 246 compile() 247 } 248 249 // SetUncountable sets the uncountable inflections slice 250 func SetUncountable(inflections []string) { 251 uncountableInflections = inflections 252 compile() 253 } 254 255 // Plural converts a word to its plural form 256 func Plural(str string) string { 257 for _, inflection := range compiledPluralMaps { 258 if inflection.regexp.MatchString(str) { 259 return inflection.regexp.ReplaceAllString(str, inflection.replace) 260 } 261 } 262 return str 263 } 264 265 // Singular converts a word to its singular form 266 func Singular(str string) string { 267 for _, inflection := range compiledSingularMaps { 268 if inflection.regexp.MatchString(str) { 269 return inflection.regexp.ReplaceAllString(str, inflection.replace) 270 } 271 } 272 return str 273 }