You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

inflect.go 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713
  1. package inflect
  2. import (
  3. "fmt"
  4. "regexp"
  5. "strconv"
  6. "strings"
  7. "unicode"
  8. "unicode/utf8"
  9. )
  10. // used by rulesets
  11. type Rule struct {
  12. suffix string
  13. replacement string
  14. exact bool
  15. }
  16. // a Ruleset is the config of pluralization rules
  17. // you can extend the rules with the Add* methods
  18. type Ruleset struct {
  19. uncountables map[string]bool
  20. plurals []*Rule
  21. singulars []*Rule
  22. humans []*Rule
  23. acronyms []*Rule
  24. acronymMatcher *regexp.Regexp
  25. }
  26. // create a blank ruleset. Unless you are going to
  27. // build your own rules from scratch you probably
  28. // won't need this and can just use the defaultRuleset
  29. // via the global inflect.* methods
  30. func NewRuleset() *Ruleset {
  31. rs := new(Ruleset)
  32. rs.uncountables = make(map[string]bool)
  33. rs.plurals = make([]*Rule, 0)
  34. rs.singulars = make([]*Rule, 0)
  35. rs.humans = make([]*Rule, 0)
  36. rs.acronyms = make([]*Rule, 0)
  37. return rs
  38. }
  39. // create a new ruleset and load it with the default
  40. // set of common English pluralization rules
  41. func NewDefaultRuleset() *Ruleset {
  42. rs := NewRuleset()
  43. rs.AddPlural("s", "s")
  44. rs.AddPlural("testis", "testes")
  45. rs.AddPlural("axis", "axes")
  46. rs.AddPlural("octopus", "octopi")
  47. rs.AddPlural("virus", "viri")
  48. rs.AddPlural("octopi", "octopi")
  49. rs.AddPlural("viri", "viri")
  50. rs.AddPlural("alias", "aliases")
  51. rs.AddPlural("status", "statuses")
  52. rs.AddPlural("bus", "buses")
  53. rs.AddPlural("buffalo", "buffaloes")
  54. rs.AddPlural("tomato", "tomatoes")
  55. rs.AddPlural("tum", "ta")
  56. rs.AddPlural("ium", "ia")
  57. rs.AddPlural("ta", "ta")
  58. rs.AddPlural("ia", "ia")
  59. rs.AddPlural("sis", "ses")
  60. rs.AddPlural("lf", "lves")
  61. rs.AddPlural("rf", "rves")
  62. rs.AddPlural("afe", "aves")
  63. rs.AddPlural("bfe", "bves")
  64. rs.AddPlural("cfe", "cves")
  65. rs.AddPlural("dfe", "dves")
  66. rs.AddPlural("efe", "eves")
  67. rs.AddPlural("gfe", "gves")
  68. rs.AddPlural("hfe", "hves")
  69. rs.AddPlural("ife", "ives")
  70. rs.AddPlural("jfe", "jves")
  71. rs.AddPlural("kfe", "kves")
  72. rs.AddPlural("lfe", "lves")
  73. rs.AddPlural("mfe", "mves")
  74. rs.AddPlural("nfe", "nves")
  75. rs.AddPlural("ofe", "oves")
  76. rs.AddPlural("pfe", "pves")
  77. rs.AddPlural("qfe", "qves")
  78. rs.AddPlural("rfe", "rves")
  79. rs.AddPlural("sfe", "sves")
  80. rs.AddPlural("tfe", "tves")
  81. rs.AddPlural("ufe", "uves")
  82. rs.AddPlural("vfe", "vves")
  83. rs.AddPlural("wfe", "wves")
  84. rs.AddPlural("xfe", "xves")
  85. rs.AddPlural("yfe", "yves")
  86. rs.AddPlural("zfe", "zves")
  87. rs.AddPlural("hive", "hives")
  88. rs.AddPlural("quy", "quies")
  89. rs.AddPlural("by", "bies")
  90. rs.AddPlural("cy", "cies")
  91. rs.AddPlural("dy", "dies")
  92. rs.AddPlural("fy", "fies")
  93. rs.AddPlural("gy", "gies")
  94. rs.AddPlural("hy", "hies")
  95. rs.AddPlural("jy", "jies")
  96. rs.AddPlural("ky", "kies")
  97. rs.AddPlural("ly", "lies")
  98. rs.AddPlural("my", "mies")
  99. rs.AddPlural("ny", "nies")
  100. rs.AddPlural("py", "pies")
  101. rs.AddPlural("qy", "qies")
  102. rs.AddPlural("ry", "ries")
  103. rs.AddPlural("sy", "sies")
  104. rs.AddPlural("ty", "ties")
  105. rs.AddPlural("vy", "vies")
  106. rs.AddPlural("wy", "wies")
  107. rs.AddPlural("xy", "xies")
  108. rs.AddPlural("zy", "zies")
  109. rs.AddPlural("x", "xes")
  110. rs.AddPlural("ch", "ches")
  111. rs.AddPlural("ss", "sses")
  112. rs.AddPlural("sh", "shes")
  113. rs.AddPlural("matrix", "matrices")
  114. rs.AddPlural("vertix", "vertices")
  115. rs.AddPlural("indix", "indices")
  116. rs.AddPlural("matrex", "matrices")
  117. rs.AddPlural("vertex", "vertices")
  118. rs.AddPlural("index", "indices")
  119. rs.AddPlural("mouse", "mice")
  120. rs.AddPlural("louse", "lice")
  121. rs.AddPlural("mice", "mice")
  122. rs.AddPlural("lice", "lice")
  123. rs.AddPluralExact("ox", "oxen", true)
  124. rs.AddPluralExact("oxen", "oxen", true)
  125. rs.AddPluralExact("quiz", "quizzes", true)
  126. rs.AddSingular("s", "")
  127. rs.AddSingular("news", "news")
  128. rs.AddSingular("ta", "tum")
  129. rs.AddSingular("ia", "ium")
  130. rs.AddSingular("analyses", "analysis")
  131. rs.AddSingular("bases", "basis")
  132. rs.AddSingular("diagnoses", "diagnosis")
  133. rs.AddSingular("parentheses", "parenthesis")
  134. rs.AddSingular("prognoses", "prognosis")
  135. rs.AddSingular("synopses", "synopsis")
  136. rs.AddSingular("theses", "thesis")
  137. rs.AddSingular("analyses", "analysis")
  138. rs.AddSingular("aves", "afe")
  139. rs.AddSingular("bves", "bfe")
  140. rs.AddSingular("cves", "cfe")
  141. rs.AddSingular("dves", "dfe")
  142. rs.AddSingular("eves", "efe")
  143. rs.AddSingular("gves", "gfe")
  144. rs.AddSingular("hves", "hfe")
  145. rs.AddSingular("ives", "ife")
  146. rs.AddSingular("jves", "jfe")
  147. rs.AddSingular("kves", "kfe")
  148. rs.AddSingular("lves", "lfe")
  149. rs.AddSingular("mves", "mfe")
  150. rs.AddSingular("nves", "nfe")
  151. rs.AddSingular("oves", "ofe")
  152. rs.AddSingular("pves", "pfe")
  153. rs.AddSingular("qves", "qfe")
  154. rs.AddSingular("rves", "rfe")
  155. rs.AddSingular("sves", "sfe")
  156. rs.AddSingular("tves", "tfe")
  157. rs.AddSingular("uves", "ufe")
  158. rs.AddSingular("vves", "vfe")
  159. rs.AddSingular("wves", "wfe")
  160. rs.AddSingular("xves", "xfe")
  161. rs.AddSingular("yves", "yfe")
  162. rs.AddSingular("zves", "zfe")
  163. rs.AddSingular("hives", "hive")
  164. rs.AddSingular("tives", "tive")
  165. rs.AddSingular("lves", "lf")
  166. rs.AddSingular("rves", "rf")
  167. rs.AddSingular("quies", "quy")
  168. rs.AddSingular("bies", "by")
  169. rs.AddSingular("cies", "cy")
  170. rs.AddSingular("dies", "dy")
  171. rs.AddSingular("fies", "fy")
  172. rs.AddSingular("gies", "gy")
  173. rs.AddSingular("hies", "hy")
  174. rs.AddSingular("jies", "jy")
  175. rs.AddSingular("kies", "ky")
  176. rs.AddSingular("lies", "ly")
  177. rs.AddSingular("mies", "my")
  178. rs.AddSingular("nies", "ny")
  179. rs.AddSingular("pies", "py")
  180. rs.AddSingular("qies", "qy")
  181. rs.AddSingular("ries", "ry")
  182. rs.AddSingular("sies", "sy")
  183. rs.AddSingular("ties", "ty")
  184. rs.AddSingular("vies", "vy")
  185. rs.AddSingular("wies", "wy")
  186. rs.AddSingular("xies", "xy")
  187. rs.AddSingular("zies", "zy")
  188. rs.AddSingular("series", "series")
  189. rs.AddSingular("movies", "movie")
  190. rs.AddSingular("xes", "x")
  191. rs.AddSingular("ches", "ch")
  192. rs.AddSingular("sses", "ss")
  193. rs.AddSingular("shes", "sh")
  194. rs.AddSingular("mice", "mouse")
  195. rs.AddSingular("lice", "louse")
  196. rs.AddSingular("buses", "bus")
  197. rs.AddSingular("oes", "o")
  198. rs.AddSingular("shoes", "shoe")
  199. rs.AddSingular("crises", "crisis")
  200. rs.AddSingular("axes", "axis")
  201. rs.AddSingular("testes", "testis")
  202. rs.AddSingular("octopi", "octopus")
  203. rs.AddSingular("viri", "virus")
  204. rs.AddSingular("statuses", "status")
  205. rs.AddSingular("aliases", "alias")
  206. rs.AddSingularExact("oxen", "ox", true)
  207. rs.AddSingular("vertices", "vertex")
  208. rs.AddSingular("indices", "index")
  209. rs.AddSingular("matrices", "matrix")
  210. rs.AddSingularExact("quizzes", "quiz", true)
  211. rs.AddSingular("databases", "database")
  212. rs.AddIrregular("person", "people")
  213. rs.AddIrregular("man", "men")
  214. rs.AddIrregular("child", "children")
  215. rs.AddIrregular("sex", "sexes")
  216. rs.AddIrregular("move", "moves")
  217. rs.AddIrregular("zombie", "zombies")
  218. rs.AddUncountable("equipment")
  219. rs.AddUncountable("information")
  220. rs.AddUncountable("rice")
  221. rs.AddUncountable("money")
  222. rs.AddUncountable("species")
  223. rs.AddUncountable("series")
  224. rs.AddUncountable("fish")
  225. rs.AddUncountable("sheep")
  226. rs.AddUncountable("jeans")
  227. rs.AddUncountable("police")
  228. return rs
  229. }
  230. func (rs *Ruleset) Uncountables() map[string]bool {
  231. return rs.uncountables
  232. }
  233. // add a pluralization rule
  234. func (rs *Ruleset) AddPlural(suffix, replacement string) {
  235. rs.AddPluralExact(suffix, replacement, false)
  236. }
  237. // add a pluralization rule with full string match
  238. func (rs *Ruleset) AddPluralExact(suffix, replacement string, exact bool) {
  239. // remove uncountable
  240. delete(rs.uncountables, suffix)
  241. // create rule
  242. r := new(Rule)
  243. r.suffix = suffix
  244. r.replacement = replacement
  245. r.exact = exact
  246. // prepend
  247. rs.plurals = append([]*Rule{r}, rs.plurals...)
  248. }
  249. // add a singular rule
  250. func (rs *Ruleset) AddSingular(suffix, replacement string) {
  251. rs.AddSingularExact(suffix, replacement, false)
  252. }
  253. // same as AddSingular but you can set `exact` to force
  254. // a full string match
  255. func (rs *Ruleset) AddSingularExact(suffix, replacement string, exact bool) {
  256. // remove from uncountable
  257. delete(rs.uncountables, suffix)
  258. // create rule
  259. r := new(Rule)
  260. r.suffix = suffix
  261. r.replacement = replacement
  262. r.exact = exact
  263. rs.singulars = append([]*Rule{r}, rs.singulars...)
  264. }
  265. // Human rules are applied by humanize to show more friendly
  266. // versions of words
  267. func (rs *Ruleset) AddHuman(suffix, replacement string) {
  268. r := new(Rule)
  269. r.suffix = suffix
  270. r.replacement = replacement
  271. rs.humans = append([]*Rule{r}, rs.humans...)
  272. }
  273. // Add any inconsistant pluralizing/sinularizing rules
  274. // to the set here.
  275. func (rs *Ruleset) AddIrregular(singular, plural string) {
  276. delete(rs.uncountables, singular)
  277. delete(rs.uncountables, plural)
  278. rs.AddPlural(singular, plural)
  279. rs.AddPlural(plural, plural)
  280. rs.AddSingular(plural, singular)
  281. }
  282. // if you use acronym you may need to add them to the ruleset
  283. // to prevent Underscored words of things like "HTML" coming out
  284. // as "h_t_m_l"
  285. func (rs *Ruleset) AddAcronym(word string) {
  286. r := new(Rule)
  287. r.suffix = word
  288. r.replacement = rs.Titleize(strings.ToLower(word))
  289. rs.acronyms = append(rs.acronyms, r)
  290. }
  291. // add a word to this ruleset that has the same singular and plural form
  292. // for example: "rice"
  293. func (rs *Ruleset) AddUncountable(word string) {
  294. rs.uncountables[strings.ToLower(word)] = true
  295. }
  296. func (rs *Ruleset) isUncountable(word string) bool {
  297. // handle multiple words by using the last one
  298. words := strings.Split(word, " ")
  299. if _, exists := rs.uncountables[strings.ToLower(words[len(words)-1])]; exists {
  300. return true
  301. }
  302. return false
  303. }
  304. // returns the plural form of a singular word
  305. func (rs *Ruleset) Pluralize(word string) string {
  306. if len(word) == 0 {
  307. return word
  308. }
  309. if rs.isUncountable(word) {
  310. return word
  311. }
  312. for _, rule := range rs.plurals {
  313. if rule.exact {
  314. if word == rule.suffix {
  315. return rule.replacement
  316. }
  317. } else {
  318. if strings.HasSuffix(word, rule.suffix) {
  319. return replaceLast(word, rule.suffix, rule.replacement)
  320. }
  321. }
  322. }
  323. return word + "s"
  324. }
  325. // returns the singular form of a plural word
  326. func (rs *Ruleset) Singularize(word string) string {
  327. if len(word) == 0 {
  328. return word
  329. }
  330. if rs.isUncountable(word) {
  331. return word
  332. }
  333. for _, rule := range rs.singulars {
  334. if rule.exact {
  335. if word == rule.suffix {
  336. return rule.replacement
  337. }
  338. } else {
  339. if strings.HasSuffix(word, rule.suffix) {
  340. return replaceLast(word, rule.suffix, rule.replacement)
  341. }
  342. }
  343. }
  344. return word
  345. }
  346. // uppercase first character
  347. func (rs *Ruleset) Capitalize(word string) string {
  348. return strings.ToUpper(word[:1]) + word[1:]
  349. }
  350. // "dino_party" -> "DinoParty"
  351. func (rs *Ruleset) Camelize(word string) string {
  352. words := splitAtCaseChangeWithTitlecase(word)
  353. return strings.Join(words, "")
  354. }
  355. // same as Camelcase but with first letter downcased
  356. func (rs *Ruleset) CamelizeDownFirst(word string) string {
  357. word = Camelize(word)
  358. return strings.ToLower(word[:1]) + word[1:]
  359. }
  360. // Captitilize every word in sentance "hello there" -> "Hello There"
  361. func (rs *Ruleset) Titleize(word string) string {
  362. words := splitAtCaseChangeWithTitlecase(word)
  363. return strings.Join(words, " ")
  364. }
  365. func (rs *Ruleset) safeCaseAcronyms(word string) string {
  366. // convert an acroymn like HTML into Html
  367. for _, rule := range rs.acronyms {
  368. word = strings.Replace(word, rule.suffix, rule.replacement, -1)
  369. }
  370. return word
  371. }
  372. func (rs *Ruleset) seperatedWords(word, sep string) string {
  373. word = rs.safeCaseAcronyms(word)
  374. words := splitAtCaseChange(word)
  375. return strings.Join(words, sep)
  376. }
  377. // lowercase underscore version "BigBen" -> "big_ben"
  378. func (rs *Ruleset) Underscore(word string) string {
  379. return rs.seperatedWords(word, "_")
  380. }
  381. // First letter of sentance captitilized
  382. // Uses custom friendly replacements via AddHuman()
  383. func (rs *Ruleset) Humanize(word string) string {
  384. word = replaceLast(word, "_id", "") // strip foreign key kinds
  385. // replace and strings in humans list
  386. for _, rule := range rs.humans {
  387. word = strings.Replace(word, rule.suffix, rule.replacement, -1)
  388. }
  389. sentance := rs.seperatedWords(word, " ")
  390. return strings.ToUpper(sentance[:1]) + sentance[1:]
  391. }
  392. // an underscored foreign key name "Person" -> "person_id"
  393. func (rs *Ruleset) ForeignKey(word string) string {
  394. return rs.Underscore(rs.Singularize(word)) + "_id"
  395. }
  396. // a foreign key (with an underscore) "Person" -> "personid"
  397. func (rs *Ruleset) ForeignKeyCondensed(word string) string {
  398. return rs.Underscore(word) + "id"
  399. }
  400. // Rails style pluralized table names: "SuperPerson" -> "super_people"
  401. func (rs *Ruleset) Tableize(word string) string {
  402. return rs.Pluralize(rs.Underscore(rs.Typeify(word)))
  403. }
  404. var notUrlSafe *regexp.Regexp = regexp.MustCompile(`[^\w\d\-_ ]`)
  405. // param safe dasherized names like "my-param"
  406. func (rs *Ruleset) Parameterize(word string) string {
  407. return ParameterizeJoin(word, "-")
  408. }
  409. // param safe dasherized names with custom seperator
  410. func (rs *Ruleset) ParameterizeJoin(word, sep string) string {
  411. word = strings.ToLower(word)
  412. word = rs.Asciify(word)
  413. word = notUrlSafe.ReplaceAllString(word, "")
  414. word = strings.Replace(word, " ", sep, -1)
  415. if len(sep) > 0 {
  416. squash, err := regexp.Compile(sep + "+")
  417. if err == nil {
  418. word = squash.ReplaceAllString(word, sep)
  419. }
  420. }
  421. word = strings.Trim(word, sep+" ")
  422. return word
  423. }
  424. var lookalikes map[string]*regexp.Regexp = map[string]*regexp.Regexp{
  425. "A": regexp.MustCompile(`À|Á|Â|Ã|Ä|Å`),
  426. "AE": regexp.MustCompile(`Æ`),
  427. "C": regexp.MustCompile(`Ç`),
  428. "E": regexp.MustCompile(`È|É|Ê|Ë`),
  429. "G": regexp.MustCompile(`Ğ`),
  430. "I": regexp.MustCompile(`Ì|Í|Î|Ï|İ`),
  431. "N": regexp.MustCompile(`Ñ`),
  432. "O": regexp.MustCompile(`Ò|Ó|Ô|Õ|Ö|Ø`),
  433. "S": regexp.MustCompile(`Ş`),
  434. "U": regexp.MustCompile(`Ù|Ú|Û|Ü`),
  435. "Y": regexp.MustCompile(`Ý`),
  436. "ss": regexp.MustCompile(`ß`),
  437. "a": regexp.MustCompile(`à|á|â|ã|ä|å`),
  438. "ae": regexp.MustCompile(`æ`),
  439. "c": regexp.MustCompile(`ç`),
  440. "e": regexp.MustCompile(`è|é|ê|ë`),
  441. "g": regexp.MustCompile(`ğ`),
  442. "i": regexp.MustCompile(`ì|í|î|ï|ı`),
  443. "n": regexp.MustCompile(`ñ`),
  444. "o": regexp.MustCompile(`ò|ó|ô|õ|ö|ø`),
  445. "s": regexp.MustCompile(`ş`),
  446. "u": regexp.MustCompile(`ù|ú|û|ü|ũ|ū|ŭ|ů|ű|ų`),
  447. "y": regexp.MustCompile(`ý|ÿ`),
  448. }
  449. // transforms latin characters like é -> e
  450. func (rs *Ruleset) Asciify(word string) string {
  451. for repl, regex := range lookalikes {
  452. word = regex.ReplaceAllString(word, repl)
  453. }
  454. return word
  455. }
  456. var tablePrefix *regexp.Regexp = regexp.MustCompile(`^[^.]*\.`)
  457. // "something_like_this" -> "SomethingLikeThis"
  458. func (rs *Ruleset) Typeify(word string) string {
  459. word = tablePrefix.ReplaceAllString(word, "")
  460. return rs.Camelize(rs.Singularize(word))
  461. }
  462. // "SomeText" -> "some-text"
  463. func (rs *Ruleset) Dasherize(word string) string {
  464. return rs.seperatedWords(word, "-")
  465. }
  466. // "1031" -> "1031st"
  467. func (rs *Ruleset) Ordinalize(str string) string {
  468. number, err := strconv.Atoi(str)
  469. if err != nil {
  470. return str
  471. }
  472. switch abs(number) % 100 {
  473. case 11, 12, 13:
  474. return fmt.Sprintf("%dth", number)
  475. default:
  476. switch abs(number) % 10 {
  477. case 1:
  478. return fmt.Sprintf("%dst", number)
  479. case 2:
  480. return fmt.Sprintf("%dnd", number)
  481. case 3:
  482. return fmt.Sprintf("%drd", number)
  483. }
  484. }
  485. return fmt.Sprintf("%dth", number)
  486. }
  487. /////////////////////////////////////////
  488. // the default global ruleset
  489. //////////////////////////////////////////
  490. var defaultRuleset *Ruleset
  491. func init() {
  492. defaultRuleset = NewDefaultRuleset()
  493. }
  494. func Uncountables() map[string]bool {
  495. return defaultRuleset.Uncountables()
  496. }
  497. func AddPlural(suffix, replacement string) {
  498. defaultRuleset.AddPlural(suffix, replacement)
  499. }
  500. func AddSingular(suffix, replacement string) {
  501. defaultRuleset.AddSingular(suffix, replacement)
  502. }
  503. func AddHuman(suffix, replacement string) {
  504. defaultRuleset.AddHuman(suffix, replacement)
  505. }
  506. func AddIrregular(singular, plural string) {
  507. defaultRuleset.AddIrregular(singular, plural)
  508. }
  509. func AddAcronym(word string) {
  510. defaultRuleset.AddAcronym(word)
  511. }
  512. func AddUncountable(word string) {
  513. defaultRuleset.AddUncountable(word)
  514. }
  515. func Pluralize(word string) string {
  516. return defaultRuleset.Pluralize(word)
  517. }
  518. func Singularize(word string) string {
  519. return defaultRuleset.Singularize(word)
  520. }
  521. func Capitalize(word string) string {
  522. return defaultRuleset.Capitalize(word)
  523. }
  524. func Camelize(word string) string {
  525. return defaultRuleset.Camelize(word)
  526. }
  527. func CamelizeDownFirst(word string) string {
  528. return defaultRuleset.CamelizeDownFirst(word)
  529. }
  530. func Titleize(word string) string {
  531. return defaultRuleset.Titleize(word)
  532. }
  533. func Underscore(word string) string {
  534. return defaultRuleset.Underscore(word)
  535. }
  536. func Humanize(word string) string {
  537. return defaultRuleset.Humanize(word)
  538. }
  539. func ForeignKey(word string) string {
  540. return defaultRuleset.ForeignKey(word)
  541. }
  542. func ForeignKeyCondensed(word string) string {
  543. return defaultRuleset.ForeignKeyCondensed(word)
  544. }
  545. func Tableize(word string) string {
  546. return defaultRuleset.Tableize(word)
  547. }
  548. func Parameterize(word string) string {
  549. return defaultRuleset.Parameterize(word)
  550. }
  551. func ParameterizeJoin(word, sep string) string {
  552. return defaultRuleset.ParameterizeJoin(word, sep)
  553. }
  554. func Typeify(word string) string {
  555. return defaultRuleset.Typeify(word)
  556. }
  557. func Dasherize(word string) string {
  558. return defaultRuleset.Dasherize(word)
  559. }
  560. func Ordinalize(word string) string {
  561. return defaultRuleset.Ordinalize(word)
  562. }
  563. func Asciify(word string) string {
  564. return defaultRuleset.Asciify(word)
  565. }
  566. // helper funcs
  567. func reverse(s string) string {
  568. o := make([]rune, utf8.RuneCountInString(s))
  569. i := len(o)
  570. for _, c := range s {
  571. i--
  572. o[i] = c
  573. }
  574. return string(o)
  575. }
  576. func isSpacerChar(c rune) bool {
  577. switch {
  578. case c == rune("_"[0]):
  579. return true
  580. case c == rune(" "[0]):
  581. return true
  582. case c == rune(":"[0]):
  583. return true
  584. case c == rune("-"[0]):
  585. return true
  586. }
  587. return false
  588. }
  589. func splitAtCaseChange(s string) []string {
  590. words := make([]string, 0)
  591. word := make([]rune, 0)
  592. for _, c := range s {
  593. spacer := isSpacerChar(c)
  594. if len(word) > 0 {
  595. if unicode.IsUpper(c) || spacer {
  596. words = append(words, string(word))
  597. word = make([]rune, 0)
  598. }
  599. }
  600. if !spacer {
  601. word = append(word, unicode.ToLower(c))
  602. }
  603. }
  604. words = append(words, string(word))
  605. return words
  606. }
  607. func splitAtCaseChangeWithTitlecase(s string) []string {
  608. words := make([]string, 0)
  609. word := make([]rune, 0)
  610. for _, c := range s {
  611. spacer := isSpacerChar(c)
  612. if len(word) > 0 {
  613. if unicode.IsUpper(c) || spacer {
  614. words = append(words, string(word))
  615. word = make([]rune, 0)
  616. }
  617. }
  618. if !spacer {
  619. if len(word) > 0 {
  620. word = append(word, unicode.ToLower(c))
  621. } else {
  622. word = append(word, unicode.ToUpper(c))
  623. }
  624. }
  625. }
  626. words = append(words, string(word))
  627. return words
  628. }
  629. func replaceLast(s, match, repl string) string {
  630. // reverse strings
  631. srev := reverse(s)
  632. mrev := reverse(match)
  633. rrev := reverse(repl)
  634. // match first and reverse back
  635. return reverse(strings.Replace(srev, mrev, rrev, 1))
  636. }
  637. func abs(x int) int {
  638. if x < 0 {
  639. return -x
  640. }
  641. return x
  642. }