github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/text/message/message.go (about)

     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  
     5  // Package message implements formatted I/O for localized strings with functions
     6  // analogous to the fmt's print functions.
     7  //
     8  // NOTE: Under construction. See https://golang.org/design/text/12750-localization
     9  // and its corresponding proposal issue https://golang.org/issues/12750.
    10  package message // import "golang.org/x/text/message"
    11  
    12  import (
    13  	"fmt"
    14  	"io"
    15  	"strings"
    16  
    17  	"golang.org/x/text/internal/format"
    18  	"golang.org/x/text/language"
    19  )
    20  
    21  // A Printer implements language-specific formatted I/O analogous to the fmt
    22  // package. Only one goroutine may use a Printer at the same time.
    23  type Printer struct {
    24  	tag language.Tag
    25  
    26  	cat *Catalog
    27  
    28  	// NOTE: limiting one goroutine per Printer allows for many optimizations
    29  	// and simplifications. We can consider removing this restriction down the
    30  	// road if it the benefits do not seem to outweigh the disadvantages.
    31  }
    32  
    33  // NewPrinter returns a Printer that formats messages tailored to language t.
    34  func NewPrinter(t language.Tag) *Printer {
    35  	return DefaultCatalog.Printer(t)
    36  }
    37  
    38  // Sprint is like fmt.Sprint, but using language-specific formatting.
    39  func (p *Printer) Sprint(a ...interface{}) string {
    40  	return fmt.Sprint(p.bindArgs(a)...)
    41  }
    42  
    43  // Fprint is like fmt.Fprint, but using language-specific formatting.
    44  func (p *Printer) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
    45  	return fmt.Fprint(w, p.bindArgs(a)...)
    46  }
    47  
    48  // Print is like fmt.Print, but using language-specific formatting.
    49  func (p *Printer) Print(a ...interface{}) (n int, err error) {
    50  	return fmt.Print(p.bindArgs(a)...)
    51  }
    52  
    53  // Sprintln is like fmt.Sprintln, but using language-specific formatting.
    54  func (p *Printer) Sprintln(a ...interface{}) string {
    55  	return fmt.Sprintln(p.bindArgs(a)...)
    56  }
    57  
    58  // Fprintln is like fmt.Fprintln, but using language-specific formatting.
    59  func (p *Printer) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
    60  	return fmt.Fprintln(w, p.bindArgs(a)...)
    61  }
    62  
    63  // Println is like fmt.Println, but using language-specific formatting.
    64  func (p *Printer) Println(a ...interface{}) (n int, err error) {
    65  	return fmt.Println(p.bindArgs(a)...)
    66  }
    67  
    68  // Sprintf is like fmt.Sprintf, but using language-specific formatting.
    69  func (p *Printer) Sprintf(key Reference, a ...interface{}) string {
    70  	msg, hasSub := p.lookup(key)
    71  	if !hasSub {
    72  		return fmt.Sprintf(msg) // work around limitation of fmt
    73  	}
    74  	return fmt.Sprintf(msg, p.bindArgs(a)...)
    75  }
    76  
    77  // Fprintf is like fmt.Fprintf, but using language-specific formatting.
    78  func (p *Printer) Fprintf(w io.Writer, key Reference, a ...interface{}) (n int, err error) {
    79  	msg, hasSub := p.lookup(key)
    80  	if !hasSub {
    81  		return fmt.Fprintf(w, msg) // work around limitation of fmt
    82  	}
    83  	return fmt.Fprintf(w, msg, p.bindArgs(a)...)
    84  }
    85  
    86  // Printf is like fmt.Printf, but using language-specific formatting.
    87  func (p *Printer) Printf(key Reference, a ...interface{}) (n int, err error) {
    88  	msg, hasSub := p.lookup(key)
    89  	if !hasSub {
    90  		return fmt.Printf(msg) // work around limitation of fmt
    91  	}
    92  	return fmt.Printf(msg, p.bindArgs(a)...)
    93  }
    94  
    95  func (p *Printer) lookup(r Reference) (msg string, hasSub bool) {
    96  	var id string
    97  	switch v := r.(type) {
    98  	case string:
    99  		id, msg = v, v
   100  	case key:
   101  		id, msg = v.id, v.fallback
   102  	default:
   103  		panic("key argument is not a Reference")
   104  	}
   105  	if s, ok := p.cat.get(p.tag, id); ok {
   106  		msg = s
   107  	}
   108  	// fmt does not allow all arguments to be dropped in a format string. It
   109  	// only allows arguments to be dropped if at least one of the substitutions
   110  	// uses the positional marker (e.g. %[1]s). This hack works around this.
   111  	// TODO: This is only an approximation of the parsing of substitution
   112  	// patterns. Make more precise once we know if we can get by with fmt's
   113  	// formatting, which may not be the case.
   114  	for i := 0; i < len(msg)-1; i++ {
   115  		if msg[i] == '%' {
   116  			for i++; i < len(msg); i++ {
   117  				if strings.IndexByte("[]#+- *01234567890.", msg[i]) < 0 {
   118  					break
   119  				}
   120  			}
   121  			if i < len(msg) && msg[i] != '%' {
   122  				hasSub = true
   123  				break
   124  			}
   125  		}
   126  	}
   127  	return msg, hasSub
   128  }
   129  
   130  // A Reference is a string or a message reference.
   131  type Reference interface {
   132  }
   133  
   134  // Key creates a message Reference for a message where the given id is used for
   135  // message lookup and the fallback is returned when no matches are found.
   136  func Key(id string, fallback string) Reference {
   137  	return key{id, fallback}
   138  }
   139  
   140  type key struct {
   141  	id, fallback string
   142  }
   143  
   144  // bindArgs wraps arguments with implementation of fmt.Formatter, if needed.
   145  func (p *Printer) bindArgs(a []interface{}) []interface{} {
   146  	out := make([]interface{}, len(a))
   147  	for i, x := range a {
   148  		switch v := x.(type) {
   149  		case fmt.Formatter:
   150  			// Wrap the value with a Formatter that augments the State with
   151  			// language-specific attributes.
   152  			out[i] = &value{v, p}
   153  
   154  			// NOTE: as we use fmt.Formatter, we can't distinguish between
   155  			// regular and localized formatters, so we always need to wrap it.
   156  
   157  			// TODO: handle
   158  			// - numbers
   159  			// - lists
   160  			// - time?
   161  		default:
   162  			out[i] = x
   163  		}
   164  	}
   165  	return out
   166  }
   167  
   168  // state implements "golang.org/x/text/internal/format".State.
   169  type state struct {
   170  	fmt.State
   171  	p *Printer
   172  }
   173  
   174  func (s *state) Language() language.Tag { return s.p.tag }
   175  
   176  var _ format.State = &state{}
   177  
   178  type value struct {
   179  	x fmt.Formatter
   180  	p *Printer
   181  }
   182  
   183  func (v *value) Format(s fmt.State, verb rune) {
   184  	v.x.Format(&state{s, v.p}, verb)
   185  }