// Copyright 2017 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 pipeline import ( "encoding/json" "errors" "strings" "golang.org/x/text/language" ) // TODO: these definitions should be moved to a package so that the can be used // by other tools. // The file contains the structures used to define translations of a certain // messages. // // A translation may have multiple translations strings, or messages, depending // on the feature values of the various arguments. For instance, consider // a hypothetical translation from English to English, where the source defines // the format string "%d file(s) remaining". // See the examples directory for examples of extracted messages. // Messages is used to store translations for a single language. type Messages struct { Language language.Tag `json:"language"` Messages []Message `json:"messages"` Macros map[string]Text `json:"macros,omitempty"` } // A Message describes a message to be translated. type Message struct { // ID contains a list of identifiers for the message. ID IDList `json:"id"` // Key is the string that is used to look up the message at runtime. Key string `json:"key,omitempty"` Meaning string `json:"meaning,omitempty"` Message Text `json:"message"` Translation Text `json:"translation"` Comment string `json:"comment,omitempty"` TranslatorComment string `json:"translatorComment,omitempty"` Placeholders []Placeholder `json:"placeholders,omitempty"` // Fuzzy indicates that the provide translation needs review by a // translator, for instance because it was derived from automated // translation. Fuzzy bool `json:"fuzzy,omitempty"` // TODO: default placeholder syntax is {foo}. Allow alternative escaping // like `foo`. // Extraction information. Position string `json:"position,omitempty"` // filePosition:line } // Placeholder reports the placeholder for the given ID if it is defined or nil // otherwise. func (m *Message) Placeholder(id string) *Placeholder { for _, p := range m.Placeholders { if p.ID == id { return &p } } return nil } // Substitute replaces placeholders in msg with their original value. func (m *Message) Substitute(msg string) (sub string, err error) { last := 0 for i := 0; i < len(msg); { pLeft := strings.IndexByte(msg[i:], '{') if pLeft == -1 { break } pLeft += i pRight := strings.IndexByte(msg[pLeft:], '}') if pRight == -1 { return "", errorf("unmatched '}'") } pRight += pLeft id := strings.TrimSpace(msg[pLeft+1 : pRight]) i = pRight + 1 if id != "" && id[0] == '$' { continue } sub += msg[last:pLeft] last = i ph := m.Placeholder(id) if ph == nil { return "", errorf("unknown placeholder %q in message %q", id, msg) } sub += ph.String } sub += msg[last:] return sub, err } var errIncompatibleMessage = errors.New("messages incompatible") func checkEquivalence(a, b *Message) error { for _, v := range a.ID { for _, w := range b.ID { if v == w { return nil } } } // TODO: canonicalize placeholders and check for type equivalence. return errIncompatibleMessage } // A Placeholder is a part of the message that should not be changed by a // translator. It can be used to hide or prettify format strings (e.g. %d or // {{.Count}}), hide HTML, or mark common names that should not be translated. type Placeholder struct { // ID is the placeholder identifier without the curly braces. ID string `json:"id"` // String is the string with which to replace the placeholder. This may be a // formatting string (for instance "%d" or "{{.Count}}") or a literal string // (