var.go (4003B)
1 package internal 2 3 import ( 4 "reflect" 5 "regexp" 6 "sort" 7 8 "golang.org/x/text/message/catalog" 9 ) 10 11 // Var represents a message variable. 12 // The variables, like the sub messages are sorted. 13 // First: plurals (which again, are sorted) 14 // and then any custom keys. 15 // In variables, the sorting depends on the exact 16 // order the associated message uses the variables. 17 // This is extremely handy. 18 // This package requires the golang.org/x/text/message capabilities 19 // only for the variables feature, the message itself's pluralization is managed by the package. 20 type Var struct { 21 Name string // Variable name, e.g. Name 22 Literal string // Its literal is ${Name} 23 Cases []interface{} // one:...,few:...,... 24 Format string // defaults to "%d". 25 Argth int // 1, 2, 3... 26 } 27 28 func getVars(loc *Locale, key string, src map[string]interface{}) []Var { 29 if len(src) == 0 { 30 return nil 31 } 32 33 varsKey, ok := src[key] 34 if !ok { 35 return nil 36 } 37 38 varValue, ok := varsKey.([]interface{}) 39 if !ok { 40 return nil 41 } 42 43 vars := make([]Var, 0, len(varValue)) 44 45 for _, v := range varValue { 46 m, ok := v.(map[string]interface{}) 47 if !ok { 48 continue 49 } 50 51 for k, inner := range m { 52 varFormat := "%d" 53 54 innerMap, ok := inner.(map[string]interface{}) 55 if !ok { 56 continue 57 } 58 59 for kk, vv := range innerMap { 60 if kk == "format" { 61 if format, ok := vv.(string); ok { 62 varFormat = format 63 } 64 break 65 } 66 } 67 68 cases := getCases(loc, innerMap) 69 70 if len(cases) > 0 { 71 // cases = sortCases(cases) 72 vars = append(vars, Var{ 73 Name: k, 74 Literal: "${" + k + "}", 75 Cases: cases, 76 Format: varFormat, 77 Argth: 1, 78 }) 79 } 80 } 81 } 82 83 delete(src, key) // delete the key after. 84 return vars 85 } 86 87 var unescapeVariableRegex = regexp.MustCompile("\\$\\{(.*?)}") 88 89 func sortVars(text string, vars []Var) (newVars []Var) { 90 argth := 1 91 for _, submatches := range unescapeVariableRegex.FindAllStringSubmatch(text, -1) { 92 name := submatches[1] 93 for _, variable := range vars { 94 if variable.Name == name { 95 variable.Argth = argth 96 newVars = append(newVars, variable) 97 argth++ 98 break 99 } 100 } 101 } 102 103 sort.SliceStable(newVars, func(i, j int) bool { 104 return newVars[i].Argth < newVars[j].Argth 105 }) 106 return 107 } 108 109 // it will panic if the incoming "elements" are not catmsg.Var (internal text package). 110 func removeVarsDuplicates(elements []Var) (result []Var) { 111 seen := make(map[string]struct{}) 112 113 for v := range elements { 114 variable := elements[v] 115 name := variable.Name 116 if _, ok := seen[name]; !ok { 117 seen[name] = struct{}{} 118 result = append(result, variable) 119 } 120 } 121 122 return result 123 } 124 125 func removeMsgVarsDuplicates(elements []catalog.Message) (result []catalog.Message) { 126 seen := make(map[string]struct{}) 127 128 for _, elem := range elements { 129 val := reflect.Indirect(reflect.ValueOf(elem)) 130 if val.Type().String() != "catmsg.Var" { 131 // keep. 132 result = append(result, elem) 133 continue // it's not a var. 134 } 135 name := val.FieldByName("Name").Interface().(string) 136 if _, ok := seen[name]; !ok { 137 seen[name] = struct{}{} 138 result = append(result, elem) 139 } 140 } 141 142 return 143 } 144 145 func getCases(loc *Locale, src map[string]interface{}) []interface{} { 146 type PluralCase struct { 147 Form PluralForm 148 Value interface{} 149 } 150 151 pluralCases := make([]PluralCase, 0, len(src)) 152 153 for key, value := range src { 154 form, ok := loc.Options.PluralFormDecoder(loc, key) 155 if !ok { 156 continue 157 } 158 159 pluralCases = append(pluralCases, PluralCase{ 160 Form: form, 161 Value: value, 162 }) 163 } 164 165 if len(pluralCases) == 0 { 166 return nil 167 } 168 169 sort.SliceStable(pluralCases, func(i, j int) bool { 170 left, right := pluralCases[i].Form, pluralCases[j].Form 171 return left.Less(right) 172 }) 173 174 cases := make([]interface{}, 0, len(pluralCases)*2) 175 for _, pluralCase := range pluralCases { 176 // fmt.Printf("%s=%v\n", pluralCase.Form, pluralCase.Value) 177 cases = append(cases, pluralCase.Form.String()) 178 cases = append(cases, pluralCase.Value) 179 } 180 181 return cases 182 }