dict.go (2978B)
1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package catalog 6 7 import ( 8 "sync" 9 10 "golang.org/x/text/internal" 11 "golang.org/x/text/internal/catmsg" 12 "golang.org/x/text/language" 13 ) 14 15 // A Message holds a collection of translations for the same phrase that may 16 // vary based on the values of substitution arguments. 17 type Message = catmsg.Message 18 19 // TODO: 20 // Dictionary returns a Dictionary that returns the first Message, using the 21 // given language tag, that matches: 22 // 1. the last one registered by one of the Set methods 23 // 2. returned by one of the Loaders 24 // 3. repeat from 1. using the parent language 25 // This approach allows messages to be underspecified. 26 // func (c *Catalog) Dictionary(tag language.Tag) (Dictionary, error) { 27 // // TODO: verify dictionary exists. 28 // return &dict{&c.index, tag}, nil 29 // } 30 31 type dict struct { 32 s *store 33 tag language.Tag // TODO: make compact tag. 34 } 35 36 func (d *dict) Lookup(key string) (data string, ok bool) { 37 return d.s.lookup(d.tag, key) 38 } 39 40 func (b *Builder) lookup(tag language.Tag, key string) (data string, ok bool) { 41 return b.index.lookup(tag, key) 42 } 43 44 func (c *Builder) set(tag language.Tag, key string, s *store, msg ...Message) error { 45 data, err := catmsg.Compile(tag, &dict{&c.macros, tag}, catmsg.FirstOf(msg)) 46 47 s.mutex.Lock() 48 defer s.mutex.Unlock() 49 50 m := s.index[tag] 51 if m == nil { 52 m = msgMap{} 53 if s.index == nil { 54 s.index = map[language.Tag]msgMap{} 55 } 56 c.matcher = nil 57 s.index[tag] = m 58 } 59 60 m[key] = data 61 return err 62 } 63 64 func (c *Builder) Matcher() language.Matcher { 65 c.index.mutex.RLock() 66 m := c.matcher 67 c.index.mutex.RUnlock() 68 if m != nil { 69 return m 70 } 71 72 c.index.mutex.Lock() 73 if c.matcher == nil { 74 c.matcher = language.NewMatcher(c.unlockedLanguages()) 75 } 76 m = c.matcher 77 c.index.mutex.Unlock() 78 return m 79 } 80 81 type store struct { 82 mutex sync.RWMutex 83 index map[language.Tag]msgMap 84 } 85 86 type msgMap map[string]string 87 88 func (s *store) lookup(tag language.Tag, key string) (data string, ok bool) { 89 s.mutex.RLock() 90 defer s.mutex.RUnlock() 91 92 for ; ; tag = tag.Parent() { 93 if msgs, ok := s.index[tag]; ok { 94 if msg, ok := msgs[key]; ok { 95 return msg, true 96 } 97 } 98 if tag == language.Und { 99 break 100 } 101 } 102 return "", false 103 } 104 105 // Languages returns all languages for which the Catalog contains variants. 106 func (b *Builder) Languages() []language.Tag { 107 s := &b.index 108 s.mutex.RLock() 109 defer s.mutex.RUnlock() 110 111 return b.unlockedLanguages() 112 } 113 114 func (b *Builder) unlockedLanguages() []language.Tag { 115 s := &b.index 116 if len(s.index) == 0 { 117 return nil 118 } 119 tags := make([]language.Tag, 0, len(s.index)) 120 _, hasFallback := s.index[b.options.fallback] 121 offset := 0 122 if hasFallback { 123 tags = append(tags, b.options.fallback) 124 offset = 1 125 } 126 for t := range s.index { 127 if t != b.options.fallback { 128 tags = append(tags, t) 129 } 130 } 131 internal.SortTags(tags[offset:]) 132 return tags 133 }