18 // baseAcronyms comes from https://en.wikipedia.org/wiki/List_of_information_technology_acronymss
19 const baseAcronyms = `JSON,JWT,ID,UUID,SQL,ACK,ACL,ADSL,AES,ANSI,API,ARP,ATM,BGP,BSS,CAT,CCITT,CHAP,CIDR,CIR,CLI,CPE,CPU,CRC,CRT,CSMA,CMOS,DCE,DEC,DES,DHCP,DNS,DRAM,DSL,DSLAM,DTE,DMI,EHA,EIA,EIGRP,EOF,ESS,FCC,FCS,FDDI,FTP,GBIC,gbps,GEPOF,HDLC,HTTP,HTTPS,IANA,ICMP,IDF,IDS,IEEE,IETF,IMAP,IP,IPS,ISDN,ISP,kbps,LACP,LAN,LAPB,LAPF,LLC,MAC,MAN,Mbps,MC,MDF,MIB,MoCA,MPLS,MTU,NAC,NAT,NBMA,NIC,NRZ,NRZI,NVRAM,OSI,OSPF,OUI,PAP,PAT,PC,PIM,PIM,PCM,PDU,POP3,POP,POTS,PPP,PPTP,PTT,PVST,RADIUS,RAM,RARP,RFC,RIP,RLL,ROM,RSTP,RTP,RCP,SDLC,SFD,SFP,SLARP,SLIP,SMTP,SNA,SNAP,SNMP,SOF,SRAM,SSH,SSID,STP,SYN,TDM,TFTP,TIA,TOFU,UDP,URL,URI,USB,UTP,VC,VLAN,VLSM,VPN,W3C,WAN,WEP,WiFi,WPA,WWW`
21 // Rule used by rulesets
28 // Ruleset a Ruleset is the config of pluralization rules
29 // you can extend the rules with the Add* methods
31 uncountables map[string]bool
38 // NewRuleset creates a blank ruleset. Unless you are going to
39 // build your own rules from scratch you probably
40 // won't need this and can just use the defaultRuleset
41 // via the global inflect.* methods
42 func NewRuleset() *Ruleset {
44 rs.uncountables = make(map[string]bool)
45 rs.plurals = make([]*Rule, 0)
46 rs.singulars = make([]*Rule, 0)
47 rs.humans = make([]*Rule, 0)
48 rs.acronyms = make([]*Rule, 0)
52 // NewDefaultRuleset creates a new ruleset and load it with the default
53 // set of common English pluralization rules
54 func NewDefaultRuleset() *Ruleset {
56 rs.AddPlural("movie", "movies")
57 rs.AddPlural("s", "s")
58 rs.AddPlural("testis", "testes")
59 rs.AddPlural("axis", "axes")
60 rs.AddPlural("octopus", "octopi")
61 rs.AddPlural("virus", "viri")
62 rs.AddPlural("octopi", "octopi")
63 rs.AddPlural("viri", "viri")
64 rs.AddPlural("alias", "aliases")
65 rs.AddPlural("status", "statuses")
66 rs.AddPlural("Status", "Statuses")
67 rs.AddPlural("campus", "campuses")
68 rs.AddPlural("bus", "buses")
69 rs.AddPlural("buffalo", "buffaloes")
70 rs.AddPlural("tomato", "tomatoes")
71 rs.AddPlural("tum", "ta")
72 rs.AddPlural("ium", "ia")
73 rs.AddPlural("ta", "ta")
74 rs.AddPlural("ia", "ia")
75 rs.AddPlural("sis", "ses")
76 rs.AddPlural("lf", "lves")
77 rs.AddPlural("rf", "rves")
78 rs.AddPlural("afe", "aves")
79 rs.AddPlural("bfe", "bves")
80 rs.AddPlural("cfe", "cves")
81 rs.AddPlural("dfe", "dves")
82 rs.AddPlural("efe", "eves")
83 rs.AddPlural("gfe", "gves")
84 rs.AddPlural("hfe", "hves")
85 rs.AddPlural("ife", "ives")
86 rs.AddPlural("jfe", "jves")
87 rs.AddPlural("kfe", "kves")
88 rs.AddPlural("lfe", "lves")
89 rs.AddPlural("mfe", "mves")
90 rs.AddPlural("nfe", "nves")
91 rs.AddPlural("ofe", "oves")
92 rs.AddPlural("pfe", "pves")
93 rs.AddPlural("qfe", "qves")
94 rs.AddPlural("rfe", "rves")
95 rs.AddPlural("sfe", "sves")
96 rs.AddPlural("tfe", "tves")
97 rs.AddPlural("ufe", "uves")
98 rs.AddPlural("vfe", "vves")
99 rs.AddPlural("wfe", "wves")
100 rs.AddPlural("xfe", "xves")
101 rs.AddPlural("yfe", "yves")
102 rs.AddPlural("zfe", "zves")
103 rs.AddPlural("hive", "hives")
104 rs.AddPlural("quy", "quies")
105 rs.AddPlural("by", "bies")
106 rs.AddPlural("cy", "cies")
107 rs.AddPlural("dy", "dies")
108 rs.AddPlural("fy", "fies")
109 rs.AddPlural("gy", "gies")
110 rs.AddPlural("hy", "hies")
111 rs.AddPlural("jy", "jies")
112 rs.AddPlural("ky", "kies")
113 rs.AddPlural("ly", "lies")
114 rs.AddPlural("my", "mies")
115 rs.AddPlural("ny", "nies")
116 rs.AddPlural("py", "pies")
117 rs.AddPlural("qy", "qies")
118 rs.AddPlural("ry", "ries")
119 rs.AddPlural("sy", "sies")
120 rs.AddPlural("ty", "ties")
121 rs.AddPlural("vy", "vies")
122 rs.AddPlural("wy", "wies")
123 rs.AddPlural("xy", "xies")
124 rs.AddPlural("zy", "zies")
125 rs.AddPlural("x", "xes")
126 rs.AddPlural("ch", "ches")
127 rs.AddPlural("ss", "sses")
128 rs.AddPlural("sh", "shes")
129 rs.AddPlural("matrix", "matrices")
130 rs.AddPlural("vertix", "vertices")
131 rs.AddPlural("indix", "indices")
132 rs.AddPlural("matrex", "matrices")
133 rs.AddPlural("vertex", "vertices")
134 rs.AddPlural("index", "indices")
135 rs.AddPlural("mouse", "mice")
136 rs.AddPlural("louse", "lice")
137 rs.AddPlural("mice", "mice")
138 rs.AddPlural("lice", "lice")
139 rs.AddPlural("ress", "resses")
140 rs.AddPluralExact("ox", "oxen", true)
141 rs.AddPluralExact("oxen", "oxen", true)
142 rs.AddPluralExact("quiz", "quizzes", true)
143 rs.AddSingular("s", "")
144 rs.AddSingular("ss", "ss")
145 rs.AddSingular("news", "news")
146 rs.AddSingular("ta", "tum")
147 rs.AddSingular("ia", "ium")
148 rs.AddSingular("analyses", "analysis")
149 rs.AddSingular("bases", "basis")
150 rs.AddSingularExact("basis", "basis", true)
151 rs.AddSingular("diagnoses", "diagnosis")
152 rs.AddSingularExact("diagnosis", "diagnosis", true)
153 rs.AddSingular("parentheses", "parenthesis")
154 rs.AddSingular("prognoses", "prognosis")
155 rs.AddSingular("synopses", "synopsis")
156 rs.AddSingular("theses", "thesis")
157 rs.AddSingular("analyses", "analysis")
158 rs.AddSingularExact("analysis", "analysis", true)
159 rs.AddSingular("ovies", "ovie")
160 rs.AddSingular("aves", "afe")
161 rs.AddSingular("bves", "bfe")
162 rs.AddSingular("cves", "cfe")
163 rs.AddSingular("dves", "dfe")
164 rs.AddSingular("eves", "efe")
165 rs.AddSingular("gves", "gfe")
166 rs.AddSingular("hves", "hfe")
167 rs.AddSingular("ives", "ife")
168 rs.AddSingular("jves", "jfe")
169 rs.AddSingular("kves", "kfe")
170 rs.AddSingular("lves", "lfe")
171 rs.AddSingular("mves", "mfe")
172 rs.AddSingular("nves", "nfe")
173 rs.AddSingular("oves", "ofe")
174 rs.AddSingular("pves", "pfe")
175 rs.AddSingular("qves", "qfe")
176 rs.AddSingular("rves", "rfe")
177 rs.AddSingular("sves", "sfe")
178 rs.AddSingular("tves", "tfe")
179 rs.AddSingular("uves", "ufe")
180 rs.AddSingular("vves", "vfe")
181 rs.AddSingular("wves", "wfe")
182 rs.AddSingular("xves", "xfe")
183 rs.AddSingular("yves", "yfe")
184 rs.AddSingular("zves", "zfe")
185 rs.AddSingular("hives", "hive")
186 rs.AddSingular("tives", "tive")
187 rs.AddSingular("lves", "lf")
188 rs.AddSingular("rves", "rf")
189 rs.AddSingular("quies", "quy")
190 rs.AddSingular("bies", "by")
191 rs.AddSingular("cies", "cy")
192 rs.AddSingular("dies", "dy")
193 rs.AddSingular("fies", "fy")
194 rs.AddSingular("gies", "gy")
195 rs.AddSingular("hies", "hy")
196 rs.AddSingular("jies", "jy")
197 rs.AddSingular("kies", "ky")
198 rs.AddSingular("lies", "ly")
199 rs.AddSingular("mies", "my")
200 rs.AddSingular("nies", "ny")
201 rs.AddSingular("pies", "py")
202 rs.AddSingular("qies", "qy")
203 rs.AddSingular("ries", "ry")
204 rs.AddSingular("sies", "sy")
205 rs.AddSingular("ties", "ty")
206 // rs.AddSingular("vies", "vy")
207 rs.AddSingular("wies", "wy")
208 rs.AddSingular("xies", "xy")
209 rs.AddSingular("zies", "zy")
210 rs.AddSingular("series", "series")
211 rs.AddSingular("xes", "x")
212 rs.AddSingular("ches", "ch")
213 rs.AddSingular("sses", "ss")
214 rs.AddSingular("shes", "sh")
215 rs.AddSingular("mice", "mouse")
216 rs.AddSingular("lice", "louse")
217 rs.AddSingular("buses", "bus")
218 rs.AddSingularExact("bus", "bus", true)
219 rs.AddSingular("oes", "o")
220 rs.AddSingular("shoes", "shoe")
221 rs.AddSingular("crises", "crisis")
222 rs.AddSingularExact("crisis", "crisis", true)
223 rs.AddSingular("axes", "axis")
224 rs.AddSingularExact("axis", "axis", true)
225 rs.AddSingular("testes", "testis")
226 rs.AddSingularExact("testis", "testis", true)
227 rs.AddSingular("octopi", "octopus")
228 rs.AddSingularExact("octopus", "octopus", true)
229 rs.AddSingular("viri", "virus")
230 rs.AddSingularExact("virus", "virus", true)
231 rs.AddSingular("statuses", "status")
232 rs.AddSingular("Statuses", "Status")
233 rs.AddSingular("campuses", "campus")
234 rs.AddSingularExact("status", "status", true)
235 rs.AddSingularExact("Status", "Status", true)
236 rs.AddSingularExact("campus", "campus", true)
237 rs.AddSingular("aliases", "alias")
238 rs.AddSingularExact("alias", "alias", true)
239 rs.AddSingularExact("oxen", "ox", true)
240 rs.AddSingular("vertices", "vertex")
241 rs.AddSingular("indices", "index")
242 rs.AddSingular("matrices", "matrix")
243 rs.AddSingularExact("quizzes", "quiz", true)
244 rs.AddSingular("databases", "database")
245 rs.AddSingular("resses", "ress")
246 rs.AddSingular("ress", "ress")
247 rs.AddIrregular("person", "people")
248 rs.AddIrregular("man", "men")
249 rs.AddIrregular("child", "children")
250 rs.AddIrregular("sex", "sexes")
251 rs.AddIrregular("move", "moves")
252 rs.AddIrregular("zombie", "zombies")
253 rs.AddIrregular("Status", "Statuses")
254 rs.AddIrregular("status", "statuses")
255 rs.AddIrregular("campus", "campuses")
256 rs.AddIrregular("human", "humans")
257 rs.AddUncountable("equipment")
258 rs.AddUncountable("information")
259 rs.AddUncountable("rice")
260 rs.AddUncountable("money")
261 rs.AddUncountable("species")
262 rs.AddUncountable("series")
263 rs.AddUncountable("fish")
264 rs.AddUncountable("sheep")
265 rs.AddUncountable("jeans")
266 rs.AddUncountable("police")
268 acronyms := strings.Split(baseAcronyms, ",")
269 for _, acr := range acronyms {
276 // Uncountables returns a map of uncountables in the ruleset
277 func (rs *Ruleset) Uncountables() map[string]bool {
278 return rs.uncountables
281 // AddPlural add a pluralization rule
282 func (rs *Ruleset) AddPlural(suffix, replacement string) {
283 rs.AddPluralExact(suffix, replacement, false)
286 // AddPluralExact add a pluralization rule with full string match
287 func (rs *Ruleset) AddPluralExact(suffix, replacement string, exact bool) {
288 // remove uncountable
289 delete(rs.uncountables, suffix)
293 r.replacement = replacement
296 rs.plurals = append([]*Rule{r}, rs.plurals...)
299 // AddSingular add a singular rule
300 func (rs *Ruleset) AddSingular(suffix, replacement string) {
301 rs.AddSingularExact(suffix, replacement, false)
304 // AddSingularExact same as AddSingular but you can set `exact` to force
305 // a full string match
306 func (rs *Ruleset) AddSingularExact(suffix, replacement string, exact bool) {
307 // remove from uncountable
308 delete(rs.uncountables, suffix)
312 r.replacement = replacement
314 rs.singulars = append([]*Rule{r}, rs.singulars...)
317 // AddHuman Human rules are applied by humanize to show more friendly
319 func (rs *Ruleset) AddHuman(suffix, replacement string) {
322 r.replacement = replacement
323 rs.humans = append([]*Rule{r}, rs.humans...)
326 // AddIrregular Add any inconsistent pluralizing/singularizing rules
328 func (rs *Ruleset) AddIrregular(singular, plural string) {
329 delete(rs.uncountables, singular)
330 delete(rs.uncountables, plural)
331 rs.AddPlural(singular, plural)
332 rs.AddPlural(plural, plural)
333 rs.AddSingular(plural, singular)
336 // AddAcronym if you use acronym you may need to add them to the ruleset
337 // to prevent Underscored words of things like "HTML" coming out
339 func (rs *Ruleset) AddAcronym(word string) {
342 r.replacement = rs.Titleize(strings.ToLower(word))
343 rs.acronyms = append(rs.acronyms, r)
346 // AddUncountable add a word to this ruleset that has the same singular and plural form
347 // for example: "rice"
348 func (rs *Ruleset) AddUncountable(word string) {
349 rs.uncountables[strings.ToLower(word)] = true
352 func (rs *Ruleset) isUncountable(word string) bool {
353 // handle multiple words by using the last one
354 words := strings.Split(word, " ")
355 if _, exists := rs.uncountables[strings.ToLower(words[len(words)-1])]; exists {
361 //isAcronym returns if a word is acronym or not.
362 func (rs *Ruleset) isAcronym(word string) bool {
363 for _, rule := range rs.acronyms {
364 if strings.ToUpper(rule.suffix) == strings.ToUpper(word) {
372 //PluralizeWithSize pluralize with taking number into account
373 func (rs *Ruleset) PluralizeWithSize(word string, size int) string {
375 return rs.Singularize(word)
377 return rs.Pluralize(word)
380 // Pluralize returns the plural form of a singular word
381 func (rs *Ruleset) Pluralize(word string) string {
385 lWord := strings.ToLower(word)
386 if rs.isUncountable(lWord) {
391 for _, rule := range rs.plurals {
393 if lWord == rule.suffix {
395 if lWord[0] != word[0] && lWord[1:] == word[1:] {
396 return rs.Capitalize(rule.replacement)
398 return rule.replacement
403 if strings.EqualFold(word, rule.suffix) {
404 candidate = rule.replacement
407 if strings.HasSuffix(word, rule.suffix) {
408 return replaceLast(word, rule.suffix, rule.replacement)
418 //Singularize returns the singular form of a plural word
419 func (rs *Ruleset) Singularize(word string) string {
423 lWord := strings.ToLower(word)
424 if rs.isUncountable(lWord) {
430 for _, rule := range rs.singulars {
432 if lWord == rule.suffix {
434 if lWord[0] != word[0] && lWord[1:] == word[1:] {
435 return rs.Capitalize(rule.replacement)
437 return rule.replacement
442 if strings.EqualFold(word, rule.suffix) {
443 candidate = rule.replacement
446 if strings.HasSuffix(word, rule.suffix) {
447 return replaceLast(word, rule.suffix, rule.replacement)
458 //Capitalize uppercase first character
459 func (rs *Ruleset) Capitalize(word string) string {
460 if rs.isAcronym(word) {
461 return strings.ToUpper(word)
463 return strings.ToUpper(word[:1]) + word[1:]
466 //Camelize "dino_party" -> "DinoParty"
467 func (rs *Ruleset) Camelize(word string) string {
468 if rs.isAcronym(word) {
469 return strings.ToUpper(word)
471 words := splitAtCaseChangeWithTitlecase(word)
472 return strings.Join(words, "")
475 //CamelizeDownFirst same as Camelcase but with first letter downcased
476 func (rs *Ruleset) CamelizeDownFirst(word string) string {
477 word = Camelize(word)
478 return strings.ToLower(word[:1]) + word[1:]
481 //Titleize Capitalize every word in sentence "hello there" -> "Hello There"
482 func (rs *Ruleset) Titleize(word string) string {
483 words := splitAtCaseChangeWithTitlecase(word)
484 result := strings.Join(words, " ")
486 var acronymWords []string
487 for index, word := range words {
489 acronymWords = append(acronymWords, word)
492 if len(word) > 1 || index == len(words)-1 || len(acronymWords) > 1 {
493 acronym := strings.Join(acronymWords, "")
494 if !rs.isAcronym(acronym) {
495 acronymWords = acronymWords[:len(acronymWords)]
499 result = strings.Replace(result, strings.Join(acronymWords, " "), acronym, 1)
500 acronymWords = []string{}
507 func (rs *Ruleset) safeCaseAcronyms(word string) string {
508 // convert an acronym like HTML into Html
509 for _, rule := range rs.acronyms {
510 word = strings.Replace(word, rule.suffix, rule.replacement, -1)
515 func (rs *Ruleset) separatedWords(word, sep string) string {
516 word = rs.safeCaseAcronyms(word)
517 words := splitAtCaseChange(word)
518 return strings.Join(words, sep)
521 //Underscore lowercase underscore version "BigBen" -> "big_ben"
522 func (rs *Ruleset) Underscore(word string) string {
523 return rs.separatedWords(word, "_")
526 //Humanize First letter of sentence capitalized
527 // Uses custom friendly replacements via AddHuman()
528 func (rs *Ruleset) Humanize(word string) string {
529 word = replaceLast(word, "_id", "") // strip foreign key kinds
530 // replace and strings in humans list
531 for _, rule := range rs.humans {
532 word = strings.Replace(word, rule.suffix, rule.replacement, -1)
534 sentence := rs.separatedWords(word, " ")
536 r, n := utf8.DecodeRuneInString(sentence)
537 return string(unicode.ToUpper(r)) + sentence[n:]
540 //ForeignKey an underscored foreign key name "Person" -> "person_id"
541 func (rs *Ruleset) ForeignKey(word string) string {
542 return rs.Underscore(rs.Singularize(word)) + "_id"
545 //ForeignKeyCondensed a foreign key (with an underscore) "Person" -> "personid"
546 func (rs *Ruleset) ForeignKeyCondensed(word string) string {
547 return rs.Underscore(word) + "id"
550 //Tableize Rails style pluralized table names: "SuperPerson" -> "super_people"
551 func (rs *Ruleset) Tableize(word string) string {
552 return rs.Pluralize(rs.Underscore(rs.Typeify(word)))
555 var notUrlSafe *regexp.Regexp = regexp.MustCompile(`[^\w\d\-_ ]`)
557 //Parameterize param safe dasherized names like "my-param"
558 func (rs *Ruleset) Parameterize(word string) string {
559 return ParameterizeJoin(word, "-")
562 //ParameterizeJoin param safe dasherized names with custom separator
563 func (rs *Ruleset) ParameterizeJoin(word, sep string) string {
564 word = strings.ToLower(word)
565 word = rs.Asciify(word)
566 word = notUrlSafe.ReplaceAllString(word, "")
567 word = strings.Replace(word, " ", sep, -1)
569 squash, err := regexp.Compile(sep + "+")
571 word = squash.ReplaceAllString(word, sep)
574 word = strings.Trim(word, sep+" ")
578 var lookalikes = map[string]*regexp.Regexp{
579 "A": regexp.MustCompile(`À|Á|Â|Ã|Ä|Å`),
580 "AE": regexp.MustCompile(`Æ`),
581 "C": regexp.MustCompile(`Ç`),
582 "E": regexp.MustCompile(`È|É|Ê|Ë`),
583 "G": regexp.MustCompile(`Ğ`),
584 "I": regexp.MustCompile(`Ì|Í|Î|Ï|İ`),
585 "N": regexp.MustCompile(`Ñ`),
586 "O": regexp.MustCompile(`Ò|Ó|Ô|Õ|Ö|Ø`),
587 "S": regexp.MustCompile(`Ş`),
588 "U": regexp.MustCompile(`Ù|Ú|Û|Ü`),
589 "Y": regexp.MustCompile(`Ý`),
590 "ss": regexp.MustCompile(`ß`),
591 "a": regexp.MustCompile(`à|á|â|ã|ä|å`),
592 "ae": regexp.MustCompile(`æ`),
593 "c": regexp.MustCompile(`ç`),
594 "e": regexp.MustCompile(`è|é|ê|ë`),
595 "g": regexp.MustCompile(`ğ`),
596 "i": regexp.MustCompile(`ì|í|î|ï|ı`),
597 "n": regexp.MustCompile(`ñ`),
598 "o": regexp.MustCompile(`ò|ó|ô|õ|ö|ø`),
599 "s": regexp.MustCompile(`ş`),
600 "u": regexp.MustCompile(`ù|ú|û|ü|ũ|ū|ŭ|ů|ű|ų`),
601 "y": regexp.MustCompile(`ý|ÿ`),
604 //Asciify transforms Latin characters like é -> e
605 func (rs *Ruleset) Asciify(word string) string {
606 for repl, regex := range lookalikes {
607 word = regex.ReplaceAllString(word, repl)
612 var tablePrefix = regexp.MustCompile(`^[^.]*\.`)
614 //Typeify "something_like_this" -> "SomethingLikeThis"
615 func (rs *Ruleset) Typeify(word string) string {
616 word = tablePrefix.ReplaceAllString(word, "")
617 return rs.Camelize(rs.Singularize(word))
620 //Dasherize "SomeText" -> "some-text"
621 func (rs *Ruleset) Dasherize(word string) string {
622 return rs.separatedWords(word, "-")
625 //Ordinalize "1031" -> "1031st"
626 func (rs *Ruleset) Ordinalize(str string) string {
627 number, err := strconv.Atoi(str)
631 switch abs(number) % 100 {
633 return fmt.Sprintf("%dth", number)
635 switch abs(number) % 10 {
637 return fmt.Sprintf("%dst", number)
639 return fmt.Sprintf("%dnd", number)
641 return fmt.Sprintf("%drd", number)
644 return fmt.Sprintf("%dth", number)
647 //ForeignKeyToAttribute returns the attribute name from the foreign key
648 func (rs *Ruleset) ForeignKeyToAttribute(str string) string {
649 w := rs.Camelize(str)
650 if strings.HasSuffix(w, "Id") {
651 return strings.TrimSuffix(w, "Id") + "ID"
656 //LoadReader loads rules from io.Reader param
657 func (rs *Ruleset) LoadReader(r io.Reader) error {
658 m := map[string]string{}
659 err := json.NewDecoder(r).Decode(&m)
661 return fmt.Errorf("could not decode inflection JSON from reader: %s", err)
663 for s, p := range m {
664 defaultRuleset.AddIrregular(s, p)
669 /////////////////////////////////////////
670 // the default global ruleset
671 //////////////////////////////////////////
673 var defaultRuleset *Ruleset
675 //LoadReader loads rules from io.Reader param
676 func LoadReader(r io.Reader) error {
677 return defaultRuleset.LoadReader(r)
681 defaultRuleset = NewDefaultRuleset()
684 cfg := filepath.Join(pwd, "inflections.json")
685 if p := os.Getenv("INFLECT_PATH"); p != "" {
688 if _, err := os.Stat(cfg); err == nil {
689 b, err := ioutil.ReadFile(cfg)
691 fmt.Printf("could not read inflection file %s (%s)\n", cfg, err)
694 if err = defaultRuleset.LoadReader(bytes.NewReader(b)); err != nil {
700 //Uncountables returns a list of uncountables rules
701 func Uncountables() map[string]bool {
702 return defaultRuleset.Uncountables()
705 //AddPlural adds plural to the ruleset
706 func AddPlural(suffix, replacement string) {
707 defaultRuleset.AddPlural(suffix, replacement)
710 //AddSingular adds singular to the ruleset
711 func AddSingular(suffix, replacement string) {
712 defaultRuleset.AddSingular(suffix, replacement)
715 //AddHuman adds human
716 func AddHuman(suffix, replacement string) {
717 defaultRuleset.AddHuman(suffix, replacement)
720 func AddIrregular(singular, plural string) {
721 defaultRuleset.AddIrregular(singular, plural)
724 func AddAcronym(word string) {
725 defaultRuleset.AddAcronym(word)
728 func AddUncountable(word string) {
729 defaultRuleset.AddUncountable(word)
732 func Pluralize(word string) string {
733 return defaultRuleset.Pluralize(word)
736 func PluralizeWithSize(word string, size int) string {
737 return defaultRuleset.PluralizeWithSize(word, size)
740 func Singularize(word string) string {
741 return defaultRuleset.Singularize(word)
744 func Capitalize(word string) string {
745 return defaultRuleset.Capitalize(word)
748 func Camelize(word string) string {
749 return defaultRuleset.Camelize(word)
752 func CamelizeDownFirst(word string) string {
753 return defaultRuleset.CamelizeDownFirst(word)
756 func Titleize(word string) string {
757 return defaultRuleset.Titleize(word)
760 func Underscore(word string) string {
761 return defaultRuleset.Underscore(word)
764 func Humanize(word string) string {
765 return defaultRuleset.Humanize(word)
768 func ForeignKey(word string) string {
769 return defaultRuleset.ForeignKey(word)
772 func ForeignKeyCondensed(word string) string {
773 return defaultRuleset.ForeignKeyCondensed(word)
776 func Tableize(word string) string {
777 return defaultRuleset.Tableize(word)
780 func Parameterize(word string) string {
781 return defaultRuleset.Parameterize(word)
784 func ParameterizeJoin(word, sep string) string {
785 return defaultRuleset.ParameterizeJoin(word, sep)
788 func Typeify(word string) string {
789 return defaultRuleset.Typeify(word)
792 func Dasherize(word string) string {
793 return defaultRuleset.Dasherize(word)
796 func Ordinalize(word string) string {
797 return defaultRuleset.Ordinalize(word)
800 func Asciify(word string) string {
801 return defaultRuleset.Asciify(word)
804 func ForeignKeyToAttribute(word string) string {
805 return defaultRuleset.ForeignKeyToAttribute(word)
810 func reverse(s string) string {
811 o := make([]rune, utf8.RuneCountInString(s))
813 for _, c := range s {
820 func isSpacerChar(c rune) bool {
822 case c == rune("_"[0]):
824 case c == rune(" "[0]):
826 case c == rune(":"[0]):
828 case c == rune("-"[0]):
834 func splitAtCaseChange(s string) []string {
835 words := make([]string, 0)
836 word := make([]rune, 0)
837 for _, c := range s {
838 spacer := isSpacerChar(c)
840 if unicode.IsUpper(c) || spacer {
841 words = append(words, string(word))
842 word = make([]rune, 0)
846 word = append(word, unicode.ToLower(c))
849 words = append(words, string(word))
853 func splitAtCaseChangeWithTitlecase(s string) []string {
854 words := make([]string, 0)
855 word := make([]rune, 0)
857 for _, c := range s {
858 spacer := isSpacerChar(c)
860 if unicode.IsUpper(c) || spacer {
861 words = append(words, string(word))
862 word = make([]rune, 0)
867 word = append(word, unicode.ToLower(c))
869 word = append(word, unicode.ToUpper(c))
874 words = append(words, string(word))
878 func replaceLast(s, match, repl string) string {
881 mrev := reverse(match)
882 rrev := reverse(repl)
883 // match first and reverse back
884 return reverse(strings.Replace(srev, mrev, rrev, 1))
887 func abs(x int) int {