Merge pull request #82 from didrocks/fix-multiline-export

Fix multiline export
This commit is contained in:
Leonel Quinteros 2023-08-29 12:38:36 -03:00 committed by GitHub
commit b2e50035bf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 182 additions and 30 deletions

View file

@ -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 ""

View file

@ -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 ""

View file

@ -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] ""

View file

@ -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"))

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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")

View file

@ -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

View file

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