3 Commits

Author SHA1 Message Date
Leonel Quinteros
50cf0f07b7 Release v0.9.1 2016-06-24 17:48:06 -03:00
Leonel Quinteros
d6f4cbb2d5 Add race conditions tests. Fix races. 2016-06-24 17:45:12 -03:00
Leonel Quinteros
6e728a3df5 Link to release on Readme 2016-06-24 17:14:07 -03:00
6 changed files with 188 additions and 1 deletions

View File

@@ -4,7 +4,7 @@
GNU gettext utilities for Go. GNU gettext utilities for Go.
**Version: 0.9.0** Version: [0.9.1](https://github.com/leonelquinteros/gotext/releases/tag/v0.9.1)
#Features #Features

View File

@@ -63,3 +63,60 @@ msgstr[2] "And this is the second plural form: %s"
t.Errorf("Expected 'And this is the second plural form: Variable' but got '%s'", tr) t.Errorf("Expected 'And this is the second plural form: Variable' but got '%s'", tr)
} }
} }
func TestPackageRace(t *testing.T) {
// Set PO content
str := `# Some comment
msgid "My text"
msgstr "Translated text"
# More comments
msgid "Another string"
msgstr ""
msgid "One with var: %s"
msgid_plural "Several with vars: %s"
msgstr[0] "This one is the singular: %s"
msgstr[1] "This one is the plural: %s"
msgstr[2] "And this is the second plural form: %s"
`
// Create Locales directory on default location
dirname := path.Clean(library + string(os.PathSeparator) + "en_US")
err := os.MkdirAll(dirname, os.ModePerm)
if err != nil {
t.Fatalf("Can't create test directory: %s", err.Error())
}
// Write PO content to default domain file
filename := path.Clean(dirname + string(os.PathSeparator) + domain + ".po")
f, err := os.Create(filename)
if err != nil {
t.Fatalf("Can't create test file: %s", err.Error())
}
defer f.Close()
_, err = f.WriteString(str)
if err != nil {
t.Fatalf("Can't write to test file: %s", err.Error())
}
// Init sync channels
c1 := make(chan bool)
c2 := make(chan bool)
// Test translations
go func(done chan bool) {
println(Get("My text"))
done <- true
}(c1)
go func(done chan bool) {
println(Get("My text"))
done <- true
}(c2)
println(Get("My text"))
}

View File

