package internal import ( "regexp" "sort" ) // Var represents a message variable. // The variables, like the sub messages are sorted. // First: plurals (which again, are sorted) // and then any custom keys. // In variables, the sorting depends on the exact // order the associated message uses the variables. // This is extremely handy. // This package requires the golang.org/x/text/message capabilities // only for the variables feature, the message itself's pluralization is managed by the package. type Var struct { Name string // Variable name, e.g. Name Literal string // Its literal is ${Name} Cases []interface{} // one:...,few:...,... Format string // defaults to "%d". Argth int // 1, 2, 3... } func getVars(loc *Locale, key string, src map[string]interface{}) []Var { if len(src) == 0 { return nil } varsKey, ok := src[key] if !ok { return nil } varValue, ok := varsKey.([]interface{}) if !ok { return nil } vars := make([]Var, 0, len(varValue)) for _, v := range varValue { m, ok := v.(map[string]interface{}) if !ok { continue } for k, inner := range m { varFormat := "%d" innerMap, ok := inner.(map[string]interface{}) if !ok { continue } for kk, vv := range innerMap { if kk == "format" { if format, ok := vv.(string); ok { varFormat = format } break } } cases := getCases(loc, innerMap) if len(cases) > 0 { // cases = sortCases(cases) vars = append(vars, Var{ Name: k, Literal: "${" + k + "}", Cases: cases, Format: varFormat, Argth: 1, }) } } } delete(src, key) // delete the key after. return vars } var unescapeVariableRegex = regexp.MustCompile(`\$\{(.*?)}`) func sortVars(text string, vars []Var) (newVars []Var) { argth := 1 for _, submatches := range unescapeVariableRegex.FindAllStringSubmatch(text, -1) { name := submatches[1] for _, variable := range vars { if variable.Name == name { variable.Argth = argth newVars = append(newVars, variable) argth++ break } } } sort.SliceStable(newVars, func(i, j int) bool { return newVars[i].Argth < newVars[j].Argth }) return } // it will panic if the incoming "elements" are not catmsg.Var (internal text package). func removeVarsDuplicates(elements []Var) (result []Var) { seen := make(map[string]struct{}) for v := range elements { variable := elements[v] name := variable.Name if _, ok := seen[name]; !ok { seen[name] = struct{}{} result = append(result, variable) } } return result } /* func removeMsgVarsDuplicates(elements []catalog.Message) (result []catalog.Message) { seen := make(map[string]struct{}) for _, elem := range elements { val := reflect.Indirect(reflect.ValueOf(elem)) if val.Type().String() != "catmsg.Var" { // keep. result = append(result, elem) continue // it's not a var. } name := val.FieldByName("Name").Interface().(string) if _, ok := seen[name]; !ok { seen[name] = struct{}{} result = append(result, elem) } } return } */ func getCases(loc *Locale, src map[string]interface{}) []interface{} { type PluralCase struct { Form PluralForm Value interface{} } pluralCases := make([]PluralCase, 0, len(src)) for key, value := range src { form, ok := loc.Options.PluralFormDecoder(loc, key) if !ok { continue } pluralCases = append(pluralCases, PluralCase{ Form: form, Value: value, }) } if len(pluralCases) == 0 { return nil } sort.SliceStable(pluralCases, func(i, j int) bool { left, right := pluralCases[i].Form, pluralCases[j].Form return left.Less(right) }) cases := make([]interface{}, 0, len(pluralCases)*2) for _, pluralCase := range pluralCases { // fmt.Printf("%s=%v\n", pluralCase.Form, pluralCase.Value) cases = append(cases, pluralCase.Form.String(), pluralCase.Value) } return cases }