Merge pull request #82 from didrocks/fix-multiline-export
Fix multiline export
This commit is contained in:
commit
b2e50035bf
10 changed files with 182 additions and 30 deletions
|
@ -7,22 +7,80 @@ msgstr ""
|
|||
"Language: \n"
|
||||
"X-Generator: xgotext\n"
|
||||
|
||||
|
||||
#: fixtures/main.go:23
|
||||
#. gotext.Get
|
||||
#: fixtures/main.go:35
|
||||
#: fixtures/main.go:37
|
||||
msgid "My text on 'domain-name' domain"
|
||||
msgstr ""
|
||||
|
||||
#: fixtures/main.go:38
|
||||
#. l.GetN
|
||||
#: fixtures/main.go:75
|
||||
msgid "Singular"
|
||||
msgid_plural "Plural"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: fixtures/main.go:40
|
||||
#. l.GetN
|
||||
#: fixtures/main.go:77
|
||||
msgid "SingularVar"
|
||||
msgid_plural "PluralVar"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: fixtures/main.go:44
|
||||
msgid "alias call"
|
||||
msgstr ""
|
||||
|
||||
#: fixtures/main.go:104
|
||||
msgid "inside dummy"
|
||||
msgstr ""
|
||||
|
||||
#: fixtures/pkg/pkg.go:15
|
||||
msgid "inside sub package"
|
||||
msgstr ""
|
||||
|
||||
#: fixtures/main.go:51
|
||||
msgid ""
|
||||
"multi\n"
|
||||
"line\n"
|
||||
"string\n"
|
||||
msgstr ""
|
||||
|
||||
#: fixtures/main.go:54
|
||||
msgid ""
|
||||
"multi\n"
|
||||
"line\n"
|
||||
"string\n"
|
||||
"ending with\n"
|
||||
"EOL\n"
|
||||
msgstr ""
|
||||
|
||||
#: fixtures/main.go:59
|
||||
msgid ""
|
||||
"multline\n"
|
||||
"ending with EOL\n"
|
||||
msgstr ""
|
||||
|
||||
#: fixtures/main.go:50
|
||||
msgid "raw string with\nmultiple\nEOL"
|
||||
msgstr ""
|
||||
|
||||
#: fixtures/main.go:48
|
||||
msgid "string ending with EOL\n"
|
||||
msgstr ""
|
||||
|
||||
#: fixtures/main.go:49
|
||||
msgid ""
|
||||
"string with\n"
|
||||
"multiple\n"
|
||||
"EOL\n"
|
||||
msgstr ""
|
||||
|
||||
#: fixtures/main.go:47
|
||||
msgid "string with backquotes"
|
||||
msgstr ""
|
||||
|
||||
#: fixtures/main.go:91
|
||||
msgid "translate package"
|
||||
msgstr ""
|
||||
|
||||
#: fixtures/main.go:92
|
||||
msgid "translate sub package"
|
||||
msgstr ""
|
|
@ -7,8 +7,11 @@ msgstr ""
|
|||
"Language: \n"
|
||||
"X-Generator: xgotext\n"
|
||||
|
||||
|
||||
#: fixtures/main.go:26
|
||||
#. gotext.GetD
|
||||
#: fixtures/main.go:61
|
||||
msgid "Another text on a different domain"
|
||||
msgstr ""
|
||||
|
||||
#: fixtures/main.go:78
|
||||
msgctxt "ctx"
|
||||
msgid "string"
|
||||
msgstr ""
|
|
@ -7,16 +7,13 @@ msgstr ""
|
|||
"Language: \n"
|
||||
"X-Generator: xgotext\n"
|
||||
|
||||
|
||||
#: fixtures/main.go:35
|
||||
#. l.GetD
|
||||
#: fixtures/main.go:71
|
||||
msgid "Translate this"
|
||||
msgstr ""
|
||||
|
||||
#: fixtures/main.go:43
|
||||
#. l.GetNDC
|
||||
#: fixtures/main.go:79
|
||||
msgctxt "NDC-CTX"
|
||||
msgid "ndc"
|
||||
msgid_plural "ndcs"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[1] ""
|
|
@ -43,6 +43,21 @@ func main() {
|
|||
// same with alias package name
|
||||
fmt.Println(alias.Get("alias call"))
|
||||
|
||||
// Special strings
|
||||
fmt.Println(gotext.Get(`string with backquotes`))
|
||||
fmt.Println(gotext.Get("string ending with EOL\n"))
|
||||
fmt.Println(gotext.Get("string with\nmultiple\nEOL"))
|
||||
fmt.Println(gotext.Get(`raw string with\nmultiple\nEOL`))
|
||||
fmt.Println(gotext.Get(`multi
|
||||
line
|
||||
string`))
|
||||
fmt.Println(gotext.Get(`multi
|
||||
line
|
||||
string
|
||||
ending with
|
||||
EOL`))
|
||||
fmt.Println(gotext.Get("multline\nending with EOL\n"))
|
||||
|
||||
// Translate text from a different domain without reconfigure
|
||||
fmt.Println(gotext.GetD("domain2", "Another text on a different domain"))
|
||||
|
||||
|
|
|
@ -248,8 +248,9 @@ func (g *GoFile) parseGetter(def GetterDef, args []*ast.BasicLit, pos string) {
|
|||
return
|
||||
}
|
||||
|
||||
msgID, _ := strconv.Unquote(args[def.Id].Value)
|
||||
trans := parser.Translation{
|
||||
MsgId: args[def.Id].Value,
|
||||
MsgId: msgID,
|
||||
SourceLocations: []string{pos},
|
||||
}
|
||||
if def.Plural > 0 {
|
||||
|
@ -258,7 +259,8 @@ func (g *GoFile) parseGetter(def GetterDef, args []*ast.BasicLit, pos string) {
|
|||
log.Printf("ERR: Unsupported call at %s (Plural not a string)", pos)
|
||||
return
|
||||
}
|
||||
trans.MsgIdPlural = args[def.Plural].Value
|
||||
msgIDPlural, _ := strconv.Unquote(args[def.Plural].Value)
|
||||
trans.MsgIdPlural = msgIDPlural
|
||||
}
|
||||
if def.Context > 0 {
|
||||
// Context must be a string
|
||||
|
|
|
@ -37,13 +37,13 @@ func (t *Translation) Dump() string {
|
|||
data = append(data, "msgctxt "+t.Context)
|
||||
}
|
||||
|
||||
data = append(data, "msgid "+t.MsgId)
|
||||
data = append(data, toMsgIDString("msgid", t.MsgId)...)
|
||||
|
||||
if t.MsgIdPlural == "" {
|
||||
data = append(data, "msgstr \"\"")
|
||||
} else {
|
||||
data = append(data, toMsgIDString("msgid_plural", t.MsgIdPlural)...)
|
||||
data = append(data,
|
||||
"msgid_plural "+t.MsgIdPlural,
|
||||
"msgstr[0] \"\"",
|
||||
"msgstr[1] \"\"")
|
||||
}
|
||||
|
@ -51,6 +51,38 @@ func (t *Translation) Dump() string {
|
|||
return strings.Join(data, "\n")
|
||||
}
|
||||
|
||||
// toMsgIDString returns the spec implementation of multi line support of po files by aligning msgid on it.
|
||||
func toMsgIDString(prefix, msgID string) []string {
|
||||
elems := strings.Split(msgID, "\n")
|
||||
// Main case: single line.
|
||||
if len(elems) == 1 {
|
||||
return []string{fmt.Sprintf(`%s "%s"`, prefix, msgID)}
|
||||
}
|
||||
|
||||
// Only one line, but finishing with \n
|
||||
if strings.Count(msgID, "\n") == 1 && strings.HasSuffix(msgID, "\n") {
|
||||
return []string{fmt.Sprintf(`%s "%s\n"`, prefix, strings.TrimSuffix(msgID, "\n"))}
|
||||
}
|
||||
|
||||
// Skip last element for multiline which is an empty
|
||||
var shouldEndWithEOL bool
|
||||
if elems[len(elems)-1] == "" {
|
||||
elems = elems[:len(elems)-1]
|
||||
shouldEndWithEOL = true
|
||||
}
|
||||
data := []string{fmt.Sprintf(`%s ""`, prefix)}
|
||||
for i, v := range elems {
|
||||
l := fmt.Sprintf(`"%s\n"`, v)
|
||||
// Last element without EOL
|
||||
if i == len(elems)-1 && !shouldEndWithEOL {
|
||||
l = fmt.Sprintf(`"%s"`, v)
|
||||
}
|
||||
data = append(data, l)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
// TranslationMap contains a map of translations with the ID as key
|
||||
type TranslationMap map[string]*Translation
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ import (
|
|||
|
||||
const gotextPkgPath = "github.com/leonelquinteros/gotext"
|
||||
|
||||
|
||||
type GetterDef struct {
|
||||
Id int
|
||||
Plural int
|
||||
|
@ -287,8 +286,9 @@ func (g *GoFile) parseGetter(def GetterDef, args []*ast.BasicLit, pos string) {
|
|||
return
|
||||
}
|
||||
|
||||
msgID, _ := strconv.Unquote(args[def.Id].Value)
|
||||
trans := parser.Translation{
|
||||
MsgId: args[def.Id].Value,
|
||||
MsgId: msgID,
|
||||
SourceLocations: []string{pos},
|
||||
}
|
||||
if def.Plural > 0 {
|
||||
|
@ -297,7 +297,8 @@ func (g *GoFile) parseGetter(def GetterDef, args []*ast.BasicLit, pos string) {
|
|||
log.Printf("ERR: Unsupported call at %s (Plural not a string)", pos)
|
||||
return
|
||||
}
|
||||
trans.MsgIdPlural = args[def.Plural].Value
|
||||
msgIDPlural, _ := strconv.Unquote(args[def.Plural].Value)
|
||||
trans.MsgIdPlural = msgIDPlural
|
||||
}
|
||||
if def.Context > 0 {
|
||||
// Context must be a string
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package pkg_tree
|
||||
|
||||
import (
|
||||
"github.com/leonelquinteros/gotext/cli/xgotext/parser"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/leonelquinteros/gotext/cli/xgotext/parser"
|
||||
)
|
||||
|
||||
func TestParsePkgTree(t *testing.T) {
|
||||
|
@ -23,7 +24,18 @@ func TestParsePkgTree(t *testing.T) {
|
|||
t.Error(err)
|
||||
}
|
||||
|
||||
translations := []string{"\"inside sub package\"", "\"My text on 'domain-name' domain\"", "\"alias call\"", "\"Singular\"", "\"SingularVar\"", "\"translate package\"", "\"translate sub package\"", "\"inside dummy\""}
|
||||
translations := []string{"inside sub package", "My text on 'domain-name' domain", "alias call", "Singular", "SingularVar", "translate package", "translate sub package", "inside dummy",
|
||||
`string with backquotes`, "string ending with EOL\n", "string with\nmultiple\nEOL", `raw string with\nmultiple\nEOL`,
|
||||
`multi
|
||||
line
|
||||
string`,
|
||||
`multi
|
||||
line
|
||||
string
|
||||
ending with
|
||||
EOL`,
|
||||
"multline\nending with EOL\n",
|
||||
}
|
||||
|
||||
if len(translations) != len(data.Domains[defaultDomain].Translations) {
|
||||
t.Error("translations count mismatch")
|
||||
|
|
37
domain.go
37
domain.go
|
@ -3,6 +3,7 @@ package gotext
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
@ -653,9 +654,39 @@ func (do *Domain) MarshalText() ([]byte, error) {
|
|||
}
|
||||
|
||||
func EscapeSpecialCharacters(s string) string {
|
||||
s = regexp.MustCompile(`([^\\])(")`).ReplaceAllString(s, "$1\\\"") // Escape non-escaped double quotation marks
|
||||
s = strings.ReplaceAll(s, "\n", "\"\n\"") // Convert newlines into multi-line strings
|
||||
return s
|
||||
s = regexp.MustCompile(`([^\\])(")`).ReplaceAllString(s, "$1\\\"") // Escape non-escaped double quotation marks
|
||||
|
||||
if strings.Count(s, "\n") == 0 {
|
||||
return s
|
||||
}
|
||||
|
||||
// Handle EOL and multi-lines
|
||||
// Only one line, but finishing with \n
|
||||
if strings.Count(s, "\n") == 1 && strings.HasSuffix(s, "\n") {
|
||||
return strings.ReplaceAll(s, "\n", "\\n")
|
||||
}
|
||||
|
||||
elems := strings.Split(s, "\n")
|
||||
// Skip last element for multiline which is an empty
|
||||
var shouldEndWithEOL bool
|
||||
if elems[len(elems)-1] == "" {
|
||||
elems = elems[:len(elems)-1]
|
||||
shouldEndWithEOL = true
|
||||
}
|
||||
data := []string{(`"`)}
|
||||
for i, v := range elems {
|
||||
l := fmt.Sprintf(`"%s\n"`, v)
|
||||
// Last element without EOL
|
||||
if i == len(elems)-1 && !shouldEndWithEOL {
|
||||
l = fmt.Sprintf(`"%s"`, v)
|
||||
}
|
||||
// Remove finale " to last element as the whole string will be quoted
|
||||
if i == len(elems)-1 {
|
||||
l = strings.TrimSuffix(l, `"`)
|
||||
}
|
||||
data = append(data, l)
|
||||
}
|
||||
return strings.Join(data, "\n")
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler interface
|
||||
|
|
|
@ -134,9 +134,10 @@ func TestDomain_CheckExportFormatting(t *testing.T) {
|
|||
msgstr ""
|
||||
|
||||
msgid "myid"
|
||||
msgstr "test string"
|
||||
msgstr ""
|
||||
"test string\n"
|
||||
"with \"newline\""`
|
||||
|
||||
|
||||
if string(poBytes) != expectedOutput {
|
||||
t.Errorf("Exported PO format does not match. Received:\n\n%v\n\n\nExpected:\n\n%v", string(poBytes), expectedOutput)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue