Create MO parser

Refactored a bit too, so we can use interfaces to take Mo and Po files

added fixtures

found that the parser for Po files have a bug... but it works... so not touched
This commit is contained in:
2018-03-23 21:17:05 +01:00
parent 8c36835ece
commit cd46239477
23 changed files with 1726 additions and 234 deletions

151
po.go
View File

@@ -1,3 +1,8 @@
/*
* Copyright (c) 2018 DeineAgentur UG https://www.deineagentur.com. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for full license information.
*/
package gotext
import (
@@ -12,50 +17,8 @@ import (
"github.com/leonelquinteros/gotext/plurals"
)
type translation struct {
id string
pluralID string
trs map[int]string
}
func newTranslation() *translation {
tr := new(translation)
tr.trs = make(map[int]string)
return tr
}
func (t *translation) get() string {
// Look for translation index 0
if _, ok := t.trs[0]; ok {
if t.trs[0] != "" {
return t.trs[0]
}
}
// Return untranslated id by default
return t.id
}
func (t *translation) getN(n int) string {
// Look for translation index
if _, ok := t.trs[n]; ok {
if t.trs[n] != "" {
return t.trs[n]
}
}
// Return untranslated singular if corresponding
if n == 0 {
return t.id
}
// Return untranslated plural by default
return t.pluralID
}
/*
Po parses the content of any PO file and provides all the translation functions needed.
Po parses the content of any PO file and provides all the Translation functions needed.
It's the base object used by all package methods.
And it's safe for concurrent use by multiple goroutines by using the sync package for locking.
@@ -68,12 +31,12 @@ Example:
func main() {
// Create po object
po := new(gotext.Po)
po := gotext.NewPoTranslator()
// Parse .po file
po.ParseFile("/path/to/po/file/translations.po")
// Get translation
// Get Translation
fmt.Println(po.Get("Translate this"))
}
@@ -94,14 +57,14 @@ type Po struct {
pluralforms plurals.Expression
// Storage
translations map[string]*translation
contexts map[string]map[string]*translation
translations map[string]*Translation
contexts map[string]map[string]*Translation
// Sync Mutex
sync.RWMutex
// Parsing buffers
trBuffer *translation
trBuffer *Translation
ctxBuffer string
}
@@ -115,6 +78,10 @@ const (
msgStr
)
func NewPoTranslator() Translator {
return new(Po)
}
// 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) {
// Check if file exists
@@ -134,25 +101,25 @@ func (po *Po) ParseFile(f string) {
return
}
po.Parse(string(data))
po.Parse(data)
}
// Parse loads the translations specified in the provided string (str)
func (po *Po) Parse(str string) {
func (po *Po) Parse(buf []byte) {
// Lock while parsing
po.Lock()
// Init storage
if po.translations == nil {
po.translations = make(map[string]*translation)
po.contexts = make(map[string]map[string]*translation)
po.translations = make(map[string]*Translation)
po.contexts = make(map[string]map[string]*Translation)
}
// Get lines
lines := strings.Split(str, "\n")
lines := strings.Split(string(buf), "\n")
// Init buffer
po.trBuffer = newTranslation()
po.trBuffer = NewTranslation()
po.ctxBuffer = ""
state := head
@@ -186,7 +153,7 @@ func (po *Po) Parse(str string) {
continue
}
// Save translation
// Save Translation
if strings.HasPrefix(l, "msgstr") {
po.parseMessage(l)
state = msgStr
@@ -200,7 +167,7 @@ func (po *Po) Parse(str string) {
}
}
// Save last translation buffer.
// Save last Translation buffer.
po.saveBuffer()
// Unlock to parse headers
@@ -210,33 +177,33 @@ func (po *Po) Parse(str string) {
po.parseHeaders()
}
// saveBuffer takes the context and translation buffers
// saveBuffer takes the context and Translation buffers
// and saves it on the translations collection
func (po *Po) saveBuffer() {
// With no context...
if po.ctxBuffer == "" {
po.translations[po.trBuffer.id] = po.trBuffer
po.translations[po.trBuffer.ID] = po.trBuffer
} else {
// With context...
if _, ok := po.contexts[po.ctxBuffer]; !ok {
po.contexts[po.ctxBuffer] = make(map[string]*translation)
po.contexts[po.ctxBuffer] = make(map[string]*Translation)
}
po.contexts[po.ctxBuffer][po.trBuffer.id] = po.trBuffer
po.contexts[po.ctxBuffer][po.trBuffer.ID] = po.trBuffer
// Cleanup current context buffer if needed
if po.trBuffer.id != "" {
if po.trBuffer.ID != "" {
po.ctxBuffer = ""
}
}
// Flush translation buffer
po.trBuffer = newTranslation()
// Flush Translation buffer
po.trBuffer = NewTranslation()
}
// parseContext takes a line starting with "msgctxt",
// saves the current translation buffer and creates a new context.
// saves the current Translation buffer and creates a new context.
func (po *Po) parseContext(l string) {
// Save current translation buffer.
// Save current Translation buffer.
po.saveBuffer()
// Buffer context
@@ -244,25 +211,25 @@ func (po *Po) parseContext(l string) {
}
// parseID takes a line starting with "msgid",
// saves the current translation and creates a new msgid buffer.
// saves the current Translation and creates a new msgid buffer.
func (po *Po) parseID(l string) {
// Save current translation buffer.
// Save current Translation buffer.
po.saveBuffer()
// Set id
po.trBuffer.id, _ = strconv.Unquote(strings.TrimSpace(strings.TrimPrefix(l, "msgid")))
po.trBuffer.ID, _ = strconv.Unquote(strings.TrimSpace(strings.TrimPrefix(l, "msgid")))
}
// parsePluralID saves the plural id buffer from a line starting with "msgid_plural"
func (po *Po) parsePluralID(l string) {
po.trBuffer.pluralID, _ = strconv.Unquote(strings.TrimSpace(strings.TrimPrefix(l, "msgid_plural")))
po.trBuffer.PluralID, _ = strconv.Unquote(strings.TrimSpace(strings.TrimPrefix(l, "msgid_plural")))
}
// parseMessage takes a line starting with "msgstr" and saves it into the current buffer.
func (po *Po) parseMessage(l string) {
l = strings.TrimSpace(strings.TrimPrefix(l, "msgstr"))
// Check for indexed translation forms
// Check for indexed Translation forms
if strings.HasPrefix(l, "[") {
idx := strings.Index(l, "]")
if idx == -1 {
@@ -277,15 +244,15 @@ func (po *Po) parseMessage(l string) {
return
}
// Parse translation string
po.trBuffer.trs[i], _ = strconv.Unquote(strings.TrimSpace(l[idx+1:]))
// Parse Translation string
po.trBuffer.Trs[i], _ = strconv.Unquote(strings.TrimSpace(l[idx+1:]))
// Loop
return
}
// Save single translation form under 0 index
po.trBuffer.trs[0], _ = strconv.Unquote(l)
// Save single Translation form under 0 index
po.trBuffer.Trs[0], _ = strconv.Unquote(l)
}
// parseString takes a well formatted string without prefix
@@ -295,16 +262,16 @@ func (po *Po) parseString(l string, state parseState) {
switch state {
case msgStr:
// Append to last translation found
po.trBuffer.trs[len(po.trBuffer.trs)-1] += clean
// Append to last Translation found
po.trBuffer.Trs[len(po.trBuffer.Trs)-1] += clean
case msgID:
// Multiline msgid - Append to current id
po.trBuffer.id += clean
po.trBuffer.ID += clean
case msgIDPlural:
// Multiline msgid - Append to current id
po.trBuffer.pluralID += clean
po.trBuffer.PluralID += clean
case msgCtxt:
// Multiline context - Append to current context
@@ -405,7 +372,7 @@ func (po *Po) pluralForm(n int) int {
return po.pluralforms.Eval(uint32(n))
}
// Get retrieves the corresponding translation for the given string.
// Get retrieves the corresponding Translation for the given string.
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
func (po *Po) Get(str string, vars ...interface{}) string {
// Sync read
@@ -414,15 +381,15 @@ func (po *Po) Get(str string, vars ...interface{}) string {
if po.translations != nil {
if _, ok := po.translations[str]; ok {
return printf(po.translations[str].get(), vars...)
return Printf(po.translations[str].Get(), vars...)
}
}
// Return the same we received by default
return printf(str, vars...)
return Printf(str, vars...)
}
// GetN retrieves the (N)th plural form of translation for the given string.
// GetN retrieves the (N)th plural form of Translation for the given string.
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
func (po *Po) GetN(str, plural string, n int, vars ...interface{}) string {
// Sync read
@@ -431,17 +398,17 @@ func (po *Po) GetN(str, plural string, n int, vars ...interface{}) string {
if po.translations != nil {
if _, ok := po.translations[str]; ok {
return printf(po.translations[str].getN(po.pluralForm(n)), vars...)
return Printf(po.translations[str].GetN(po.pluralForm(n)), vars...)
}
}
if n == 1 {
return printf(str, vars...)
return Printf(str, vars...)
}
return printf(plural, vars...)
return Printf(plural, vars...)
}
// GetC retrieves the corresponding translation for a given string in the given context.
// GetC retrieves the corresponding Translation for a given string in the given context.
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
func (po *Po) GetC(str, ctx string, vars ...interface{}) string {
// Sync read
@@ -452,17 +419,17 @@ func (po *Po) GetC(str, ctx string, vars ...interface{}) string {
if _, ok := po.contexts[ctx]; ok {
if po.contexts[ctx] != nil {
if _, ok := po.contexts[ctx][str]; ok {
return printf(po.contexts[ctx][str].get(), vars...)
return Printf(po.contexts[ctx][str].Get(), vars...)
}
}
}
}
// Return the string we received by default
return printf(str, vars...)
return Printf(str, vars...)
}
// GetNC retrieves the (N)th plural form of translation for the given string in the given context.
// GetNC retrieves the (N)th plural form of Translation for the given string in the given context.
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
func (po *Po) GetNC(str, plural string, n int, ctx string, vars ...interface{}) string {
// Sync read
@@ -473,14 +440,14 @@ func (po *Po) GetNC(str, plural string, n int, ctx string, vars ...interface{})
if _, ok := po.contexts[ctx]; ok {
if po.contexts[ctx] != nil {
if _, ok := po.contexts[ctx][str]; ok {
return printf(po.contexts[ctx][str].getN(po.pluralForm(n)), vars...)
return Printf(po.contexts[ctx][str].GetN(po.pluralForm(n)), vars...)
}
}
}
}
if n == 1 {
return printf(str, vars...)
return Printf(str, vars...)
}
return printf(plural, vars...)
return Printf(plural, vars...)
}