taldir

Directory service to resolve wallet mailboxes by messenger addresses
Log | Files | Refs | Submodules | README | LICENSE

message.go (6453B)


      1 // Copyright 2017 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 plural
      6 
      7 import (
      8 	"fmt"
      9 	"io"
     10 	"reflect"
     11 	"strconv"
     12 
     13 	"golang.org/x/text/internal/catmsg"
     14 	"golang.org/x/text/internal/number"
     15 	"golang.org/x/text/language"
     16 	"golang.org/x/text/message/catalog"
     17 )
     18 
     19 // TODO: consider deleting this interface. Maybe VisibleDigits is always
     20 // sufficient and practical.
     21 
     22 // Interface is used for types that can determine their own plural form.
     23 type Interface interface {
     24 	// PluralForm reports the plural form for the given language of the
     25 	// underlying value. It also returns the integer value. If the integer value
     26 	// is larger than fits in n, PluralForm may return a value modulo
     27 	// 10,000,000.
     28 	PluralForm(t language.Tag, scale int) (f Form, n int)
     29 }
     30 
     31 // Selectf returns the first case for which its selector is a match for the
     32 // arg-th substitution argument to a formatting call, formatting it as indicated
     33 // by format.
     34 //
     35 // The cases argument are pairs of selectors and messages. Selectors are of type
     36 // string or Form. Messages are of type string or catalog.Message. A selector
     37 // matches an argument if:
     38 //   - it is "other" or Other
     39 //   - it matches the plural form of the argument: "zero", "one", "two", "few",
     40 //     or "many", or the equivalent Form
     41 //   - it is of the form "=x" where x is an integer that matches the value of
     42 //     the argument.
     43 //   - it is of the form "<x" where x is an integer that is larger than the
     44 //     argument.
     45 //
     46 // The format argument determines the formatting parameters for which to
     47 // determine the plural form. This is especially relevant for non-integer
     48 // values.
     49 //
     50 // The format string may be "", in which case a best-effort attempt is made to
     51 // find a reasonable representation on which to base the plural form. Examples
     52 // of format strings are:
     53 //   - %.2f   decimal with scale 2
     54 //   - %.2e   scientific notation with precision 3 (scale + 1)
     55 //   - %d     integer
     56 func Selectf(arg int, format string, cases ...interface{}) catalog.Message {
     57 	var p parser
     58 	// Intercept the formatting parameters of format by doing a dummy print.
     59 	fmt.Fprintf(io.Discard, format, &p)
     60 	m := &message{arg, kindDefault, 0, cases}
     61 	switch p.verb {
     62 	case 'g':
     63 		m.kind = kindPrecision
     64 		m.scale = p.scale
     65 	case 'f':
     66 		m.kind = kindScale
     67 		m.scale = p.scale
     68 	case 'e':
     69 		m.kind = kindScientific
     70 		m.scale = p.scale
     71 	case 'd':
     72 		m.kind = kindScale
     73 		m.scale = 0
     74 	default:
     75 		// TODO: do we need to handle errors?
     76 	}
     77 	return m
     78 }
     79 
     80 type parser struct {
     81 	verb  rune
     82 	scale int
     83 }
     84 
     85 func (p *parser) Format(s fmt.State, verb rune) {
     86 	p.verb = verb
     87 	p.scale = -1
     88 	if prec, ok := s.Precision(); ok {
     89 		p.scale = prec
     90 	}
     91 }
     92 
     93 type message struct {
     94 	arg   int
     95 	kind  int
     96 	scale int
     97 	cases []interface{}
     98 }
     99 
    100 const (
    101 	// Start with non-ASCII to allow skipping values.
    102 	kindDefault    = 0x80 + iota
    103 	kindScale      // verb f, number of fraction digits follows
    104 	kindScientific // verb e, number of fraction digits follows
    105 	kindPrecision  // verb g, number of significant digits follows
    106 )
    107 
    108 var handle = catmsg.Register("golang.org/x/text/feature/plural:plural", execute)
    109 
    110 func (m *message) Compile(e *catmsg.Encoder) error {
    111 	e.EncodeMessageType(handle)
    112 
    113 	e.EncodeUint(uint64(m.arg))
    114 
    115 	e.EncodeUint(uint64(m.kind))
    116 	if m.kind > kindDefault {
    117 		e.EncodeUint(uint64(m.scale))
    118 	}
    119 
    120 	forms := validForms(cardinal, e.Language())
    121 
    122 	for i := 0; i < len(m.cases); {
    123 		if err := compileSelector(e, forms, m.cases[i]); err != nil {
    124 			return err
    125 		}
    126 		if i++; i >= len(m.cases) {
    127 			return fmt.Errorf("plural: no message defined for selector %v", m.cases[i-1])
    128 		}
    129 		var msg catalog.Message
    130 		switch x := m.cases[i].(type) {
    131 		case string:
    132 			msg = catalog.String(x)
    133 		case catalog.Message:
    134 			msg = x
    135 		default:
    136 			return fmt.Errorf("plural: message of type %T; must be string or catalog.Message", x)
    137 		}
    138 		if err := e.EncodeMessage(msg); err != nil {
    139 			return err
    140 		}
    141 		i++
    142 	}
    143 	return nil
    144 }
    145 
    146 func compileSelector(e *catmsg.Encoder, valid []Form, selector interface{}) error {
    147 	form := Other
    148 	switch x := selector.(type) {
    149 	case string:
    150 		if x == "" {
    151 			return fmt.Errorf("plural: empty selector")
    152 		}
    153 		if c := x[0]; c == '=' || c == '<' {
    154 			val, err := strconv.ParseUint(x[1:], 10, 16)
    155 			if err != nil {
    156 				return fmt.Errorf("plural: invalid number in selector %q: %v", selector, err)
    157 			}
    158 			e.EncodeUint(uint64(c))
    159 			e.EncodeUint(val)
    160 			return nil
    161 		}
    162 		var ok bool
    163 		form, ok = countMap[x]
    164 		if !ok {
    165 			return fmt.Errorf("plural: invalid plural form %q", selector)
    166 		}
    167 	case Form:
    168 		form = x
    169 	default:
    170 		return fmt.Errorf("plural: selector of type %T; want string or Form", selector)
    171 	}
    172 
    173 	ok := false
    174 	for _, f := range valid {
    175 		if f == form {
    176 			ok = true
    177 			break
    178 		}
    179 	}
    180 	if !ok {
    181 		return fmt.Errorf("plural: form %q not supported for language %q", selector, e.Language())
    182 	}
    183 	e.EncodeUint(uint64(form))
    184 	return nil
    185 }
    186 
    187 func execute(d *catmsg.Decoder) bool {
    188 	lang := d.Language()
    189 	argN := int(d.DecodeUint())
    190 	kind := int(d.DecodeUint())
    191 	scale := -1 // default
    192 	if kind > kindDefault {
    193 		scale = int(d.DecodeUint())
    194 	}
    195 	form := Other
    196 	n := -1
    197 	if arg := d.Arg(argN); arg == nil {
    198 		// Default to Other.
    199 	} else if x, ok := arg.(number.VisibleDigits); ok {
    200 		d := x.Digits(nil, lang, scale)
    201 		form, n = cardinal.matchDisplayDigits(lang, &d)
    202 	} else if x, ok := arg.(Interface); ok {
    203 		// This covers lists and formatters from the number package.
    204 		form, n = x.PluralForm(lang, scale)
    205 	} else {
    206 		var f number.Formatter
    207 		switch kind {
    208 		case kindScale:
    209 			f.InitDecimal(lang)
    210 			f.SetScale(scale)
    211 		case kindScientific:
    212 			f.InitScientific(lang)
    213 			f.SetScale(scale)
    214 		case kindPrecision:
    215 			f.InitDecimal(lang)
    216 			f.SetPrecision(scale)
    217 		case kindDefault:
    218 			// sensible default
    219 			f.InitDecimal(lang)
    220 			if k := reflect.TypeOf(arg).Kind(); reflect.Int <= k && k <= reflect.Uintptr {
    221 				f.SetScale(0)
    222 			} else {
    223 				f.SetScale(2)
    224 			}
    225 		}
    226 		var dec number.Decimal // TODO: buffer in Printer
    227 		dec.Convert(f.RoundingContext, arg)
    228 		v := number.FormatDigits(&dec, f.RoundingContext)
    229 		if !v.NaN && !v.Inf {
    230 			form, n = cardinal.matchDisplayDigits(d.Language(), &v)
    231 		}
    232 	}
    233 	for !d.Done() {
    234 		f := d.DecodeUint()
    235 		if (f == '=' && n == int(d.DecodeUint())) ||
    236 			(f == '<' && 0 <= n && n < int(d.DecodeUint())) ||
    237 			form == Form(f) ||
    238 			Other == Form(f) {
    239 			return d.ExecuteMessage()
    240 		}
    241 		d.SkipMessage()
    242 	}
    243 	return false
    244 }