1// Copyright 2015 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 5package message // import "golang.org/x/text/message" 6 7import ( 8 "io" 9 "os" 10 11 // Include features to facilitate generated catalogs. 12 _ "golang.org/x/text/feature/plural" 13 14 "golang.org/x/text/internal/number" 15 "golang.org/x/text/language" 16 "golang.org/x/text/message/catalog" 17) 18 19// A Printer implements language-specific formatted I/O analogous to the fmt 20// package. 21type Printer struct { 22 // the language 23 tag language.Tag 24 25 toDecimal number.Formatter 26 toScientific number.Formatter 27 28 cat catalog.Catalog 29} 30 31type options struct { 32 cat catalog.Catalog 33 // TODO: 34 // - allow %s to print integers in written form (tables are likely too large 35 // to enable this by default). 36 // - list behavior 37 // 38} 39 40// An Option defines an option of a Printer. 41type Option func(o *options) 42 43// Catalog defines the catalog to be used. 44func Catalog(c catalog.Catalog) Option { 45 return func(o *options) { o.cat = c } 46} 47 48// NewPrinter returns a Printer that formats messages tailored to language t. 49func NewPrinter(t language.Tag, opts ...Option) *Printer { 50 options := &options{ 51 cat: DefaultCatalog, 52 } 53 for _, o := range opts { 54 o(options) 55 } 56 p := &Printer{ 57 tag: t, 58 cat: options.cat, 59 } 60 p.toDecimal.InitDecimal(t) 61 p.toScientific.InitScientific(t) 62 return p 63} 64 65// Sprint is like fmt.Sprint, but using language-specific formatting. 66func (p *Printer) Sprint(a ...interface{}) string { 67 pp := newPrinter(p) 68 pp.doPrint(a) 69 s := pp.String() 70 pp.free() 71 return s 72} 73 74// Fprint is like fmt.Fprint, but using language-specific formatting. 75func (p *Printer) Fprint(w io.Writer, a ...interface{}) (n int, err error) { 76 pp := newPrinter(p) 77 pp.doPrint(a) 78 n64, err := io.Copy(w, &pp.Buffer) 79 pp.free() 80 return int(n64), err 81} 82 83// Print is like fmt.Print, but using language-specific formatting. 84func (p *Printer) Print(a ...interface{}) (n int, err error) { 85 return p.Fprint(os.Stdout, a...) 86} 87 88// Sprintln is like fmt.Sprintln, but using language-specific formatting. 89func (p *Printer) Sprintln(a ...interface{}) string { 90 pp := newPrinter(p) 91 pp.doPrintln(a) 92 s := pp.String() 93 pp.free() 94 return s 95} 96 97// Fprintln is like fmt.Fprintln, but using language-specific formatting. 98func (p *Printer) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { 99 pp := newPrinter(p) 100 pp.doPrintln(a) 101 n64, err := io.Copy(w, &pp.Buffer) 102 pp.free() 103 return int(n64), err 104} 105 106// Println is like fmt.Println, but using language-specific formatting. 107func (p *Printer) Println(a ...interface{}) (n int, err error) { 108 return p.Fprintln(os.Stdout, a...) 109} 110 111// Sprintf is like fmt.Sprintf, but using language-specific formatting. 112func (p *Printer) Sprintf(key Reference, a ...interface{}) string { 113 pp := newPrinter(p) 114 lookupAndFormat(pp, key, a) 115 s := pp.String() 116 pp.free() 117 return s 118} 119 120// Fprintf is like fmt.Fprintf, but using language-specific formatting. 121func (p *Printer) Fprintf(w io.Writer, key Reference, a ...interface{}) (n int, err error) { 122 pp := newPrinter(p) 123 lookupAndFormat(pp, key, a) 124 n, err = w.Write(pp.Bytes()) 125 pp.free() 126 return n, err 127 128} 129 130// Printf is like fmt.Printf, but using language-specific formatting. 131func (p *Printer) Printf(key Reference, a ...interface{}) (n int, err error) { 132 pp := newPrinter(p) 133 lookupAndFormat(pp, key, a) 134 n, err = os.Stdout.Write(pp.Bytes()) 135 pp.free() 136 return n, err 137} 138 139func lookupAndFormat(p *printer, r Reference, a []interface{}) { 140 p.fmt.Reset(a) 141 var id, msg string 142 switch v := r.(type) { 143 case string: 144 id, msg = v, v 145 case key: 146 id, msg = v.id, v.fallback 147 default: 148 panic("key argument is not a Reference") 149 } 150 151 if p.catContext.Execute(id) == catalog.ErrNotFound { 152 if p.catContext.Execute(msg) == catalog.ErrNotFound { 153 p.Render(msg) 154 return 155 } 156 } 157} 158 159type rawPrinter struct { 160 p *printer 161} 162 163func (p rawPrinter) Render(msg string) { p.p.WriteString(msg) } 164func (p rawPrinter) Arg(i int) interface{} { return nil } 165 166// Arg implements catmsg.Renderer. 167func (p *printer) Arg(i int) interface{} { // TODO, also return "ok" bool 168 i-- 169 if uint(i) < uint(len(p.fmt.Args)) { 170 return p.fmt.Args[i] 171 } 172 return nil 173} 174 175// Render implements catmsg.Renderer. 176func (p *printer) Render(msg string) { 177 p.doPrintf(msg) 178} 179 180// A Reference is a string or a message reference. 181type Reference interface { 182 // TODO: also allow []string 183} 184 185// Key creates a message Reference for a message where the given id is used for 186// message lookup and the fallback is returned when no matches are found. 187func Key(id string, fallback string) Reference { 188 return key{id, fallback} 189} 190 191type key struct { 192 id, fallback string 193} 194