Skip to content

Commit

Permalink
Make Locale and Po objects serializable. Closes #23
Browse files Browse the repository at this point in the history
  • Loading branch information
leonelquinteros committed Sep 7, 2018
1 parent 302c88a commit 4cbf30d
Show file tree
Hide file tree
Showing 8 changed files with 303 additions and 4 deletions.
6 changes: 5 additions & 1 deletion gotext.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ For quick/simple translations you can use the package level functions directly.
package gotext

import (
"encoding/gob"
"sync"
)

Expand All @@ -45,14 +46,17 @@ type config struct {

var globalConfig *config

// Init default configuration
func init() {
// Init default configuration
globalConfig = &config{
domain: "default",
language: "en_US",
library: "/usr/local/share/locale",
storage: nil,
}

// Register Translator types for gob encoding
gob.Register(TranslatorEncoding{})
}

// loadStorage creates a new Locale object at package level based on the Global variables settings.
Expand Down
66 changes: 66 additions & 0 deletions locale.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
package gotext

import (
"bytes"
"encoding/gob"
"os"
"path"
"sync"
Expand All @@ -19,6 +21,8 @@ multiple languages at the same time by working with this object.
Example:
import (
"encoding/gob"
"bytes"
"fmt"
"github.com/leonelquinteros/gotext"
)
Expand Down Expand Up @@ -236,3 +240,65 @@ func (l *Locale) GetNDC(dom, str, plural string, n int, ctx string, vars ...inte
// Return the same we received by default
return Printf(plural, vars...)
}

// LocaleEncoding is used as intermediary storage to encode Locale objects to Gob.
type LocaleEncoding struct {
Path string
Lang string
Domains map[string][]byte
DefaultDomain string
}

// MarshalBinary implements encoding BinaryMarshaler interface
func (l *Locale) MarshalBinary() ([]byte, error) {
obj := new(LocaleEncoding)
obj.DefaultDomain = l.defaultDomain
obj.Domains = make(map[string][]byte)
for k, v := range l.Domains {
var err error
obj.Domains[k], err = v.MarshalBinary()
if err != nil {
return nil, err
}
}
obj.Lang = l.lang
obj.Path = l.path

var buff bytes.Buffer
encoder := gob.NewEncoder(&buff)
err := encoder.Encode(obj)

return buff.Bytes(), err
}

// UnmarshalBinary implements encoding BinaryUnmarshaler interface
func (l *Locale) UnmarshalBinary(data []byte) error {
buff := bytes.NewBuffer(data)
obj := new(LocaleEncoding)

decoder := gob.NewDecoder(buff)
err := decoder.Decode(obj)
if err != nil {
return err
}

l.defaultDomain = obj.DefaultDomain
l.lang = obj.Lang
l.path = obj.Path

// Decode Domains
l.Domains = make(map[string]Translator)
for k, v := range obj.Domains {
var tr TranslatorEncoding
buff := bytes.NewBuffer(v)
trDecoder := gob.NewDecoder(buff)
err := trDecoder.Decode(&tr)
if err != nil {
return err
}

l.Domains[k] = tr.GetTranslator()
}

return nil
}
39 changes: 39 additions & 0 deletions locale_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -485,3 +485,42 @@ func TestArabicTranslation(t *testing.T) {
t.Errorf("Expected to get 'الكحول والتبغ', but got '%s'", tr)
}
}

func TestLocaleBinaryEncoding(t *testing.T) {
// Create Locale
l := NewLocale("fixtures/", "en_US")
l.AddDomain("default")

buff, err := l.MarshalBinary()
if err != nil {
t.Fatal(err)
}

l2 := new(Locale)
err = l2.UnmarshalBinary(buff)
if err != nil {
t.Fatal(err)
}

// Check object properties
if l.path != l2.path {
t.Fatalf("path doesn't match: '%s' vs '%s'", l.path, l2.path)
}
if l.lang != l2.lang {
t.Fatalf("lang doesn't match: '%s' vs '%s'", l.lang, l2.lang)
}
if l.defaultDomain != l2.defaultDomain {
t.Fatalf("defaultDomain doesn't match: '%s' vs '%s'", l.defaultDomain, l2.defaultDomain)
}

// Check translations
if l.Get("My text") != l2.Get("My text") {
t.Errorf("'%s' is different from '%s", l.Get("My text"), l2.Get("My text"))
}
if l.Get("More") != l2.Get("More") {
t.Errorf("'%s' is different from '%s", l.Get("More"), l2.Get("More"))
}
if l.GetN("One with var: %s", "Several with vars: %s", 3, "VALUE") != l2.GetN("One with var: %s", "Several with vars: %s", 3, "VALUE") {
t.Errorf("'%s' is different from '%s", l.GetN("One with var: %s", "Several with vars: %s", 3, "VALUE"), l2.GetN("One with var: %s", "Several with vars: %s", 3, "VALUE"))
}
}
45 changes: 45 additions & 0 deletions mo.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"bufio"
"bytes"
"encoding/binary"
"encoding/gob"
"io/ioutil"
"net/textproto"
"os"
Expand Down Expand Up @@ -425,3 +426,47 @@ func (mo *Mo) GetNC(str, plural string, n int, ctx string, vars ...interface{})
}
return Printf(plural, vars...)
}

