Add support for multi-line msgid

This commit is contained in:
Mattias Lundell
2017-05-29 11:36:18 +02:00
parent 2c51ed2000
commit 4a52c7709d
4 changed files with 94 additions and 44 deletions

View File

@@ -32,8 +32,8 @@ func TestGettersSetters(t *testing.T) {
func TestPackageFunctions(t *testing.T) { func TestPackageFunctions(t *testing.T) {
// Set PO content // Set PO content
str := ` str := `
msgid "" # msgid ""
msgstr "" # msgstr ""
# Initial comment # Initial comment
# Headers below # Headers below
"Language: en\n" "Language: en\n"
@@ -79,7 +79,7 @@ msgstr "Some random translation in a context"
msgid "More" msgid "More"
msgstr "More translation" msgstr "More translation"
` `
// Create Locales directory on default location // Create Locales directory on default location
dirname := path.Clean("/tmp" + string(os.PathSeparator) + "en_US") dirname := path.Clean("/tmp" + string(os.PathSeparator) + "en_US")
@@ -157,7 +157,7 @@ msgstr[0] "This one is the singular: %s"
msgstr[1] "This one is the plural: %s" msgstr[1] "This one is the plural: %s"
msgstr[2] "And this is the second plural form: %s" msgstr[2] "And this is the second plural form: %s"
` `
// Create Locales directory on default location // Create Locales directory on default location
dirname := path.Clean(library + string(os.PathSeparator) + "en_US") dirname := path.Clean(library + string(os.PathSeparator) + "en_US")

View File

@@ -9,8 +9,8 @@ import (
func TestLocale(t *testing.T) { func TestLocale(t *testing.T) {
// Set PO content // Set PO content
str := ` str := `
msgid "" # msgid ""
msgstr "" # msgstr ""
# Initial comment # Initial comment
# Headers below # Headers below
"Language: en\n" "Language: en\n"
@@ -56,7 +56,7 @@ msgstr "Some random translation in a context"
msgid "More" msgid "More"
msgstr "More translation" msgstr "More translation"
` `
// Create Locales directory with simplified language code // Create Locales directory with simplified language code
dirname := path.Join("/tmp", "en", "LC_MESSAGES") dirname := path.Join("/tmp", "en", "LC_MESSAGES")
@@ -176,7 +176,7 @@ msgstr "Some random translation in a context"
msgid "More" msgid "More"
msgstr "More translation" msgstr "More translation"
` `
// Create Locales directory with simplified language code // Create Locales directory with simplified language code
dirname := path.Join("/tmp", "en", "LC_MESSAGES") dirname := path.Join("/tmp", "en", "LC_MESSAGES")
@@ -259,7 +259,7 @@ msgstr[0] "This one is the singular: %s"
msgstr[1] "This one is the plural: %s" msgstr[1] "This one is the plural: %s"
msgstr[2] "And this is the second plural form: %s" msgstr[2] "And this is the second plural form: %s"
` `
// Create Locales directory with simplified language code // Create Locales directory with simplified language code
dirname := path.Join("/tmp", "es") dirname := path.Join("/tmp", "es")

76
po.go
View File

@@ -52,18 +52,18 @@ And it's safe for concurrent use by multiple goroutines by using the sync packag
Example: Example:
import "github.com/leonelquinteros/gotext" import "github.com/leonelquinteros/gotext"
func main() { func main() {
// Create po object // Create po object
po := new(gotext.Po) po := new(gotext.Po)
// Parse .po file // Parse .po file
po.ParseFile("/path/to/po/file/translations.po") po.ParseFile("/path/to/po/file/translations.po")
// Get translation // Get translation
println(po.Get("Translate this")) println(po.Get("Translate this"))
} }
*/ */
type Po struct { type Po struct {
@@ -92,6 +92,16 @@ type Po struct {
ctxBuffer string ctxBuffer string
} }
type parseState int
const (
head parseState = iota
msgCtxt
msgID
msgIDPlural
msgStr
)
// ParseFile tries to read the file by its provided path (f) and parse its content as a .po file. // ParseFile tries to read the file by its provided path (f) and parse its content as a .po file.
func (po *Po) ParseFile(f string) { func (po *Po) ParseFile(f string) {
// Check if file exists // Check if file exists
@@ -133,6 +143,7 @@ func (po *Po) Parse(str string) {
po.trBuffer = newTranslation() po.trBuffer = newTranslation()
po.ctxBuffer = "" po.ctxBuffer = ""
state := head
for _, l := range lines { for _, l := range lines {
// Trim spaces // Trim spaces
l = strings.TrimSpace(l) l = strings.TrimSpace(l)
@@ -145,30 +156,34 @@ func (po *Po) Parse(str string) {
// Buffer context and continue // Buffer context and continue
if strings.HasPrefix(l, "msgctxt") { if strings.HasPrefix(l, "msgctxt") {
po.parseContext(l) po.parseContext(l)
state = msgCtxt
continue continue
} }
// Buffer msgid and continue // Buffer msgid and continue
if strings.HasPrefix(l, "msgid") && !strings.HasPrefix(l, "msgid_plural") { if strings.HasPrefix(l, "msgid") && !strings.HasPrefix(l, "msgid_plural") {
po.parseID(l) po.parseID(l)
state = msgID
continue continue
} }
// Check for plural form // Check for plural form
if strings.HasPrefix(l, "msgid_plural") { if strings.HasPrefix(l, "msgid_plural") {
po.parsePluralID(l) po.parsePluralID(l)
state = msgIDPlural
continue continue
} }
// Save translation // Save translation
if strings.HasPrefix(l, "msgstr") { if strings.HasPrefix(l, "msgstr") {
po.parseMessage(l) po.parseMessage(l)
state = msgStr
continue continue
} }
// Multi line strings and headers // Multi line strings and headers
if strings.HasPrefix(l, "\"") && strings.HasSuffix(l, "\"") { if strings.HasPrefix(l, "\"") && strings.HasSuffix(l, "\"") {
po.parseString(l) state = po.parseString(l, state)
continue continue
} }
} }
@@ -259,23 +274,36 @@ func (po *Po) parseMessage(l string) {
// parseString takes a well formatted string without prefix // parseString takes a well formatted string without prefix
// and creates headers or attach multi-line strings when corresponding // and creates headers or attach multi-line strings when corresponding
func (po *Po) parseString(l string) { func (po *Po) parseString(l string, state parseState) parseState {
// Check for multiline from previously set msgid switch state {
if po.trBuffer.id != "" { case msgStr:
// Append to last translation found // Check for multiline from previously set msgid
if po.trBuffer.id != "" {
// Append to last translation found
uq, _ := strconv.Unquote(l)
po.trBuffer.trs[len(po.trBuffer.trs)-1] += uq
}
case msgID:
// Multiline msgid - Append to current id
uq, _ := strconv.Unquote(l) uq, _ := strconv.Unquote(l)
po.trBuffer.trs[len(po.trBuffer.trs)-1] += uq po.trBuffer.id += uq
case msgIDPlural:
return // Multiline msgid - Append to current id
uq, _ := strconv.Unquote(l)
po.trBuffer.pluralID += uq
case msgCtxt:
// Multiline context - Append to current context
ctxt, _ := strconv.Unquote(l)
po.ctxBuffer += ctxt
default:
// Otherwise is a header
h, _ := strconv.Unquote(strings.TrimSpace(l))
po.RawHeaders += h
return head
} }
// Otherwise is a header return state
h, err := strconv.Unquote(strings.TrimSpace(l))
if err != nil {
return
}
po.RawHeaders += h
} }
// isValidLine checks for line prefixes to detect valid syntax. // isValidLine checks for line prefixes to detect valid syntax.

View File

@@ -9,8 +9,6 @@ import (
func TestPo(t *testing.T) { func TestPo(t *testing.T) {
// Set PO content // Set PO content
str := ` str := `
msgid ""
msgstr ""
# Initial comment # Initial comment
# Headers below # Headers below
"Language: en\n" "Language: en\n"
@@ -26,6 +24,19 @@ msgstr "Translated text"
msgid "Another string" msgid "Another string"
msgstr "" msgstr ""
# Multi-line msgid
msgid "multi"
"line"
"id"
msgstr "id with multiline content"
# Multi-line msgid_plural
msgid "multi"
"line"
"plural"
"id"
msgstr "plural id with multiline content"
#Multi-line string #Multi-line string
msgid "Multi-line" msgid "Multi-line"
msgstr "Multi " msgstr "Multi "
@@ -60,7 +71,7 @@ msgstr "Some random translation in a context"
msgid "More" msgid "More"
msgstr "More translation" msgstr "More translation"
` `
// Write PO content to file // Write PO content to file
filename := path.Clean(os.TempDir() + string(os.PathSeparator) + "default.po") filename := path.Clean(os.TempDir() + string(os.PathSeparator) + "default.po")
@@ -97,6 +108,18 @@ msgstr "More translation"
t.Errorf("Expected 'This one is the singular: Variable' but got '%s'", tr) t.Errorf("Expected 'This one is the singular: Variable' but got '%s'", tr)
} }
// Test multi-line id
tr = po.Get("multilineid")
if tr != "id with multiline content" {
t.Errorf("Expected 'id with multiline content' but got '%s'", tr)
}
// Test multi-line plural id
tr = po.Get("multilinepluralid")
if tr != "plural id with multiline content" {
t.Errorf("Expected 'plural id with multiline content' but got '%s'", tr)
}
// Test multi-line // Test multi-line
tr = po.Get("Multi-line") tr = po.Get("Multi-line")
if tr != "Multi line" { if tr != "Multi line" {
@@ -150,13 +173,12 @@ msgstr "More translation"
t.Errorf("Expected 'More translation' but got '%s'", tr) t.Errorf("Expected 'More translation' but got '%s'", tr)
} }
t.Log(po.contexts)
} }
func TestPoHeaders(t *testing.T) { func TestPoHeaders(t *testing.T) {
// Set PO content // Set PO content
str := ` str := `
msgid ""
msgstr ""
# Initial comment # Initial comment
# Headers below # Headers below
"Language: en\n" "Language: en\n"
@@ -167,7 +189,7 @@ msgstr ""
# Some comment # Some comment
msgid "Example" msgid "Example"
msgstr "Translated example" msgstr "Translated example"
` `
// Create po object // Create po object
po := new(Po) po := new(Po)
@@ -198,7 +220,7 @@ msgstr[0] "Singular form"
msgstr[1] "Plural form 1" msgstr[1] "Plural form 1"
msgstr[2] "Plural form 2" msgstr[2] "Plural form 2"
msgstr[3] "Plural form 3" msgstr[3] "Plural form 3"
` `
// Create po object // Create po object
po := new(Po) po := new(Po)
@@ -241,7 +263,7 @@ msgstr[0] "Singular form"
msgstr[1] "Plural form 1" msgstr[1] "Plural form 1"
msgstr[2] "Plural form 2" msgstr[2] "Plural form 2"
msgstr[3] "Plural form 3" msgstr[3] "Plural form 3"
` `
// Create po object // Create po object
po := new(Po) po := new(Po)
@@ -280,7 +302,7 @@ msgstr[0] "Singular form"
msgstr[1] "Plural form 1" msgstr[1] "Plural form 1"
msgstr[2] "Plural form 2" msgstr[2] "Plural form 2"
msgstr[3] "Plural form 3" msgstr[3] "Plural form 3"
` `
// Create po object // Create po object
po := new(Po) po := new(Po)
@@ -328,7 +350,7 @@ msgstr[0] "Singular form"
msgstr[1] "Plural form 1" msgstr[1] "Plural form 1"
msgstr[2] "Plural form 2" msgstr[2] "Plural form 2"
msgstr[3] "Plural form 3" msgstr[3] "Plural form 3"
` `
// Create po object // Create po object
po := new(Po) po := new(Po)
@@ -394,7 +416,7 @@ msgstr[0] "This one is the singular: %s"
msgstr[1] "This one is the plural: %s" msgstr[1] "This one is the plural: %s"
msgstr[2] "And this is the second plural form: %s" msgstr[2] "And this is the second plural form: %s"
` `
// Create Po object // Create Po object
po := new(Po) po := new(Po)