You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

194 lines
4.6 KiB

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