summaryrefslogtreecommitdiffstats
path: root/vendor/golang.org/x/text/message/catalog/catalog.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/text/message/catalog/catalog.go')
-rw-r--r--vendor/golang.org/x/text/message/catalog/catalog.go161
1 files changed, 119 insertions, 42 deletions
diff --git a/vendor/golang.org/x/text/message/catalog/catalog.go b/vendor/golang.org/x/text/message/catalog/catalog.go
index 957444c10..34a30d3c8 100644
--- a/vendor/golang.org/x/text/message/catalog/catalog.go
+++ b/vendor/golang.org/x/text/message/catalog/catalog.go
@@ -156,23 +156,127 @@ import (
"errors"
"fmt"
+ "golang.org/x/text/internal"
+
"golang.org/x/text/internal/catmsg"
"golang.org/x/text/language"
)
-// A Catalog holds translations for messages for supported languages.
-type Catalog struct {
+// A Catalog allows lookup of translated messages.
+type Catalog interface {
+ // Languages returns all languages for which the Catalog contains variants.
+ Languages() []language.Tag
+
+ // Matcher returns a Matcher for languages from this Catalog.
+ Matcher() language.Matcher
+
+ // A Context is used for evaluating Messages.
+ Context(tag language.Tag, r catmsg.Renderer) *Context
+
+ // This method also makes Catalog a private interface.
+ lookup(tag language.Tag, key string) (data string, ok bool)
+}
+
+// NewFromMap creates a Catalog from the given map. If a Dictionary is
+// underspecified the entry is retrieved from a parent language.
+func NewFromMap(dictionaries map[string]Dictionary, opts ...Option) (Catalog, error) {
+ options := options{}
+ for _, o := range opts {
+ o(&options)
+ }
+ c := &catalog{
+ dicts: map[language.Tag]Dictionary{},
+ }
+ _, hasFallback := dictionaries[options.fallback.String()]
+ if hasFallback {
+ // TODO: Should it be okay to not have a fallback language?
+ // Catalog generators could enforce there is always a fallback.
+ c.langs = append(c.langs, options.fallback)
+ }
+ for lang, dict := range dictionaries {
+ tag, err := language.Parse(lang)
+ if err != nil {
+ return nil, fmt.Errorf("catalog: invalid language tag %q", lang)
+ }
+ if _, ok := c.dicts[tag]; ok {
+ return nil, fmt.Errorf("catalog: duplicate entry for tag %q after normalization", tag)
+ }
+ c.dicts[tag] = dict
+ if !hasFallback || tag != options.fallback {
+ c.langs = append(c.langs, tag)
+ }
+ }
+ if hasFallback {
+ internal.SortTags(c.langs[1:])
+ } else {
+ internal.SortTags(c.langs)
+ }
+ c.matcher = language.NewMatcher(c.langs)
+ return c, nil
+}
+
+// A Dictionary is a source of translations for a single language.
+type Dictionary interface {
+ // Lookup returns a message compiled with catmsg.Compile for the given key.
+ // It returns false for ok if such a message could not be found.
+ Lookup(key string) (data string, ok bool)
+}
+
+type catalog struct {
+ langs []language.Tag
+ dicts map[language.Tag]Dictionary
+ macros store
+ matcher language.Matcher
+}
+
+func (c *catalog) Languages() []language.Tag { return c.langs }
+func (c *catalog) Matcher() language.Matcher { return c.matcher }
+
+func (c *catalog) lookup(tag language.Tag, key string) (data string, ok bool) {
+ for ; ; tag = tag.Parent() {
+ if dict, ok := c.dicts[tag]; ok {
+ if data, ok := dict.Lookup(key); ok {
+ return data, true
+ }
+ }
+ if tag == language.Und {
+ break
+ }
+ }
+ return "", false
+}
+
+// Context returns a Context for formatting messages.
+// Only one Message may be formatted per context at any given time.
+func (c *catalog) Context(tag language.Tag, r catmsg.Renderer) *Context {
+ return &Context{
+ cat: c,
+ tag: tag,
+ dec: catmsg.NewDecoder(tag, r, &dict{&c.macros, tag}),
+ }
+}
+
+// A Builder allows building a Catalog programmatically.
+type Builder struct {
options
+ matcher language.Matcher
index store
macros store
}
-type options struct{}
+type options struct {
+ fallback language.Tag
+}
// An Option configures Catalog behavior.
type Option func(*options)
+// Fallback specifies the default fallback language. The default is Und.
+func Fallback(tag language.Tag) Option {
+ return func(o *options) { o.fallback = tag }
+}
+
// TODO:
// // Catalogs specifies one or more sources for a Catalog.
// // Lookups are in order.
@@ -186,22 +290,17 @@ type Option func(*options)
//
// func Dict(tag language.Tag, d ...Dictionary) Option
-// New returns a new Catalog.
-func New(opts ...Option) *Catalog {
- c := &Catalog{}
+// NewBuilder returns an empty mutable Catalog.
+func NewBuilder(opts ...Option) *Builder {
+ c := &Builder{}
for _, o := range opts {
o(&c.options)
}
return c
}
-// Languages returns all languages for which the Catalog contains variants.
-func (c *Catalog) Languages() []language.Tag {
- return c.index.languages()
-}
-
// SetString is shorthand for Set(tag, key, String(msg)).
-func (c *Catalog) SetString(tag language.Tag, key string, msg string) error {
+func (c *Builder) SetString(tag language.Tag, key string, msg string) error {
return c.set(tag, key, &c.index, String(msg))
}
@@ -209,26 +308,20 @@ func (c *Catalog) SetString(tag language.Tag, key string, msg string) error {
//
// When evaluation this message, the first Message in the sequence to msgs to
// evaluate to a string will be the message returned.
-func (c *Catalog) Set(tag language.Tag, key string, msg ...Message) error {
+func (c *Builder) Set(tag language.Tag, key string, msg ...Message) error {
return c.set(tag, key, &c.index, msg...)
}
// SetMacro defines a Message that may be substituted in another message.
// The arguments to a macro Message are passed as arguments in the
// placeholder the form "${foo(arg1, arg2)}".
-func (c *Catalog) SetMacro(tag language.Tag, name string, msg ...Message) error {
+func (c *Builder) SetMacro(tag language.Tag, name string, msg ...Message) error {
return c.set(tag, name, &c.macros, msg...)
}
// ErrNotFound indicates there was no message for the given key.
var ErrNotFound = errors.New("catalog: message not found")
-// A Message holds a collection of translations for the same phrase that may
-// vary based on the values of substitution arguments.
-type Message interface {
- catmsg.Message
-}
-
// String specifies a plain message string. It can be used as fallback if no
// other strings match or as a simple standalone message.
//
@@ -247,44 +340,28 @@ func Var(name string, msg ...Message) Message {
return &catmsg.Var{Name: name, Message: firstInSequence(msg)}
}
-// firstInSequence is a message type that prints the first message in the
-// sequence that resolves to a match for the given substitution arguments.
-type firstInSequence []Message
-
-func (s firstInSequence) Compile(e *catmsg.Encoder) error {
- e.EncodeMessageType(catmsg.First)
- err := catmsg.ErrIncomplete
- for i, m := range s {
- if err == nil {
- return fmt.Errorf("catalog: message argument %d is complete and blocks subsequent messages", i-1)
- }
- err = e.EncodeMessage(m)
- }
- return err
-}
-
// Context returns a Context for formatting messages.
// Only one Message may be formatted per context at any given time.
-func (c *Catalog) Context(tag language.Tag, r catmsg.Renderer) *Context {
+func (b *Builder) Context(tag language.Tag, r catmsg.Renderer) *Context {
return &Context{
- cat: c,
+ cat: b,
tag: tag,
- dec: catmsg.NewDecoder(tag, r, &dict{&c.macros, tag}),
+ dec: catmsg.NewDecoder(tag, r, &dict{&b.macros, tag}),
}
}
// A Context is used for evaluating Messages.
// Only one Message may be formatted per context at any given time.
type Context struct {
- cat *Catalog
- tag language.Tag
+ cat Catalog
+ tag language.Tag // TODO: use compact index.
dec *catmsg.Decoder
}
// Execute looks up and executes the message with the given key.
// It returns ErrNotFound if no message could be found in the index.
func (c *Context) Execute(key string) error {
- data, ok := c.cat.index.lookup(c.tag, key)
+ data, ok := c.cat.lookup(c.tag, key)
if !ok {
return ErrNotFound
}