@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
"path" "path"
"sync"
) )
/* /*
@@ -42,6 +43,9 @@ type Locale struct {
// List of available domains for this locale. // List of available domains for this locale.
domains map[string]*Po domains map[string]*Po
// Sync Mutex
sync.RWMutex
} }
// NewLocale creates and initializes a new Locale object for a given language. // NewLocale creates and initializes a new Locale object for a given language.
@@ -73,6 +77,9 @@ func (l *Locale) AddDomain(dom string) {
po.ParseFile(filename) po.ParseFile(filename)
// Save new domain // Save new domain
l.Lock()
defer l.Unlock()
if l.domains == nil { if l.domains == nil {
l.domains = make(map[string]*Po) l.domains = make(map[string]*Po)
} }
@@ -102,6 +109,10 @@ func (l *Locale) GetD(dom, str string, vars ...interface{}) string {
// If n == 0, usually the singular form of the string is returned as defined in the PO file. // If n == 0, usually the singular form of the string is returned as defined in the PO file.
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. // Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
func (l *Locale) GetND(dom, str, plural string, n int, vars ...interface{}) string { func (l *Locale) GetND(dom, str, plural string, n int, vars ...interface{}) string {
// Sync read
l.RLock()
defer l.RUnlock()
if l.domains != nil { if l.domains != nil {
if _, ok := l.domains[dom]; ok { if _, ok := l.domains[dom]; ok {
if l.domains[dom] != nil { if l.domains[dom] != nil {

View File

@@ -69,3 +69,69 @@ msgstr[2] "And this is the second plural form: %s"
t.Errorf("Expected 'And this is the second plural form: Variable' but got '%s'", tr) t.Errorf("Expected 'And this is the second plural form: Variable' but got '%s'", tr)
} }
} }
func TestLocaleRace(t *testing.T) {
// Set PO content
str := `# Some comment
msgid "My text"
msgstr "Translated text"
# More comments
msgid "Another string"
msgstr ""
msgid "One with var: %s"
msgid_plural "Several with vars: %s"
msgstr[0] "This one is the singular: %s"
msgstr[1] "This one is the plural: %s"
msgstr[2] "And this is the second plural form: %s"
`
// Create Locales directory with simplified language code
dirname := path.Clean("/tmp" + string(os.PathSeparator) + "es")
err := os.MkdirAll(dirname, os.ModePerm)
if err != nil {
t.Fatalf("Can't create test directory: %s", err.Error())
}
// Write PO content to file
filename := path.Clean(dirname + string(os.PathSeparator) + "race.po")
f, err := os.Create(filename)
if err != nil {
t.Fatalf("Can't create test file: %s", err.Error())
}
defer f.Close()
_, err = f.WriteString(str)
if err != nil {
t.Fatalf("Can't write to test file: %s", err.Error())
}
// Create Locale with full language code
l := NewLocale("/tmp", "es")
// Init sync channels
ac := make(chan bool)
rc := make(chan bool)
// Add domain in goroutine
go func(l *Locale, done chan bool) {
l.AddDomain("race")
done <- true
}(l, ac)
// Get translations in goroutine
go func(l *Locale, done chan bool) {
println(l.GetD("race", "My text"))
done <- true
}(l, rc)
// Get translations at top level
println(l.GetD("race", "My text"))
// Wait for goroutines to finish
<-ac
<-rc
}

8
po.go
View File

@@ -186,6 +186,10 @@ func (po *Po) Parse(str string) {
// 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. // 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 { func (po *Po) Get(str string, vars ...interface{}) string {
// Sync read
po.RLock()
defer po.RUnlock()
if po.translations != nil { if po.translations != nil {
if _, ok := po.translations[str]; ok { if _, ok := po.translations[str]; ok {
return fmt.Sprintf(po.translations[str].get(), vars...) return fmt.Sprintf(po.translations[str].get(), vars...)
@@ -200,6 +204,10 @@ func (po *Po) Get(str string, vars ...interface{}) string {
// If n == 0, usually the singular form of the string is returned as defined in the PO file. // If n == 0, usually the singular form of the string is returned as defined in the PO file.
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. // 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 { func (po *Po) GetN(str, plural string, n int, vars ...interface{}) string {
// Sync read
po.RLock()
defer po.RUnlock()
if po.translations != nil { if po.translations != nil {
if _, ok := po.translations[str]; ok { if _, ok := po.translations[str]; ok {
return fmt.Sprintf(po.translations[str].getN(n), vars...) return fmt.Sprintf(po.translations[str].getN(n), vars...)

View File

@@ -59,3 +59,48 @@ msgstr[2] "And this is the second plural form: %s"
t.Errorf("Expected 'And this is the second plural form: Variable' but got '%s'", tr) t.Errorf("Expected 'And this is the second plural form: Variable' but got '%s'", tr)
} }
} }
func TestPoRace(t *testing.T) {
// Set PO content
str := `# Some comment
msgid "My text"
msgstr "Translated text"
# More comments
msgid "Another string"
msgstr ""
msgid "One with var: %s"
msgid_plural "Several with vars: %s"
msgstr[0] "This one is the singular: %s"
msgstr[1] "This one is the plural: %s"
msgstr[2] "And this is the second plural form: %s"
`
// Create Po object
po := new(Po)
// Create sync channels
pc := make(chan bool)
rc := make(chan bool)
// Parse po content in a goroutine
go func(po *Po, done chan bool) {
po.Parse(str)
done <- true
}(po, pc)
// Read some translation on a goroutine
go func(po *Po, done chan bool) {
println(po.Get("My text"))
done <- true
}(po, rc)
// Read something at top level
println(po.Get("My text"))
// Wait for goroutines to finish
<-pc
<-rc
}