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:
151
po.go
151
po.go
@@ -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...)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user