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