| // Copyright 2017 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package catalog |
| |
| import ( |
| "sync" |
| |
| "golang.org/x/text/internal" |
| "golang.org/x/text/internal/catmsg" |
| "golang.org/x/text/language" |
| ) |
| |
| // TODO: |
| // Dictionary returns a Dictionary that returns the first Message, using the |
| // given language tag, that matches: |
| // 1. the last one registered by one of the Set methods |
| // 2. returned by one of the Loaders |
| // 3. repeat from 1. using the parent language |
| // This approach allows messages to be underspecified. |
| // func (c *Catalog) Dictionary(tag language.Tag) (Dictionary, error) { |
| // // TODO: verify dictionary exists. |
| // return &dict{&c.index, tag}, nil |
| // } |
| |
| type dict struct { |
| s *store |
| tag language.Tag // TODO: make compact tag. |
| } |
| |
| func (d *dict) Lookup(key string) (data string, ok bool) { |
| return d.s.lookup(d.tag, key) |
| } |
| |
| func (b *Builder) lookup(tag language.Tag, key string) (data string, ok bool) { |
| return b.index.lookup(tag, key) |
| } |
| |
| func (c *Builder) set(tag language.Tag, key string, s *store, msg ...Message) error { |
| data, err := catmsg.Compile(tag, &dict{&c.macros, tag}, firstInSequence(msg)) |
| |
| s.mutex.Lock() |
| defer s.mutex.Unlock() |
| |
| m := s.index[tag] |
| if m == nil { |
| m = msgMap{} |
| if s.index == nil { |
| s.index = map[language.Tag]msgMap{} |
| } |
| c.matcher = nil |
| s.index[tag] = m |
| } |
| |
| m[key] = data |
| return err |
| } |
| |
| func (c *Builder) Matcher() language.Matcher { |
| c.index.mutex.RLock() |
| m := c.matcher |
| c.index.mutex.RUnlock() |
| if m != nil { |
| return m |
| } |
| |
| c.index.mutex.Lock() |
| if c.matcher == nil { |
| c.matcher = language.NewMatcher(c.unlockedLanguages()) |
| } |
| m = c.matcher |
| c.index.mutex.Unlock() |
| return m |
| } |
| |
| type store struct { |
| mutex sync.RWMutex |
| index map[language.Tag]msgMap |
| } |
| |
| type msgMap map[string]string |
| |
| func (s *store) lookup(tag language.Tag, key string) (data string, ok bool) { |
| s.mutex.RLock() |
| defer s.mutex.RUnlock() |
| |
| for ; ; tag = tag.Parent() { |
| if msgs, ok := s.index[tag]; ok { |
| if msg, ok := msgs[key]; ok { |
| return msg, true |
| } |
| } |
| if tag == language.Und { |
| break |
| } |
| } |
| return "", false |
| } |
| |
| // Languages returns all languages for which the Catalog contains variants. |
| func (b *Builder) Languages() []language.Tag { |
| s := &b.index |
| s.mutex.RLock() |
| defer s.mutex.RUnlock() |
| |
| return b.unlockedLanguages() |
| } |
| |
| func (b *Builder) unlockedLanguages() []language.Tag { |
| s := &b.index |
| if len(s.index) == 0 { |
| return nil |
| } |
| tags := make([]language.Tag, 0, len(s.index)) |
| _, hasFallback := s.index[b.options.fallback] |
| offset := 0 |
| if hasFallback { |
| tags = append(tags, b.options.fallback) |
| offset = 1 |
| } |
| for t := range s.index { |
| if t != b.options.fallback { |
| tags = append(tags, t) |
| } |
| } |
| internal.SortTags(tags[offset:]) |
| return tags |
| } |