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)
+		}
+	}
+}