github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/man/english/words.go (about) 1 // Package english provides utilities to generate more user-friendly English output. 2 package english 3 4 import ( 5 "fmt" 6 "strings" 7 8 "github.com/bingoohuang/gg/pkg/man" 9 ) 10 11 // These are included because they are common technical terms. 12 var specialPlurals = map[string]string{ 13 "index": "indices", 14 "matrix": "matrices", 15 "vertex": "vertices", 16 } 17 18 var sibilantEndings = []string{"s", "sh", "tch", "x"} 19 20 var isVowel = map[byte]bool{ 21 'A': true, 'E': true, 'I': true, 'O': true, 'U': true, 22 'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 23 } 24 25 // PluralWord builds the plural form of an English word. 26 // The simple English rules of regular pluralization will be used 27 // if the plural form is an empty string (i.e. not explicitly given). 28 // The special cases are not guaranteed to work for strings outside ASCII. 29 func PluralWord(quantity int, singular, plural string) string { 30 if quantity == 1 { 31 return singular 32 } 33 if plural != "" { 34 return plural 35 } 36 if plural = specialPlurals[singular]; plural != "" { 37 return plural 38 } 39 40 // We need to guess what the English plural might be. Keep this 41 // function simple! It doesn't need to know about every possiblity; 42 // only regular rules and the most common special cases. 43 // 44 // Reference: http://en.wikipedia.org/wiki/English_plural 45 46 for _, ending := range sibilantEndings { 47 if strings.HasSuffix(singular, ending) { 48 return singular + "es" 49 } 50 } 51 l := len(singular) 52 if l >= 2 && singular[l-1] == 'o' && !isVowel[singular[l-2]] { 53 return singular + "es" 54 } 55 if l >= 2 && singular[l-1] == 'y' && !isVowel[singular[l-2]] { 56 return singular[:l-1] + "ies" 57 } 58 59 return singular + "s" 60 } 61 62 // Plural formats an integer and a string into a single pluralized string. 63 // The simple English rules of regular pluralization will be used 64 // if the plural form is an empty string (i.e. not explicitly given). 65 func Plural(quantity int, singular, plural string) string { 66 return fmt.Sprintf("%s %s", man.Comma(int64(quantity)), PluralWord(quantity, singular, plural)) 67 } 68 69 // WordSeries converts a list of words into a word series in English. 70 // It returns a string containing all the given words separated by commas, 71 // the coordinating conjunction, and a serial comma, as appropriate. 72 func WordSeries(words []string, conjunction string) string { 73 switch len(words) { 74 case 0: 75 return "" 76 case 1: 77 return words[0] 78 default: 79 return fmt.Sprintf("%s %s %s", strings.Join(words[:len(words)-1], ", "), conjunction, words[len(words)-1]) 80 } 81 } 82 83 // OxfordWordSeries converts a list of words into a word series in English, 84 // using an Oxford comma (https://en.wikipedia.org/wiki/Serial_comma). It 85 // returns a string containing all the given words separated by commas, the 86 // coordinating conjunction, and a serial comma, as appropriate. 87 func OxfordWordSeries(words []string, conjunction string) string { 88 switch len(words) { 89 case 0: 90 return "" 91 case 1: 92 return words[0] 93 case 2: 94 return strings.Join(words, fmt.Sprintf(" %s ", conjunction)) 95 default: 96 return fmt.Sprintf("%s, %s %s", strings.Join(words[:len(words)-1], ", "), conjunction, words[len(words)-1]) 97 } 98 }