| // Copyright 2015 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 message // import "golang.org/x/text/message" | |
| import ( | |
| "io" | |
| "os" | |
| // Include features to facilitate generated catalogs. | |
| _ "golang.org/x/text/feature/plural" | |
| "golang.org/x/text/internal/number" | |
| "golang.org/x/text/language" | |
| "golang.org/x/text/message/catalog" | |
| ) | |
| // A Printer implements language-specific formatted I/O analogous to the fmt | |
| // package. | |
| type Printer struct { | |
| // the language | |
| tag language.Tag | |
| toDecimal number.Formatter | |
| toScientific number.Formatter | |
| cat catalog.Catalog | |
| } | |
| type options struct { | |
| cat catalog.Catalog | |
| // TODO: | |
| // - allow %s to print integers in written form (tables are likely too large | |
| // to enable this by default). | |
| // - list behavior | |
| // | |
| } | |
| // An Option defines an option of a Printer. | |
| type Option func(o *options) | |
| // Catalog defines the catalog to be used. | |
| func Catalog(c catalog.Catalog) Option { | |
| return func(o *options) { o.cat = c } | |
| } | |
| // NewPrinter returns a Printer that formats messages tailored to language t. | |
| func NewPrinter(t language.Tag, opts ...Option) *Printer { | |
| options := &options{ | |
| cat: DefaultCatalog, | |
| } | |
| for _, o := range opts { | |
| o(options) | |
| } | |
| p := &Printer{ | |
| tag: t, | |
| cat: options.cat, | |
| } | |
| p.toDecimal.InitDecimal(t) | |
| p.toScientific.InitScientific(t) | |
| return p | |
| } | |
| // Sprint is like fmt.Sprint, but using language-specific formatting. | |
| func (p *Printer) Sprint(a ...interface{}) string { | |
| pp := newPrinter(p) | |
| pp.doPrint(a) | |
| s := pp.String() | |
| pp.free() | |
| return s | |
| } | |
| // Fprint is like fmt.Fprint, but using language-specific formatting. | |
| func (p *Printer) Fprint(w io.Writer, a ...interface{}) (n int, err error) { | |
| pp := newPrinter(p) | |
| pp.doPrint(a) | |
| n64, err := io.Copy(w, &pp.Buffer) | |
| pp.free() | |
| return int(n64), err | |
| } | |
| // Print is like fmt.Print, but using language-specific formatting. | |
| func (p *Printer) Print(a ...interface{}) (n int, err error) { | |
| return p.Fprint(os.Stdout, a...) | |
| } | |
| // Sprintln is like fmt.Sprintln, but using language-specific formatting. | |
| func (p *Printer) Sprintln(a ...interface{}) string { | |
| pp := newPrinter(p) | |
| pp.doPrintln(a) | |
| s := pp.String() | |
| pp.free() | |
| return s | |
| } | |
| // Fprintln is like fmt.Fprintln, but using language-specific formatting. | |
| func (p *Printer) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { | |
| pp := newPrinter(p) | |
| pp.doPrintln(a) | |
| n64, err := io.Copy(w, &pp.Buffer) | |
| pp.free() | |
| return int(n64), err | |
| } | |
| // Println is like fmt.Println, but using language-specific formatting. | |
| func (p *Printer) Println(a ...interface{}) (n int, err error) { | |
| return p.Fprintln(os.Stdout, a...) | |
| } | |
| // Sprintf is like fmt.Sprintf, but using language-specific formatting. | |
| func (p *Printer) Sprintf(key Reference, a ...interface{}) string { | |
| pp := newPrinter(p) | |
| lookupAndFormat(pp, key, a) | |
| s := pp.String() | |
| pp.free() | |
| return s | |
| } | |
| // Fprintf is like fmt.Fprintf, but using language-specific formatting. | |
| func (p *Printer) Fprintf(w io.Writer, key Reference, a ...interface{}) (n int, err error) { | |
| pp := newPrinter(p) | |
| lookupAndFormat(pp, key, a) | |
| n, err = w.Write(pp.Bytes()) | |
| pp.free() | |
| return n, err | |
| } | |
| // Printf is like fmt.Printf, but using language-specific formatting. | |
| func (p *Printer) Printf(key Reference, a ...interface{}) (n int, err error) { | |
| pp := newPrinter(p) | |
| lookupAndFormat(pp, key, a) | |
| n, err = os.Stdout.Write(pp.Bytes()) | |
| pp.free() | |
| return n, err | |
| } | |
| func lookupAndFormat(p *printer, r Reference, a []interface{}) { | |
| p.fmt.Reset(a) | |
| switch v := r.(type) { | |
| case string: | |
| if p.catContext.Execute(v) == catalog.ErrNotFound { | |
| p.Render(v) | |
| return | |
| } | |
| case key: | |
| if p.catContext.Execute(v.id) == catalog.ErrNotFound && | |
| p.catContext.Execute(v.fallback) == catalog.ErrNotFound { | |
| p.Render(v.fallback) | |
| return | |
| } | |
| default: | |
| panic("key argument is not a Reference") | |
| } | |
| } | |
| type rawPrinter struct { | |
| p *printer | |
| } | |
| func (p rawPrinter) Render(msg string) { p.p.WriteString(msg) } | |
| func (p rawPrinter) Arg(i int) interface{} { return nil } | |
| // Arg implements catmsg.Renderer. | |
| func (p *printer) Arg(i int) interface{} { // TODO, also return "ok" bool | |
| i-- | |
| if uint(i) < uint(len(p.fmt.Args)) { | |
| return p.fmt.Args[i] | |
| } | |
| return nil | |
| } | |
| // Render implements catmsg.Renderer. | |
| func (p *printer) Render(msg string) { | |
| p.doPrintf(msg) | |
| } | |
| // A Reference is a string or a message reference. | |
| type Reference interface { | |
| // TODO: also allow []string | |
| } | |
| // Key creates a message Reference for a message where the given id is used for | |
| // message lookup and the fallback is returned when no matches are found. | |
| func Key(id string, fallback string) Reference { | |
| return key{id, fallback} | |
| } | |
| type key struct { | |
| id, fallback string | |
| } |