Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/gohugoio/go-i18n.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Snyder <nickdsnyder@gmail.com>2019-05-07 22:45:26 +0300
committerGitHub <noreply@github.com>2019-05-07 22:45:26 +0300
commit1c44ce2d4dc76f3033da222b1497415c4f7dc19b (patch)
treea65a8282f0a0a11c26594e759ec4016974fff289
parent14847a9af8ca81e09347a0e596871bd237c8e1aa (diff)
export message (#170)
-rw-r--r--v2/goi18n/extract_command.go7
-rw-r--r--v2/goi18n/marshal.go6
-rw-r--r--v2/goi18n/merge_command.go34
-rw-r--r--v2/i18n/bundle.go16
-rw-r--r--v2/i18n/bundle_test.go9
-rw-r--r--v2/i18n/localizer.go7
-rw-r--r--v2/i18n/message.go219
-rw-r--r--v2/i18n/message_template.go (renamed from v2/internal/message_template.go)13
-rw-r--r--v2/i18n/message_template_test.go (renamed from v2/internal/message_template_test.go)2
-rw-r--r--v2/i18n/message_test.go (renamed from v2/internal/message_test.go)2
-rw-r--r--v2/i18n/parse.go (renamed from v2/internal/parse.go)5
-rw-r--r--v2/i18n/parse_test.go (renamed from v2/internal/parse_test.go)2
-rw-r--r--v2/internal/message.go221
-rw-r--r--v2/internal/template.go2
-rw-r--r--v2/internal/template_test.go6
15 files changed, 268 insertions, 283 deletions
diff --git a/v2/goi18n/extract_command.go b/v2/goi18n/extract_command.go
index 87debbb..b7bee37 100644
--- a/v2/goi18n/extract_command.go
+++ b/v2/goi18n/extract_command.go
@@ -12,7 +12,6 @@ import (
"strings"
"github.com/nicksnyder/go-i18n/v2/i18n"
- "github.com/nicksnyder/go-i18n/v2/internal"
)
func usageExtract() {
@@ -100,9 +99,9 @@ func (ec *extractCommand) execute() error {
return err
}
}
- messageTemplates := map[string]*internal.MessageTemplate{}
+ messageTemplates := map[string]*i18n.MessageTemplate{}
for _, m := range messages {
- if mt := internal.NewMessageTemplate(m); mt != nil {
+ if mt := i18n.NewMessageTemplate(m); mt != nil {
messageTemplates[m.ID] = mt
}
}
@@ -229,7 +228,7 @@ func (e *extractor) extractMessage(cl *ast.CompositeLit) {
if messageID := data["MessageID"]; messageID != "" {
data["ID"] = messageID
}
- e.messages = append(e.messages, internal.MustNewMessage(data))
+ e.messages = append(e.messages, i18n.MustNewMessage(data))
}
func extractStringLiteral(expr ast.Expr) (string, bool) {
diff --git a/v2/goi18n/marshal.go b/v2/goi18n/marshal.go
index 5fd908b..751b698 100644
--- a/v2/goi18n/marshal.go
+++ b/v2/goi18n/marshal.go
@@ -7,13 +7,13 @@ import (
"path/filepath"
"github.com/BurntSushi/toml"
- "github.com/nicksnyder/go-i18n/v2/internal"
+ "github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/nicksnyder/go-i18n/v2/internal/plural"
"golang.org/x/text/language"
yaml "gopkg.in/yaml.v2"
)
-func writeFile(outdir, label string, langTag language.Tag, format string, messageTemplates map[string]*internal.MessageTemplate, sourceLanguage bool) (path string, content []byte, err error) {
+func writeFile(outdir, label string, langTag language.Tag, format string, messageTemplates map[string]*i18n.MessageTemplate, sourceLanguage bool) (path string, content []byte, err error) {
v := marshalValue(messageTemplates, sourceLanguage)
content, err = marshal(v, format)
if err != nil {
@@ -23,7 +23,7 @@ func writeFile(outdir, label string, langTag language.Tag, format string, messag
return
}
-func marshalValue(messageTemplates map[string]*internal.MessageTemplate, sourceLanguage bool) interface{} {
+func marshalValue(messageTemplates map[string]*i18n.MessageTemplate, sourceLanguage bool) interface{} {
v := make(map[string]interface{}, len(messageTemplates))
for id, template := range messageTemplates {
if other := template.PluralTemplates[plural.Other]; sourceLanguage && len(template.PluralTemplates) == 1 &&
diff --git a/v2/goi18n/merge_command.go b/v2/goi18n/merge_command.go
index 7a02598..6e6041f 100644
--- a/v2/goi18n/merge_command.go
+++ b/v2/goi18n/merge_command.go
@@ -108,21 +108,21 @@ type fileSystemOp struct {
}
func merge(messageFiles map[string][]byte, sourceLanguageTag language.Tag, outdir, outputFormat string) (*fileSystemOp, error) {
- unmerged := make(map[language.Tag][]map[string]*internal.MessageTemplate)
- sourceMessageTemplates := make(map[string]*internal.MessageTemplate)
- unmarshalFuncs := map[string]internal.UnmarshalFunc{
+ unmerged := make(map[language.Tag][]map[string]*i18n.MessageTemplate)
+ sourceMessageTemplates := make(map[string]*i18n.MessageTemplate)
+ unmarshalFuncs := map[string]i18n.UnmarshalFunc{
"json": json.Unmarshal,
"toml": toml.Unmarshal,
"yaml": yaml.Unmarshal,
}
for path, content := range messageFiles {
- mf, err := internal.ParseMessageFileBytes(content, path, unmarshalFuncs)
+ mf, err := i18n.ParseMessageFileBytes(content, path, unmarshalFuncs)
if err != nil {
return nil, fmt.Errorf("failed to load message file %s: %s", path, err)
}
- templates := map[string]*internal.MessageTemplate{}
+ templates := map[string]*i18n.MessageTemplate{}
for _, m := range mf.Messages {
- templates[m.ID] = internal.NewMessageTemplate(m)
+ templates[m.ID] = i18n.NewMessageTemplate(m)
}
if mf.Tag == sourceLanguageTag {
for _, template := range templates {
@@ -141,7 +141,7 @@ func merge(messageFiles map[string][]byte, sourceLanguageTag language.Tag, outdi
}
pluralRules := plural.DefaultRules()
- all := make(map[language.Tag]map[string]*internal.MessageTemplate)
+ all := make(map[language.Tag]map[string]*i18n.MessageTemplate)
all[sourceLanguageTag] = sourceMessageTemplates
for _, srcTemplate := range sourceMessageTemplates {
for dstLangTag, messageTemplates := range unmerged {
@@ -155,11 +155,11 @@ func merge(messageFiles map[string][]byte, sourceLanguageTag language.Tag, outdi
continue
}
if all[dstLangTag] == nil {
- all[dstLangTag] = make(map[string]*internal.MessageTemplate)
+ all[dstLangTag] = make(map[string]*i18n.MessageTemplate)
}
dstMessageTemplate := all[dstLangTag][srcTemplate.ID]
if dstMessageTemplate == nil {
- dstMessageTemplate = &internal.MessageTemplate{
+ dstMessageTemplate = &i18n.MessageTemplate{
Message: &i18n.Message{
ID: srcTemplate.ID,
Description: srcTemplate.Description,
@@ -193,10 +193,10 @@ func merge(messageFiles map[string][]byte, sourceLanguageTag language.Tag, outdi
}
}
- translate := make(map[language.Tag]map[string]*internal.MessageTemplate)
- active := make(map[language.Tag]map[string]*internal.MessageTemplate)
+ translate := make(map[language.Tag]map[string]*i18n.MessageTemplate)
+ active := make(map[language.Tag]map[string]*i18n.MessageTemplate)
for langTag, messageTemplates := range all {
- active[langTag] = make(map[string]*internal.MessageTemplate)
+ active[langTag] = make(map[string]*i18n.MessageTemplate)
if langTag == sourceLanguageTag {
active[langTag] = messageTemplates
continue
@@ -212,7 +212,7 @@ func merge(messageFiles map[string][]byte, sourceLanguageTag language.Tag, outdi
activeMessageTemplate, translateMessageTemplate := activeDst(srcMessageTemplate, messageTemplate, pluralRule)
if translateMessageTemplate != nil {
if translate[langTag] == nil {
- translate[langTag] = make(map[string]*internal.MessageTemplate)
+ translate[langTag] = make(map[string]*i18n.MessageTemplate)
}
translate[langTag][messageTemplate.ID] = translateMessageTemplate
}
@@ -246,7 +246,7 @@ func merge(messageFiles map[string][]byte, sourceLanguageTag language.Tag, outdi
}
// activeDst returns the active part of the dst and whether dst is a complete translation of src.
-func activeDst(src, dst *internal.MessageTemplate, pluralRule *plural.Rule) (active *internal.MessageTemplate, translateMessageTemplate *internal.MessageTemplate) {
+func activeDst(src, dst *i18n.MessageTemplate, pluralRule *plural.Rule) (active *i18n.MessageTemplate, translateMessageTemplate *i18n.MessageTemplate) {
pluralForms := pluralRule.PluralForms
if len(src.PluralTemplates) == 1 {
pluralForms = map[plural.Form]struct{}{
@@ -257,7 +257,7 @@ func activeDst(src, dst *internal.MessageTemplate, pluralRule *plural.Rule) (act
dt := dst.PluralTemplates[pluralForm]
if dt == nil || dt.Src == "" {
if translateMessageTemplate == nil {
- translateMessageTemplate = &internal.MessageTemplate{
+ translateMessageTemplate = &i18n.MessageTemplate{
Message: &i18n.Message{
ID: src.ID,
Description: src.Description,
@@ -270,7 +270,7 @@ func activeDst(src, dst *internal.MessageTemplate, pluralRule *plural.Rule) (act
continue
}
if active == nil {
- active = &internal.MessageTemplate{
+ active = &i18n.MessageTemplate{
Message: &i18n.Message{
ID: src.ID,
Description: src.Description,
@@ -284,7 +284,7 @@ func activeDst(src, dst *internal.MessageTemplate, pluralRule *plural.Rule) (act
return
}
-func hash(t *internal.MessageTemplate) string {
+func hash(t *i18n.MessageTemplate) string {
h := sha1.New()
io.WriteString(h, t.Description)
io.WriteString(h, t.PluralTemplates[plural.Other].Src)
diff --git a/v2/i18n/bundle.go b/v2/i18n/bundle.go
index a25eb87..9977cde 100644
--- a/v2/i18n/bundle.go
+++ b/v2/i18n/bundle.go
@@ -4,14 +4,13 @@ import (
"fmt"
"io/ioutil"
- "github.com/nicksnyder/go-i18n/v2/internal"
"github.com/nicksnyder/go-i18n/v2/internal/plural"
"golang.org/x/text/language"
)
// UnmarshalFunc unmarshals data into v.
-type UnmarshalFunc = internal.UnmarshalFunc
+type UnmarshalFunc func(data []byte, v interface{}) error
// Bundle stores a set of messages and pluralization rules.
// Most applications only need a single bundle
@@ -21,7 +20,7 @@ type UnmarshalFunc = internal.UnmarshalFunc
type Bundle struct {
defaultLanguage language.Tag
unmarshalFuncs map[string]UnmarshalFunc
- messageTemplates map[language.Tag]map[string]*internal.MessageTemplate
+ messageTemplates map[language.Tag]map[string]*MessageTemplate
pluralRules plural.Rules
tags []language.Tag
matcher language.Matcher
@@ -62,16 +61,13 @@ func (b *Bundle) MustLoadMessageFile(path string) {
}
}
-// MessageFile represents a parsed message file.
-type MessageFile = internal.MessageFile
-
// ParseMessageFileBytes parses the bytes in buf to add translations to the bundle.
//
// The format of the file is everything after the last ".".
//
// The language tag of the file is everything after the second to last "." or after the last path separator, but before the format.
func (b *Bundle) ParseMessageFileBytes(buf []byte, path string) (*MessageFile, error) {
- messageFile, err := internal.ParseMessageFileBytes(buf, path, b.unmarshalFuncs)
+ messageFile, err := ParseMessageFileBytes(buf, path, b.unmarshalFuncs)
if err != nil {
return nil, err
}
@@ -97,14 +93,14 @@ func (b *Bundle) AddMessages(tag language.Tag, messages ...*Message) error {
return fmt.Errorf("no plural rule registered for %s", tag)
}
if b.messageTemplates == nil {
- b.messageTemplates = map[language.Tag]map[string]*internal.MessageTemplate{}
+ b.messageTemplates = map[language.Tag]map[string]*MessageTemplate{}
}
if b.messageTemplates[tag] == nil {
- b.messageTemplates[tag] = map[string]*internal.MessageTemplate{}
+ b.messageTemplates[tag] = map[string]*MessageTemplate{}
b.addTag(tag)
}
for _, m := range messages {
- b.messageTemplates[tag][m.ID] = internal.NewMessageTemplate(m)
+ b.messageTemplates[tag][m.ID] = NewMessageTemplate(m)
}
return nil
}
diff --git a/v2/i18n/bundle_test.go b/v2/i18n/bundle_test.go
index e14c846..327cc08 100644
--- a/v2/i18n/bundle_test.go
+++ b/v2/i18n/bundle_test.go
@@ -5,23 +5,22 @@ import (
"testing"
"github.com/BurntSushi/toml"
- "github.com/nicksnyder/go-i18n/v2/internal"
"golang.org/x/text/language"
yaml "gopkg.in/yaml.v2"
)
-var simpleMessage = internal.MustNewMessage(map[string]string{
+var simpleMessage = MustNewMessage(map[string]string{
"id": "simple",
"other": "simple translation",
})
-var detailMessage = internal.MustNewMessage(map[string]string{
+var detailMessage = MustNewMessage(map[string]string{
"id": "detail",
"description": "detail description",
"other": "detail translation",
})
-var everythingMessage = internal.MustNewMessage(map[string]string{
+var everythingMessage = MustNewMessage(map[string]string{
"id": "everything",
"description": "everything description",
"zero": "zero translation",
@@ -216,7 +215,7 @@ func TestV1FlatFormat(t *testing.T) {
}
func expectMessage(t *testing.T, bundle *Bundle, tag language.Tag, messageID string, message *Message) {
- expected := internal.NewMessageTemplate(message)
+ expected := NewMessageTemplate(message)
actual := bundle.messageTemplates[tag][messageID]
if !reflect.DeepEqual(actual, expected) {
t.Errorf("bundle.MessageTemplates[%q][%q] = %#v; want %#v", tag, messageID, actual, expected)
diff --git a/v2/i18n/localizer.go b/v2/i18n/localizer.go
index e02f66b..e149f45 100644
--- a/v2/i18n/localizer.go
+++ b/v2/i18n/localizer.go
@@ -5,7 +5,6 @@ import (
"text/template"
- "github.com/nicksnyder/go-i18n/v2/internal"
"github.com/nicksnyder/go-i18n/v2/internal/plural"
"golang.org/x/text/language"
)
@@ -145,7 +144,7 @@ func (l *Localizer) LocalizeWithTag(lc *LocalizeConfig) (string, language.Tag, e
return msg, tag, nil
}
-func (l *Localizer) getTemplate(id string, defaultMessage *Message) (language.Tag, *internal.MessageTemplate) {
+func (l *Localizer) getTemplate(id string, defaultMessage *Message) (language.Tag, *MessageTemplate) {
// Fast path.
// Optimistically assume this message id is defined in each language.
fastTag, template := l.matchTemplate(id, defaultMessage, l.bundle.matcher, l.bundle.tags)
@@ -175,7 +174,7 @@ func (l *Localizer) getTemplate(id string, defaultMessage *Message) (language.Ta
return l.matchTemplate(id, defaultMessage, language.NewMatcher(foundTags), foundTags)
}
-func (l *Localizer) matchTemplate(id string, defaultMessage *Message, matcher language.Matcher, tags []language.Tag) (language.Tag, *internal.MessageTemplate) {
+func (l *Localizer) matchTemplate(id string, defaultMessage *Message, matcher language.Matcher, tags []language.Tag) (language.Tag, *MessageTemplate) {
_, i, _ := matcher.Match(l.tags...)
tag := tags[i]
templates := l.bundle.messageTemplates[tag]
@@ -183,7 +182,7 @@ func (l *Localizer) matchTemplate(id string, defaultMessage *Message, matcher la
return tag, templates[id]
}
if tag == l.bundle.defaultLanguage && defaultMessage != nil {
- return tag, internal.NewMessageTemplate(defaultMessage)
+ return tag, NewMessageTemplate(defaultMessage)
}
return tag, nil
}
diff --git a/v2/i18n/message.go b/v2/i18n/message.go
index 7c881a6..f8f789a 100644
--- a/v2/i18n/message.go
+++ b/v2/i18n/message.go
@@ -1,6 +1,221 @@
package i18n
-import "github.com/nicksnyder/go-i18n/v2/internal"
+import (
+ "fmt"
+ "strings"
+)
// Message is a string that can be localized.
-type Message = internal.Message
+type Message struct {
+ // ID uniquely identifies the message.
+ ID string
+
+ // Hash uniquely identifies the content of the message
+ // that this message was translated from.
+ Hash string
+
+ // Description describes the message to give additional
+ // context to translators that may be relevant for translation.
+ Description string
+
+ // LeftDelim is the left Go template delimiter.
+ LeftDelim string
+
+ // RightDelim is the right Go template delimiter.``
+ RightDelim string
+
+ // Zero is the content of the message for the CLDR plural form "zero".
+ Zero string
+
+ // One is the content of the message for the CLDR plural form "one".
+ One string
+
+ // Two is the content of the message for the CLDR plural form "two".
+ Two string
+
+ // Few is the content of the message for the CLDR plural form "few".
+ Few string
+
+ // Many is the content of the message for the CLDR plural form "many".
+ Many string
+
+ // Other is the content of the message for the CLDR plural form "other".
+ Other string
+}
+
+// NewMessage parses data and returns a new message.
+func NewMessage(data interface{}) (*Message, error) {
+ m := &Message{}
+ if err := m.unmarshalInterface(data); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+// MustNewMessage is similar to NewMessage except it panics if an error happens.
+func MustNewMessage(data interface{}) *Message {
+ m, err := NewMessage(data)
+ if err != nil {
+ panic(err)
+ }
+ return m
+}
+
+// unmarshalInterface unmarshals a message from data.
+func (m *Message) unmarshalInterface(v interface{}) error {
+ strdata, err := stringMap(v)
+ if err != nil {
+ return err
+ }
+ for k, v := range strdata {
+ switch strings.ToLower(k) {
+ case "id":
+ m.ID = v
+ case "description":
+ m.Description = v
+ case "hash":
+ m.Hash = v
+ case "leftdelim":
+ m.LeftDelim = v
+ case "rightdelim":
+ m.RightDelim = v
+ case "zero":
+ m.Zero = v
+ case "one":
+ m.One = v
+ case "two":
+ m.Two = v
+ case "few":
+ m.Few = v
+ case "many":
+ m.Many = v
+ case "other":
+ m.Other = v
+ }
+ }
+ return nil
+}
+
+type keyTypeErr struct {
+ key interface{}
+}
+
+func (err *keyTypeErr) Error() string {
+ return fmt.Sprintf("expected key to be a string but got %#v", err.key)
+}
+
+type valueTypeErr struct {
+ value interface{}
+}
+
+func (err *valueTypeErr) Error() string {
+ return fmt.Sprintf("unsupported type %#v", err.value)
+}
+
+func stringMap(v interface{}) (map[string]string, error) {
+ switch value := v.(type) {
+ case string:
+ return map[string]string{
+ "other": value,
+ }, nil
+ case map[string]string:
+ return value, nil
+ case map[string]interface{}:
+ strdata := make(map[string]string, len(value))
+ for k, v := range value {
+ err := stringSubmap(k, v, strdata)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return strdata, nil
+ case map[interface{}]interface{}:
+ strdata := make(map[string]string, len(value))
+ for k, v := range value {
+ kstr, ok := k.(string)
+ if !ok {
+ return nil, &keyTypeErr{key: k}
+ }
+ err := stringSubmap(kstr, v, strdata)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return strdata, nil
+ default:
+ return nil, &valueTypeErr{value: value}
+ }
+}
+
+func stringSubmap(k string, v interface{}, strdata map[string]string) error {
+ if k == "translation" {
+ switch vt := v.(type) {
+ case string:
+ strdata["other"] = vt
+ default:
+ v1Message, err := stringMap(v)
+ if err != nil {
+ return err
+ }
+ for kk, vv := range v1Message {
+ strdata[kk] = vv
+ }
+ }
+ return nil
+ }
+
+ switch vt := v.(type) {
+ case string:
+ strdata[k] = vt
+ return nil
+ case nil:
+ return nil
+ default:
+ return fmt.Errorf("expected value for key %q be a string but got %#v", k, v)
+ }
+}
+
+// isMessage tells whether the given data is a message, or a map containing
+// nested messages.
+// A map is assumed to be a message if it contains any of the "reserved" keys:
+// "id", "description", "hash", "leftdelim", "rightdelim", "zero", "one", "two", "few", "many", "other"
+// with a string value.
+// e.g.,
+// - {"message": {"description": "world"}} is a message
+// - {"message": {"description": "world", "foo": "bar"}} is a message ("foo" key is ignored)
+// - {"notmessage": {"description": {"hello": "world"}}} is not
+// - {"notmessage": {"foo": "bar"}} is not
+func isMessage(v interface{}) bool {
+ reservedKeys := []string{"id", "description", "hash", "leftdelim", "rightdelim", "zero", "one", "two", "few", "many", "other"}
+ switch data := v.(type) {
+ case string:
+ return true
+ case map[string]interface{}:
+ for _, key := range reservedKeys {
+ val, ok := data[key]
+ if !ok {
+ continue
+ }
+ _, ok = val.(string)
+ if !ok {
+ continue
+ }
+ // v is a message if it contains a "reserved" key holding a string value
+ return true
+ }
+ case map[interface{}]interface{}:
+ for _, key := range reservedKeys {
+ val, ok := data[key]
+ if !ok {
+ continue
+ }
+ _, ok = val.(string)
+ if !ok {
+ continue
+ }
+ // v is a message if it contains a "reserved" key holding a string value
+ return true
+ }
+ }
+ return false
+}
diff --git a/v2/internal/message_template.go b/v2/i18n/message_template.go
index 134dae7..65a16cb 100644
--- a/v2/internal/message_template.go
+++ b/v2/i18n/message_template.go
@@ -1,4 +1,4 @@
-package internal
+package i18n
import (
"bytes"
@@ -6,18 +6,19 @@ import (
"text/template"
+ "github.com/nicksnyder/go-i18n/v2/internal"
"github.com/nicksnyder/go-i18n/v2/internal/plural"
)
// MessageTemplate is an executable template for a message.
type MessageTemplate struct {
*Message
- PluralTemplates map[plural.Form]*Template
+ PluralTemplates map[plural.Form]*internal.Template
}
// NewMessageTemplate returns a new message template.
func NewMessageTemplate(m *Message) *MessageTemplate {
- pluralTemplates := map[plural.Form]*Template{}
+ pluralTemplates := map[plural.Form]*internal.Template{}
setPluralTemplate(pluralTemplates, plural.Zero, m.Zero)
setPluralTemplate(pluralTemplates, plural.One, m.One)
setPluralTemplate(pluralTemplates, plural.Two, m.Two)
@@ -33,9 +34,9 @@ func NewMessageTemplate(m *Message) *MessageTemplate {
}
}
-func setPluralTemplate(pluralTemplates map[plural.Form]*Template, pluralForm plural.Form, src string) {
+func setPluralTemplate(pluralTemplates map[plural.Form]*internal.Template, pluralForm plural.Form, src string) {
if src != "" {
- pluralTemplates[pluralForm] = &Template{Src: src}
+ pluralTemplates[pluralForm] = &internal.Template{Src: src}
}
}
@@ -57,7 +58,7 @@ func (mt *MessageTemplate) Execute(pluralForm plural.Form, data interface{}, fun
messageID: mt.Message.ID,
}
}
- if err := t.parse(mt.LeftDelim, mt.RightDelim, funcs); err != nil {
+ if err := t.Parse(mt.LeftDelim, mt.RightDelim, funcs); err != nil {
return "", err
}
if t.Template == nil {
diff --git a/v2/internal/message_template_test.go b/v2/i18n/message_template_test.go
index a32708c..d920cd4 100644
--- a/v2/internal/message_template_test.go
+++ b/v2/i18n/message_template_test.go
@@ -1,4 +1,4 @@
-package internal
+package i18n
import (
"reflect"
diff --git a/v2/internal/message_test.go b/v2/i18n/message_test.go
index cab5735..b37063a 100644
--- a/v2/internal/message_test.go
+++ b/v2/i18n/message_test.go
@@ -1,4 +1,4 @@
-package internal
+package i18n
import (
"reflect"
diff --git a/v2/internal/parse.go b/v2/i18n/parse.go
index da37d43..57dd7fe 100644
--- a/v2/internal/parse.go
+++ b/v2/i18n/parse.go
@@ -1,4 +1,4 @@
-package internal
+package i18n
import (
"encoding/json"
@@ -9,9 +9,6 @@ import (
"golang.org/x/text/language"
)
-// UnmarshalFunc unmarshals data into v.
-type UnmarshalFunc func(data []byte, v interface{}) error
-
// MessageFile represents a parsed message file.
type MessageFile struct {
Path string
diff --git a/v2/internal/parse_test.go b/v2/i18n/parse_test.go
index 5581b27..3272e52 100644
--- a/v2/internal/parse_test.go
+++ b/v2/i18n/parse_test.go
@@ -1,4 +1,4 @@
-package internal
+package i18n
import (
"reflect"
diff --git a/v2/internal/message.go b/v2/internal/message.go
deleted file mode 100644
index 9274404..0000000
--- a/v2/internal/message.go
+++ /dev/null
@@ -1,221 +0,0 @@
-package internal
-
-import (
- "fmt"
- "strings"
-)
-
-// Message is a string that can be localized.
-type Message struct {
- // ID uniquely identifies the message.
- ID string
-
- // Hash uniquely identifies the content of the message
- // that this message was translated from.
- Hash string
-
- // Description describes the message to give additional
- // context to translators that may be relevant for translation.
- Description string
-
- // LeftDelim is the left Go template delimiter.
- LeftDelim string
-
- // RightDelim is the right Go template delimiter.``
- RightDelim string
-
- // Zero is the content of the message for the CLDR plural form "zero".
- Zero string
-
- // One is the content of the message for the CLDR plural form "one".
- One string
-
- // Two is the content of the message for the CLDR plural form "two".
- Two string
-
- // Few is the content of the message for the CLDR plural form "few".
- Few string
-
- // Many is the content of the message for the CLDR plural form "many".
- Many string
-
- // Other is the content of the message for the CLDR plural form "other".
- Other string
-}
-
-// NewMessage parses data and returns a new message.
-func NewMessage(data interface{}) (*Message, error) {
- m := &Message{}
- if err := m.unmarshalInterface(data); err != nil {
- return nil, err
- }
- return m, nil
-}
-
-// MustNewMessage is similar to NewMessage except it panics if an error happens.
-func MustNewMessage(data interface{}) *Message {
- m, err := NewMessage(data)
- if err != nil {
- panic(err)
- }
- return m
-}
-
-// unmarshalInterface unmarshals a message from data.
-func (m *Message) unmarshalInterface(v interface{}) error {
- strdata, err := stringMap(v)
- if err != nil {
- return err
- }
- for k, v := range strdata {
- switch strings.ToLower(k) {
- case "id":
- m.ID = v
- case "description":
- m.Description = v
- case "hash":
- m.Hash = v
- case "leftdelim":
- m.LeftDelim = v
- case "rightdelim":
- m.RightDelim = v
- case "zero":
- m.Zero = v
- case "one":
- m.One = v
- case "two":
- m.Two = v
- case "few":
- m.Few = v
- case "many":
- m.Many = v
- case "other":
- m.Other = v
- }
- }
- return nil
-}
-
-type keyTypeErr struct {
- key interface{}
-}
-
-func (err *keyTypeErr) Error() string {
- return fmt.Sprintf("expected key to be a string but got %#v", err.key)
-}
-
-type valueTypeErr struct {
- value interface{}
-}
-
-func (err *valueTypeErr) Error() string {
- return fmt.Sprintf("unsupported type %#v", err.value)
-}
-
-func stringMap(v interface{}) (map[string]string, error) {
- switch value := v.(type) {
- case string:
- return map[string]string{
- "other": value,
- }, nil
- case map[string]string:
- return value, nil
- case map[string]interface{}:
- strdata := make(map[string]string, len(value))
- for k, v := range value {
- err := stringSubmap(k, v, strdata)
- if err != nil {
- return nil, err
- }
- }
- return strdata, nil
- case map[interface{}]interface{}:
- strdata := make(map[string]string, len(value))
- for k, v := range value {
- kstr, ok := k.(string)
- if !ok {
- return nil, &keyTypeErr{key: k}
- }
- err := stringSubmap(kstr, v, strdata)
- if err != nil {
- return nil, err
- }
- }
- return strdata, nil
- default:
- return nil, &valueTypeErr{value: value}
- }
-}
-
-func stringSubmap(k string, v interface{}, strdata map[string]string) error {
- if k == "translation" {
- switch vt := v.(type) {
- case string:
- strdata["other"] = vt
- default:
- v1Message, err := stringMap(v)
- if err != nil {
- return err
- }
- for kk, vv := range v1Message {
- strdata[kk] = vv
- }
- }
- return nil
- }
-
- switch vt := v.(type) {
- case string:
- strdata[k] = vt
- return nil
- case nil:
- return nil
- default:
- return fmt.Errorf("expected value for key %q be a string but got %#v", k, v)
- }
-}
-
-// isMessage tells whether the given data is a message, or a map containing
-// nested messages.
-// A map is assumed to be a message if it contains any of the "reserved" keys:
-// "id", "description", "hash", "leftdelim", "rightdelim", "zero", "one", "two", "few", "many", "other"
-// with a string value.
-// e.g.,
-// - {"message": {"description": "world"}} is a message
-// - {"message": {"description": "world", "foo": "bar"}} is a message ("foo" key is ignored)
-// - {"notmessage": {"description": {"hello": "world"}}} is not
-// - {"notmessage": {"foo": "bar"}} is not
-func isMessage(v interface{}) bool {
- reservedKeys := []string{"id", "description", "hash", "leftdelim", "rightdelim", "zero", "one", "two", "few", "many", "other"}
- switch data := v.(type) {
- case string:
- return true
- case map[string]interface{}:
- for _, key := range reservedKeys {
- val, ok := data[key]
- if !ok {
- continue
- }
- _, ok = val.(string)
- if !ok {
- continue
- }
- // v is a message if it contains a "reserved" key holding a string value
- return true
- }
- case map[interface{}]interface{}:
- for _, key := range reservedKeys {
- val, ok := data[key]
- if !ok {
- continue
- }
- _, ok = val.(string)
- if !ok {
- continue
- }
- // v is a message if it contains a "reserved" key holding a string value
- return true
- }
- }
- return false
-}
diff --git a/v2/internal/template.go b/v2/internal/template.go
index 2ef1eea..4079f52 100644
--- a/v2/internal/template.go
+++ b/v2/internal/template.go
@@ -12,7 +12,7 @@ type Template struct {
ParseErr *error
}
-func (t *Template) parse(leftDelim, rightDelim string, funcs gotemplate.FuncMap) error {
+func (t *Template) Parse(leftDelim, rightDelim string, funcs gotemplate.FuncMap) error {
if t.ParseErr == nil {
if strings.Contains(t.Src, leftDelim) {
gt, err := gotemplate.New("").Funcs(funcs).Delims(leftDelim, rightDelim).Parse(t.Src)
diff --git a/v2/internal/template_test.go b/v2/internal/template_test.go
index 68e60d0..dcc7b27 100644
--- a/v2/internal/template_test.go
+++ b/v2/internal/template_test.go
@@ -9,7 +9,7 @@ import (
func TestParse(t *testing.T) {
tmpl := &Template{Src: "hello"}
- if err := tmpl.parse("", "", nil); err != nil {
+ if err := tmpl.Parse("", "", nil); err != nil {
t.Fatal(err)
}
if tmpl.ParseErr == nil {
@@ -23,7 +23,7 @@ func TestParse(t *testing.T) {
func TestParseError(t *testing.T) {
expectedErr := fmt.Errorf("expected error")
tmpl := &Template{ParseErr: &expectedErr}
- if err := tmpl.parse("", "", nil); err != expectedErr {
+ if err := tmpl.Parse("", "", nil); err != expectedErr {
t.Fatalf("expected %#v; got %#v", expectedErr, err)
}
}
@@ -35,7 +35,7 @@ func TestParseWithFunc(t *testing.T) {
return "bar"
},
}
- if err := tmpl.parse("", "", funcs); err != nil {
+ if err := tmpl.Parse("", "", funcs); err != nil {
t.Fatal(err)
}
if tmpl.ParseErr == nil {