catalog.go (4077B)
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 // MessageFunc is the function type to modify the behavior when a key or language was not found. 13 // All language inputs fallback to the default locale if not matched. 14 // This is why this signature accepts both input and matched languages, so caller 15 // can provide better messages. 16 // 17 // The first parameter is set to the client real input of the language, 18 // the second one is set to the matched language (default one if input wasn't matched) 19 // and the third and forth are the translation format/key and its optional arguments. 20 // 21 // Note: we don't accept the Context here because Tr method and template func {{ tr }} 22 // have no direct access to it. 23 type MessageFunc func(langInput, langMatched, key string, args ...interface{}) string 24 25 // Catalog holds the locales and the variables message storage. 26 type Catalog struct { 27 builder *catalog.Builder 28 Locales []*Locale 29 } 30 31 // The Options of the Catalog and its Locales. 32 type Options struct { 33 // Left delimiter for template messages. 34 Left string 35 // Right delimeter for template messages. 36 Right string 37 // Enable strict mode. 38 Strict bool 39 // Optional functions for template messages per locale. 40 Funcs func(*Locale) template.FuncMap 41 // Optional function to be called when no message was found. 42 DefaultMessageFunc MessageFunc 43 // Customize the overall behavior of the plurazation feature. 44 PluralFormDecoder PluralFormDecoder 45 } 46 47 // NewCatalog returns a new Catalog based on the registered languages and the loader options. 48 func NewCatalog(languages []language.Tag, opts Options) (*Catalog, error) { // ordered languages, the first should be the default one. 49 if len(languages) == 0 { 50 return nil, fmt.Errorf("catalog: empty languages") 51 } 52 53 if opts.Left == "" { 54 opts.Left = "{{" 55 } 56 57 if opts.Right == "" { 58 opts.Right = "}}" 59 } 60 61 if opts.PluralFormDecoder == nil { 62 opts.PluralFormDecoder = DefaultPluralFormDecoder 63 } 64 65 builder := catalog.NewBuilder(catalog.Fallback(languages[0])) 66 67 locales := make([]*Locale, 0, len(languages)) 68 for idx, tag := range languages { 69 locale := &Locale{ 70 tag: tag, 71 index: idx, 72 ID: tag.String(), 73 Options: opts, 74 Printer: message.NewPrinter(tag, message.Catalog(builder)), 75 Messages: make(map[string]Renderer), 76 } 77 locale.FuncMap = getFuncs(locale) 78 79 locales = append(locales, locale) 80 } 81 82 c := &Catalog{ 83 builder: builder, 84 Locales: locales, 85 } 86 87 return c, nil 88 } 89 90 // Set sets a simple translation message. 91 func (c *Catalog) Set(tag language.Tag, key string, msgs ...catalog.Message) error { 92 // fmt.Printf("Catalog.Set[%s] %s:\n", tag.String(), key) 93 // for _, msg := range msgs { 94 // fmt.Printf("%#+v\n", msg) 95 // } 96 return c.builder.Set(tag, key, msgs...) 97 } 98 99 // Store stores the a map of values to the locale derives from the given "langIndex". 100 func (c *Catalog) Store(langIndex int, kv Map) error { 101 loc := c.getLocale(langIndex) 102 if loc == nil { 103 return fmt.Errorf("expected language index to be lower or equal than %d but got %d", len(c.Locales), langIndex) 104 } 105 return loc.Load(c, kv) 106 } 107 108 /* Localizer interface. */ 109 110 // SetDefault changes the default language based on the "index". 111 // See `I18n#SetDefault` method for more. 112 func (c *Catalog) SetDefault(index int) bool { 113 if index < 0 { 114 index = 0 115 } 116 117 if maxIdx := len(c.Locales) - 1; index > maxIdx { 118 return false 119 } 120 121 // callers should protect with mutex if called at serve-time. 122 loc := c.Locales[index] 123 loc.index = 0 124 f := c.Locales[0] 125 c.Locales[0] = loc 126 f.index = index 127 c.Locales[index] = f 128 return true 129 } 130 131 // GetLocale returns a valid `Locale` based on the "index". 132 func (c *Catalog) GetLocale(index int) *Locale { 133 return c.getLocale(index) 134 } 135 136 func (c *Catalog) getLocale(index int) *Locale { 137 if index < 0 { 138 index = 0 139 } 140 141 if maxIdx := len(c.Locales) - 1; index > maxIdx { 142 // panic("expected language index to be lower or equal than %d but got %d", maxIdx, langIndex) 143 return nil 144 } 145 146 loc := c.Locales[index] 147 return loc 148 }