// MarshalBinary implements encoding.BinaryMarshaler interface
func (mo *Mo) MarshalBinary() ([]byte, error) {
obj := new(TranslatorEncoding)
obj.Headers = mo.Headers
obj.Language = mo.Language
obj.PluralForms = mo.PluralForms
obj.Nplurals = mo.nplurals
obj.Plural = mo.plural
obj.Translations = mo.translations
obj.Contexts = mo.contexts

var buff bytes.Buffer
encoder := gob.NewEncoder(&buff)
err := encoder.Encode(obj)

return buff.Bytes(), err
}

// UnmarshalBinary implements encoding.BinaryUnmarshaler interface
func (mo *Mo) UnmarshalBinary(data []byte) error {
buff := bytes.NewBuffer(data)
obj := new(TranslatorEncoding)

decoder := gob.NewDecoder(buff)
err := decoder.Decode(obj)
if err != nil {
return err
}

mo.Headers = obj.Headers
mo.Language = obj.Language
mo.PluralForms = obj.PluralForms
mo.nplurals = obj.Nplurals
mo.plural = obj.Plural
mo.translations = obj.Translations
mo.contexts = obj.Contexts

if expr, err := plurals.Compile(mo.plural); err == nil {
mo.pluralforms = expr
}

return nil
}
30 changes: 30 additions & 0 deletions mo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,33 @@ func TestNewMoTranslatorRace(t *testing.T) {
<-pc
<-rc
}

func TestMoBinaryEncoding(t *testing.T) {
// Create mo objects
mo := new(Mo)
mo2 := new(Mo)

// Parse file
mo.ParseFile("fixtures/en_US/default.mo")

buff, err := mo.MarshalBinary()
if err != nil {
t.Fatal(err)
}

err = mo2.UnmarshalBinary(buff)
if err != nil {
t.Fatal(err)
}

// Test translations
tr := mo2.Get("My text")
if tr != "Translated text" {
t.Errorf("Expected 'Translated text' but got '%s'", tr)
}
// Test translations
tr = mo2.Get("language")
if tr != "en_US" {
t.Errorf("Expected 'en_US' but got '%s'", tr)
}
}
46 changes: 46 additions & 0 deletions po.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ package gotext

import (
"bufio"
"bytes"
"encoding/gob"
"io/ioutil"
"net/textproto"
"os"
Expand Down Expand Up @@ -451,3 +453,47 @@ func (po *Po) GetNC(str, plural string, n int, ctx string, vars ...interface{})
}
return Printf(plural, vars...)
}

// MarshalBinary implements encoding.BinaryMarshaler interface
func (po *Po) MarshalBinary() ([]byte, error) {
obj := new(TranslatorEncoding)
obj.Headers = po.Headers
obj.Language = po.Language
obj.PluralForms = po.PluralForms
obj.Nplurals = po.nplurals
obj.Plural = po.plural
obj.Translations = po.translations
obj.Contexts = po.contexts

var buff bytes.Buffer
encoder := gob.NewEncoder(&buff)
err := encoder.Encode(obj)

return buff.Bytes(), err
}

// UnmarshalBinary implements encoding.BinaryUnmarshaler interface
func (po *Po) UnmarshalBinary(data []byte) error {
buff := bytes.NewBuffer(data)
obj := new(TranslatorEncoding)

decoder := gob.NewDecoder(buff)
err := decoder.Decode(obj)
if err != nil {
return err
}

po.Headers = obj.Headers
po.Language = obj.Language
po.PluralForms = obj.PluralForms
po.nplurals = obj.Nplurals
po.plural = obj.Plural
po.translations = obj.Translations
po.contexts = obj.Contexts

if expr, err := plurals.Compile(po.plural); err == nil {
po.pluralforms = expr
}

return nil
}
33 changes: 30 additions & 3 deletions po_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ import (
"os"
"path"
"testing"

)

func TestPo_Get(t *testing.T) {

// Create po object
po := new(Po)

Expand Down Expand Up @@ -589,7 +587,6 @@ msgstr[2] "And this is the second plural form: %s"
}

func TestNewPoTranslatorRace(t *testing.T) {

// Create Po object
mo := NewPoTranslator()

Expand Down Expand Up @@ -617,3 +614,33 @@ func TestNewPoTranslatorRace(t *testing.T) {
<-pc
<-rc
}

func TestPoBinaryEncoding(t *testing.T) {
// Create po objects
po := new(Po)
po2 := new(Po)

// Parse file
po.ParseFile("fixtures/en_US/default.po")

buff, err := po.MarshalBinary()
if err != nil {
t.Fatal(err)
}

err = po2.UnmarshalBinary(buff)
if err != nil {
t.Fatal(err)
}

// Test translations
tr := po2.Get("My text")
if tr != "Translated text" {
t.Errorf("Expected 'Translated text' but got '%s'", tr)
}
// Test translations
tr = po2.Get("language")
if tr != "en_US" {
t.Errorf("Expected 'en_US' but got '%s'", tr)
}
}
Loading

0 comments on commit 4cbf30d

Please sign in to comment.