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