summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/nicksnyder/go-i18n/v2/i18n/parse.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/nicksnyder/go-i18n/v2/i18n/parse.go')
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/v2/i18n/parse.go166
1 files changed, 166 insertions, 0 deletions
diff --git a/vendor/github.com/nicksnyder/go-i18n/v2/i18n/parse.go b/vendor/github.com/nicksnyder/go-i18n/v2/i18n/parse.go
new file mode 100644
index 000000000..57dd7fe7f
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/v2/i18n/parse.go
@@ -0,0 +1,166 @@
+package i18n
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "os"
+
+ "golang.org/x/text/language"
+)
+
+// MessageFile represents a parsed message file.
+type MessageFile struct {
+ Path string
+ Tag language.Tag
+ Format string
+ Messages []*Message
+}
+
+// ParseMessageFileBytes returns the messages parsed from file.
+func ParseMessageFileBytes(buf []byte, path string, unmarshalFuncs map[string]UnmarshalFunc) (*MessageFile, error) {
+ lang, format := parsePath(path)
+ tag := language.Make(lang)
+ messageFile := &MessageFile{
+ Path: path,
+ Tag: tag,
+ Format: format,
+ }
+ if len(buf) == 0 {
+ return messageFile, nil
+ }
+ unmarshalFunc := unmarshalFuncs[messageFile.Format]
+ if unmarshalFunc == nil {
+ if messageFile.Format == "json" {
+ unmarshalFunc = json.Unmarshal
+ } else {
+ return nil, fmt.Errorf("no unmarshaler registered for %s", messageFile.Format)
+ }
+ }
+ var err error
+ var raw interface{}
+ if err = unmarshalFunc(buf, &raw); err != nil {
+ return nil, err
+ }
+
+ if messageFile.Messages, err = recGetMessages(raw, isMessage(raw), true); err != nil {
+ return nil, err
+ }
+
+ return messageFile, nil
+}
+
+const nestedSeparator = "."
+
+var errInvalidTranslationFile = errors.New("invalid translation file, expected key-values, got a single value")
+
+// recGetMessages looks for translation messages inside "raw" parameter,
+// scanning nested maps using recursion.
+func recGetMessages(raw interface{}, isMapMessage, isInitialCall bool) ([]*Message, error) {
+ var messages []*Message
+ var err error
+
+ switch data := raw.(type) {
+ case string:
+ if isInitialCall {
+ return nil, errInvalidTranslationFile
+ }
+ m, err := NewMessage(data)
+ return []*Message{m}, err
+
+ case map[string]interface{}:
+ if isMapMessage {
+ m, err := NewMessage(data)
+ return []*Message{m}, err
+ }
+ messages = make([]*Message, 0, len(data))
+ for id, data := range data {
+ // recursively scan map items
+ messages, err = addChildMessages(id, data, messages)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ case map[interface{}]interface{}:
+ if isMapMessage {
+ m, err := NewMessage(data)
+ return []*Message{m}, err
+ }
+ messages = make([]*Message, 0, len(data))
+ for id, data := range data {
+ strid, ok := id.(string)
+ if !ok {
+ return nil, fmt.Errorf("expected key to be string but got %#v", id)
+ }
+ // recursively scan map items
+ messages, err = addChildMessages(strid, data, messages)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ case []interface{}:
+ // Backward compatibility for v1 file format.
+ messages = make([]*Message, 0, len(data))
+ for _, data := range data {
+ // recursively scan slice items
+ childMessages, err := recGetMessages(data, isMessage(data), false)
+ if err != nil {
+ return nil, err
+ }
+ messages = append(messages, childMessages...)
+ }
+
+ default:
+ return nil, fmt.Errorf("unsupported file format %T", raw)
+ }
+
+ return messages, nil
+}
+
+func addChildMessages(id string, data interface{}, messages []*Message) ([]*Message, error) {
+ isChildMessage := isMessage(data)
+ childMessages, err := recGetMessages(data, isChildMessage, false)
+ if err != nil {
+ return nil, err
+ }
+ for _, m := range childMessages {
+ if isChildMessage {
+ if m.ID == "" {
+ m.ID = id // start with innermost key
+ }
+ } else {
+ m.ID = id + nestedSeparator + m.ID // update ID with each nested key on the way
+ }
+ messages = append(messages, m)
+ }
+ return messages, nil
+}
+
+func parsePath(path string) (langTag, format string) {
+ formatStartIdx := -1
+ for i := len(path) - 1; i >= 0; i-- {
+ c := path[i]
+ if os.IsPathSeparator(c) {
+ if formatStartIdx != -1 {
+ langTag = path[i+1 : formatStartIdx]
+ }
+ return
+ }
+ if path[i] == '.' {
+ if formatStartIdx != -1 {
+ langTag = path[i+1 : formatStartIdx]
+ return
+ }
+ if formatStartIdx == -1 {
+ format = path[i+1:]
+ formatStartIdx = i
+ }
+ }
+ }
+ if formatStartIdx != -1 {
+ langTag = path[:formatStartIdx]
+ }
+ return
+}