message: first checkin
Very rudimentary implementation of package message.
Enough for simple formatting jobs.
Change-Id: I4e83dc3bdbf4d9d6bc4eca221e892ba1a056026f
Reviewed-on: https://go-review.googlesource.com/15261
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/internal/format/format.go b/internal/format/format.go
new file mode 100644
index 0000000..3bcabc3
--- /dev/null
+++ b/internal/format/format.go
@@ -0,0 +1,31 @@
+// 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 format contains types for defining language-specific formatting of
+// values.
+//
+// This package is internal now, but will eventually be exposed after the API
+// settles.
+package format
+
+import (
+ "fmt"
+
+ "golang.org/x/text/language"
+)
+
+// State represents the printer state passed to custom formatters. It provides
+// access to the fmt.State interface and the sentence and language-related
+// context.
+type State interface {
+ fmt.State
+
+ // Language reports the requested language in which to render a message.
+ Language() language.Tag
+
+ // TODO: more info:
+ // - sentence context
+ // - user preferences, like measurement systems
+ // - options
+}
diff --git a/message/message.go b/message/message.go
new file mode 100644
index 0000000..25f47db
--- /dev/null
+++ b/message/message.go
@@ -0,0 +1,91 @@
+// 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.
+//
+// Under construction. See https://golang.org/design/text/12750-localization
+// and its corresponding proposal issue https://golang.org/issues/12750.
+package message
+
+import (
+ "fmt"
+ "io"
+
+ "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
+
+ // 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 &Printer{tag: 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)...)
+}
+
+// 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)
+}
diff --git a/message/message_test.go b/message/message_test.go
new file mode 100644
index 0000000..2c47701
--- /dev/null
+++ b/message/message_test.go
@@ -0,0 +1,49 @@
+// 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
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "testing"
+
+ "golang.org/x/text/internal/format"
+ "golang.org/x/text/language"
+)
+
+type formatFunc func(s fmt.State, v rune)
+
+func (f formatFunc) Format(s fmt.State, v rune) { f(s, v) }
+
+func TestBinding(t *testing.T) {
+ testCases := []struct {
+ tag string
+ value interface{}
+ want string
+ }{
+ {"en", 1, "1"},
+ {"en", "2", "2"},
+ { // Language is passed.
+ "en",
+ formatFunc(func(fs fmt.State, v rune) {
+ s := fs.(format.State)
+ io.WriteString(s, s.Language().String())
+ }),
+ "en",
+ },
+ }
+ for i, tc := range testCases {
+ p := NewPrinter(language.MustParse(tc.tag))
+ if got := p.Sprint(tc.value); got != tc.want {
+ t.Errorf("%d:%s:Sprint(%v) = %q; want %q", i, tc.tag, tc.value, got, tc.want)
+ }
+ var buf bytes.Buffer
+ p.Fprint(&buf, tc.value)
+ if got := buf.String(); got != tc.want {
+ t.Errorf("%d:%s:Fprint(%v) = %q; want %q", i, tc.tag, tc.value, got, tc.want)
+ }
+ }
+}