| // 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" |
| |
| "golang.org/x/text/language" |
| "golang.org/x/text/message/catalog" |
| ) |
| |
| // TODO: allow more than one goroutine per printer. This will allow porting from |
| // fmt much less error prone. |
| |
| // A Printer implements language-specific formatted I/O analogous to the fmt |
| // package. Only one goroutine may use a Printer at the same time. |
| type Printer struct { |
| // Wrap the fields in a hidden type to hide some of the implemented methods. |
| printer printer |
| |
| // NOTE: limiting one goroutine per Printer allows for many optimizations |
| // and simplifications. We can consider removing this restriction down the |
| // road if it the benefits do not seem to outweigh the disadvantages. |
| } |
| |
| 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{printer{ |
| tag: t, |
| }} |
| p.printer.toDecimal.InitDecimal(t) |
| p.printer.toScientific.InitScientific(t) |
| p.printer.catContext = options.cat.Context(t, &p.printer) |
| return p |
| } |
| |
| // Sprint is like fmt.Sprint, but using language-specific formatting. |
| func (p *Printer) Sprint(a ...interface{}) string { |
| p.printer.reset() |
| p.printer.doPrint(a) |
| return p.printer.String() |
| } |
| |
| // Fprint is like fmt.Fprint, but using language-specific formatting. |
| func (p *Printer) Fprint(w io.Writer, a ...interface{}) (n int, err error) { |
| p.printer.reset() |
| p.printer.doPrint(a) |
| n64, err := io.Copy(w, &p.printer.Buffer) |
| 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 { |
| p.printer.reset() |
| p.printer.doPrintln(a) |
| return p.printer.String() |
| } |
| |
| // Fprintln is like fmt.Fprintln, but using language-specific formatting. |
| func (p *Printer) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { |
| p.printer.reset() |
| p.printer.doPrintln(a) |
| n64, err := io.Copy(w, &p.printer.Buffer) |
| 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 { |
| lookupAndFormat(p, key, a) |
| return p.printer.String() |
| } |
| |
| // 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) { |
| lookupAndFormat(p, key, a) |
| return w.Write(p.printer.Bytes()) |
| } |
| |
| // Printf is like fmt.Printf, but using language-specific formatting. |
| func (p *Printer) Printf(key Reference, a ...interface{}) (n int, err error) { |
| lookupAndFormat(p, key, a) |
| return os.Stdout.Write(p.printer.Bytes()) |
| } |
| |
| func lookupAndFormat(p *Printer, r Reference, a []interface{}) { |
| p.printer.reset() |
| p.printer.args = a |
| var id, msg string |
| switch v := r.(type) { |
| case string: |
| id, msg = v, v |
| case key: |
| id, msg = v.id, v.fallback |
| default: |
| panic("key argument is not a Reference") |
| } |
| |
| if p.printer.catContext.Execute(id) == catalog.ErrNotFound { |
| if p.printer.catContext.Execute(msg) == catalog.ErrNotFound { |
| p.printer.Render(msg) |
| return |
| } |
| } |
| } |
| |
| // Arg implements catmsg.Renderer. |
| func (p *printer) Arg(i int) interface{} { // TODO, also return "ok" bool |
| i-- |
| if uint(i) < uint(len(p.args)) { |
| return p.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 |
| } |