locale.go (4259B)
1 package internal 2 3 import ( 4 "fmt" 5 "text/template" 6 7 "golang.org/x/text/language" 8 "golang.org/x/text/message" 9 "golang.org/x/text/message/catalog" 10 ) 11 12 // Locale is the default Locale. 13 // Created by Catalog. 14 // One Locale maps to one registered and loaded language. 15 // Stores the translation variables and most importantly, the Messages (keys and their renderers). 16 type Locale struct { 17 // The index of the language registered by the user, starting from zero. 18 index int 19 tag language.Tag 20 // ID is the tag.String(). 21 ID string 22 // Options given by the Catalog 23 Options Options 24 25 // Fields set by Catalog. 26 FuncMap template.FuncMap 27 Printer *message.Printer 28 // 29 30 // Fields set by this Load method. 31 Messages map[string]Renderer 32 Vars []Var // shared per-locale variables. 33 } 34 35 // Load sets the translation messages based on the Catalog's key values. 36 func (loc *Locale) Load(c *Catalog, keyValues Map) error { 37 return loc.setMap(c, "", keyValues) 38 } 39 40 func (loc *Locale) setMap(c *Catalog, key string, keyValues Map) error { 41 // unique locals or the shared ones. 42 isRoot := key == "" 43 44 vars := getVars(loc, VarsKey, keyValues) 45 if isRoot { 46 loc.Vars = vars 47 } else { 48 vars = removeVarsDuplicates(append(vars, loc.Vars...)) 49 } 50 51 for k, v := range keyValues { 52 form, isPlural := loc.Options.PluralFormDecoder(loc, k) 53 if isPlural { 54 k = key 55 } else if !isRoot { 56 k = key + "." + k 57 } 58 59 switch value := v.(type) { 60 case string: 61 if err := loc.setString(c, k, value, vars, form); err != nil { 62 return fmt.Errorf("%s:%s parse string: %w", loc.ID, key, err) 63 } 64 case Map: 65 // fmt.Printf("%s is map\n", fullKey) 66 if err := loc.setMap(c, k, value); err != nil { 67 return fmt.Errorf("%s:%s parse map: %w", loc.ID, key, err) 68 } 69 70 default: 71 return fmt.Errorf("%s:%s unexpected type of %T as value", loc.ID, key, value) 72 } 73 } 74 75 return nil 76 } 77 78 func (loc *Locale) setString(c *Catalog, key string, value string, vars []Var, form PluralForm) (err error) { 79 isPlural := form != nil 80 81 // fmt.Printf("setStringVars: %s=%s\n", key, value) 82 msgs, vars := makeSelectfVars(value, vars, isPlural) 83 msgs = append(msgs, catalog.String(value)) 84 85 m := &Message{ 86 Locale: loc, 87 Key: key, 88 Value: value, 89 Vars: vars, 90 Plural: isPlural, 91 } 92 93 var ( 94 renderer, pluralRenderer Renderer = m, m 95 ) 96 97 if stringIsTemplateValue(value, loc.Options.Left, loc.Options.Right) { 98 t, err := NewTemplate(c, m) 99 if err != nil { 100 return err 101 } 102 103 pluralRenderer = t 104 if !isPlural { 105 renderer = t 106 } 107 } else { 108 if isPlural { 109 pluralRenderer, err = newIndependentPluralRenderer(c, loc, key, msgs...) 110 if err != nil { 111 return fmt.Errorf("<%s = %s>: %w", key, value, err) 112 } 113 } else { 114 // let's make normal keys direct fire: 115 // renderer = &simpleRenderer{key, loc.Printer} 116 if err = c.Set(loc.tag, key, msgs...); err != nil { 117 return fmt.Errorf("<%s = %s>: %w", key, value, err) 118 } 119 } 120 121 } 122 123 if isPlural { 124 if existingMsg, ok := loc.Messages[key]; ok { 125 if msg, ok := existingMsg.(*Message); ok { 126 msg.AddPlural(form, pluralRenderer) 127 return 128 } 129 } 130 131 m.AddPlural(form, pluralRenderer) 132 } 133 134 loc.Messages[key] = renderer 135 return 136 } 137 138 // Index returns the current locale index from the languages list. 139 func (loc *Locale) Index() int { 140 return loc.index 141 } 142 143 // Tag returns the full language Tag attached to this Locale, 144 // it should be unique across different Locales. 145 func (loc *Locale) Tag() *language.Tag { 146 return &loc.tag 147 } 148 149 // Language should return the exact languagecode of this `Locale` 150 // that the user provided on `New` function. 151 // 152 // Same as `Tag().String()` but it's static. 153 func (loc *Locale) Language() string { 154 return loc.ID 155 } 156 157 // GetMessage should return translated text based on the given "key". 158 func (loc *Locale) GetMessage(key string, args ...interface{}) string { 159 return loc.getMessage(loc.ID, key, args...) 160 } 161 162 func (loc *Locale) getMessage(langInput, key string, args ...interface{}) string { 163 if msg, ok := loc.Messages[key]; ok { 164 result, err := msg.Render(args...) 165 if err != nil { 166 result = err.Error() 167 } 168 169 return result 170 } 171 172 if fn := loc.Options.DefaultMessageFunc; fn != nil { 173 // let langInput to be empty if that's the case. 174 return fn(langInput, loc.ID, key, args...) 175 } 176 177 return "" 178 }