From 4b94e83723274165cef26e02b45d06a26133b122 Mon Sep 17 00:00:00 2001 From: Leonel Quinteros Date: Fri, 1 Sep 2017 13:28:51 -0300 Subject: [PATCH] Properly handle singular vs plural defaults for untranslated strings. Fixes #9 --- README.md | 39 +++++++++++++------ gotext.go | 9 +++-- gotext_test.go | 100 ++++++++++++++++++++++++++++++++++++++++++++----- locale.go | 9 +++-- po.go | 18 ++++++--- 5 files changed, 142 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 146e55b..dc395c2 100644 --- a/README.md +++ b/README.md @@ -156,17 +156,20 @@ This is a normal Go compiler behavior. For quick/simple translations you can use the package level functions directly. ```go -import "github.com/leonelquinteros/gotext" +import ( + "fmt" + "github.com/leonelquinteros/gotext" +) func main() { // Configure package gotext.Configure("/path/to/locales/root/dir", "en_UK", "domain-name") // Translate text from default domain - println(gotext.Get("My text on 'domain-name' domain")) + fmt.Println(gotext.Get("My text on 'domain-name' domain")) // Translate text from a different domain without reconfigure - println(gotext.GetD("domain2", "Another text on a different domain")) + fmt.Println(gotext.GetD("domain2", "Another text on a different domain")) } ``` @@ -177,7 +180,10 @@ All translation strings support dynamic variables to be inserted without transla Use the fmt.Printf syntax (from Go's "fmt" package) to specify how to print the non-translated variable inside the translation string. ```go -import "github.com/leonelquinteros/gotext" +import ( + "fmt" + "github.com/leonelquinteros/gotext" +) func main() { // Configure package @@ -187,7 +193,7 @@ func main() { name := "John" // Translate text with variables - println(gotext.Get("Hi, my name is %s", name)) + fmt.Println(gotext.Get("Hi, my name is %s", name)) } ``` @@ -199,7 +205,10 @@ When having multiple languages/domains/libraries at the same time, you can creat so you can handle each settings on their own. ```go -import "github.com/leonelquinteros/gotext" +import ( + "fmt" + "github.com/leonelquinteros/gotext" +) func main() { // Create Locale with library path and language code @@ -209,13 +218,13 @@ func main() { l.AddDomain("default") // Translate text from default domain - println(l.Get("Translate this")) + fmt.Println(l.Get("Translate this")) // Load different domain l.AddDomain("translations") // Translate text from domain - println(l.GetD("translations", "Translate this")) + fmt.Println(l.GetD("translations", "Translate this")) } ``` @@ -233,7 +242,10 @@ For when you need to work with PO files and strings, you can directly use the Po object to parse it and access the translations in there in the same way. ```go -import "github.com/leonelquinteros/gotext" +import ( + "fmt" + "github.com/leonelquinteros/gotext" +) func main() { // Set PO content @@ -252,7 +264,7 @@ msgstr "This one sets the var: %s" po := new(Po) po.Parse(str) - println(po.Get("Translate this")) + fmt.Println(po.Get("Translate this")) } ``` @@ -266,7 +278,10 @@ as defined in (https://www.gnu.org/savannah-checkouts/gnu/gettext/manual/html_no Plural formulas are parsed and evaluated using [Kinako](https://github.com/mattn/kinako) ```go -import "github.com/leonelquinteros/gotext" +import ( + "fmt" + "github.com/leonelquinteros/gotext" +) func main() { // Set PO content @@ -293,7 +308,7 @@ msgstr[1] "This one is the plural: %s" po := new(Po) po.Parse(str) - println(po.GetN("One with var: %s", "Several with vars: %s", 54, v)) + fmt.Println(po.GetN("One with var: %s", "Several with vars: %s", 54, v)) // "This one is the plural: Variable" } ``` diff --git a/gotext.go b/gotext.go index 4f4d943..d6dc200 100644 --- a/gotext.go +++ b/gotext.go @@ -3,17 +3,20 @@ Package gotext implements GNU gettext utilities. For quick/simple translations you can use the package level functions directly. - import "github.com/leonelquinteros/gotext" + import ( + "fmt" + "github.com/leonelquinteros/gotext" + ) func main() { // Configure package gotext.Configure("/path/to/locales/root/dir", "en_UK", "domain-name") // Translate text from default domain - println(gotext.Get("My text on 'domain-name' domain")) + fmt.Println(gotext.Get("My text on 'domain-name' domain")) // Translate text from a different domain without reconfigure - println(gotext.GetD("domain2", "Another text on a different domain")) + fmt.Println(gotext.GetD("domain2", "Another text on a different domain")) } */ diff --git a/gotext_test.go b/gotext_test.go index a642e23..58c241f 100644 --- a/gotext_test.go +++ b/gotext_test.go @@ -79,6 +79,11 @@ msgstr "Some random translation in a context" msgid "More" msgstr "More translation" +msgid "Untranslated" +msgid_plural "Several untranslated" +msgstr[0] "" +msgstr[1] "" + ` // Create Locales directory on default location @@ -141,6 +146,79 @@ msgstr "More translation" } } +func TestUntranslated(t *testing.T) { + // Set PO content + str := ` +# msgid "" +# msgstr "" +# Initial comment +# Headers below +"Language: en\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Untranslated" +msgid_plural "Several untranslated" +msgstr[0] "" +msgstr[1] "" + + ` + + // Create Locales directory on default location + dirname := path.Clean("/tmp" + 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) + "default.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()) + } + + // Set package configuration + Configure("/tmp", "en_US", "default") + + // Test untranslated + tr := Get("Untranslated") + if tr != "Untranslated" { + t.Errorf("Expected 'Untranslated' but got '%s'", tr) + } + tr = GetN("Untranslated", "Several untranslated", 1) + if tr != "Untranslated" { + t.Errorf("Expected 'Untranslated' but got '%s'", tr) + } + + tr = GetN("Untranslated", "Several untranslated", 2) + if tr != "Several untranslated" { + t.Errorf("Expected 'Several untranslated' but got '%s'", tr) + } + + tr = GetD("default", "Untranslated") + if tr != "Untranslated" { + t.Errorf("Expected 'Untranslated' but got '%s'", tr) + } + tr = GetND("default", "Untranslated", "Several untranslated", 1) + if tr != "Untranslated" { + t.Errorf("Expected 'Untranslated' but got '%s'", tr) + } + + tr = GetND("default", "Untranslated", "Several untranslated", 2) + if tr != "Several untranslated" { + t.Errorf("Expected 'Several untranslated' but got '%s'", tr) + } +} + func TestPackageRace(t *testing.T) { // Set PO content str := `# Some comment @@ -184,16 +262,18 @@ msgstr[2] "And this is the second plural form: %s" c1 := make(chan bool) c2 := make(chan bool) - // Test translations - go func(done chan bool) { - Get("My text") - done <- true - }(c1) + for i := 0; i < 100; i++ { + // Test translations + go func(done chan bool) { + Get("My text") + done <- true + }(c1) - go func(done chan bool) { - Get("My text") - done <- true - }(c2) + go func(done chan bool) { + Get("My text") + done <- true + }(c2) - Get("My text") + Get("My text") + } } diff --git a/locale.go b/locale.go index 1cb59b7..66015f4 100644 --- a/locale.go +++ b/locale.go @@ -14,7 +14,10 @@ multiple languages at the same time by working with this object. Example: - import "github.com/leonelquinteros/gotext" + import ( + "fmt" + "github.com/leonelquinteros/gotext" + ) func main() { // Create Locale with library path and language code @@ -24,13 +27,13 @@ Example: l.AddDomain("default") // Translate text from default domain - println(l.Get("Translate this")) + fmt.Println(l.Get("Translate this")) // Load different domain ('/path/to/i18n/dir/en_US/LC_MESSAGES/extras.po') l.AddDomain("extras") // Translate text from domain - println(l.GetD("extras", "Translate this")) + fmt.Println(l.GetD("extras", "Translate this")) } */ diff --git a/po.go b/po.go index b521da7..7b76af5 100644 --- a/po.go +++ b/po.go @@ -3,14 +3,13 @@ package gotext import ( "bufio" "fmt" + "github.com/mattn/kinako/vm" "io/ioutil" "net/textproto" "os" "strconv" "strings" "sync" - - "github.com/mattn/kinako/vm" ) type translation struct { @@ -46,7 +45,12 @@ func (t *translation) getN(n int) string { } } - // Return unstranlated plural by default + // Return unstranlated singular if corresponding + if n == 0 { + return t.id + } + + // Return untranslated plural by default return t.pluralID } @@ -57,7 +61,10 @@ And it's safe for concurrent use by multiple goroutines by using the sync packag Example: - import "github.com/leonelquinteros/gotext" + import ( + "fmt" + "github.com/leonelquinteros/gotext" + ) func main() { // Create po object @@ -67,7 +74,7 @@ Example: po.ParseFile("/path/to/po/file/translations.po") // Get translation - println(po.Get("Translate this")) + fmt.Println(po.Get("Translate this")) } */ @@ -439,6 +446,7 @@ func (po *Po) GetN(str, plural string, n int, vars ...interface{}) string { if n == 1 { return fmt.Sprintf(str, vars...) } + return fmt.Sprintf(plural, vars...) }