diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..4cd59f3 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,9 @@ +language: go +script: go test -v -race ./... + +go: + - 1.3 + - 1.4 + - 1.5 + - 1.6 + - tip diff --git a/README.md b/README.md index 9c7ef22..f1bb3ff 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,17 @@ [GNU gettext utilities](https://www.gnu.org/software/gettext) for Go. -Version: [v1.0.1](https://github.com/leonelquinteros/gotext/releases/tag/v1.0.1) +Version: [v1.1.0](https://github.com/leonelquinteros/gotext/releases/tag/v1.1.0) # Features - Implements GNU gettext support in native Go. -- Complete support for [PO files](https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html). -- Support for [pluralization rules](https://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html). -- Support for [message context](https://www.gnu.org/software/gettext/manual/html_node/Contexts.html). -- Support for variables inside translation strings using Go's [fmt package syntax](https://golang.org/pkg/fmt/). +- Complete support for [PO files](https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html) including: + - Support for multiline strings and headers. + - Support for variables inside translation strings using Go's [fmt syntax](https://golang.org/pkg/fmt/). + - Support for [pluralization rules](https://www.gnu.org/software/gettext/manual/html_node/Translating-plural-forms.html). + - Support for [message contexts](https://www.gnu.org/software/gettext/manual/html_node/Contexts.html). - Thread-safe: This package is safe for concurrent use across multiple goroutines. - It works with UTF-8 encoding as it's the default for Go language. - Unit tests available. @@ -256,6 +257,10 @@ msgstr "This one sets the var: %s" ## Use plural forms of translations PO format supports defining one or more plural forms for the same translation. +Relying on the PO file headers, a Plural-Forms formula can be set on the translation file +as defined in (https://www.gnu.org/savannah-checkouts/gnu/gettext/manual/html_node/Plural-forms.html) + +Plural formulas are parsed and evaluated using [Anko](https://github.com/mattn/anko) ```go import "github.com/leonelquinteros/gotext" @@ -263,6 +268,12 @@ import "github.com/leonelquinteros/gotext" func main() { // Set PO content str := ` +msgid "" +msgstr "" + +# Header below +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + msgid "Translate this" msgstr "Translated text" @@ -273,15 +284,14 @@ 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) po.Parse(str) - println(po.GetN("One with var: %s", "Several with vars: %s", 2, v)) - // "And this is the second plural form: Variable" + 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 3ce0c41..4f4d943 100644 --- a/gotext.go +++ b/gotext.go @@ -100,8 +100,7 @@ func Get(str string, vars ...interface{}) string { return GetD(domain, str, vars...) } -// GetN retrieves the (N)th plural form translation for the given string in the "default" domain. -// If n == 0, usually the singular form of the string is returned as defined in the PO file. +// GetN retrieves the (N)th plural form of translation for the given string in the "default" domain. // Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. func GetN(str, plural string, n int, vars ...interface{}) string { return GetND("default", str, plural, n, vars...) @@ -110,10 +109,10 @@ func GetN(str, plural string, n int, vars ...interface{}) string { // GetD returns the corresponding translation in the given domain for a given string. // Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. func GetD(dom, str string, vars ...interface{}) string { - return GetND(dom, str, str, 0, vars...) + return GetND(dom, str, str, 1, vars...) } -// GetND retrieves the (N)th plural form translation in the given domain for a given string. +// GetND retrieves the (N)th plural form of translation in the given domain for a given string. // Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. func GetND(dom, str, plural string, n int, vars ...interface{}) string { // Try to load default package Locale storage @@ -129,8 +128,7 @@ func GetC(str, ctx string, vars ...interface{}) string { return GetDC(domain, str, ctx, vars...) } -// GetNC retrieves the (N)th plural form translation for the given string in the given context in the "default" domain. -// If n == 0, usually the singular form of the string is returned as defined in the PO file. +// GetNC retrieves the (N)th plural form of translation for the given string in the given context in the "default" domain. // Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. func GetNC(str, plural string, n int, ctx string, vars ...interface{}) string { return GetNDC("default", str, plural, n, ctx, vars...) @@ -139,10 +137,10 @@ func GetNC(str, plural string, n int, ctx string, vars ...interface{}) string { // GetDC returns the corresponding translation in the given domain 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 GetDC(dom, str, ctx string, vars ...interface{}) string { - return GetNDC(dom, str, str, 0, ctx, vars...) + return GetNDC(dom, str, str, 1, ctx, vars...) } -// GetNDC retrieves the (N)th plural form translation in the given domain for a given string. +// GetNDC retrieves the (N)th plural form of translation in the given domain for a given string. // Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. func GetNDC(dom, str, plural string, n int, ctx string, vars ...interface{}) string { // Try to load default package Locale storage diff --git a/gotext_test.go b/gotext_test.go index 4f41d17..d9b786d 100644 --- a/gotext_test.go +++ b/gotext_test.go @@ -31,7 +31,17 @@ func TestGettersSetters(t *testing.T) { func TestPackageFunctions(t *testing.T) { // Set PO content - str := `# Some comment + 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" + +# Some comment msgid "My text" msgstr "Translated text" @@ -109,8 +119,8 @@ msgstr "More translation" // Test plural tr = GetN("One with var: %s", "Several with vars: %s", 2, v) - if tr != "And this is the second plural form: Variable" { - t.Errorf("Expected 'And this is the second plural form: Variable' but got '%s'", tr) + if tr != "This one is the plural: Variable" { + t.Errorf("Expected 'This one is the plural: Variable' but got '%s'", tr) } // Test context translations @@ -125,7 +135,7 @@ msgstr "More translation" t.Errorf("Expected 'This one is the singular in a Ctx context: Variable' but got '%s'", tr) } - tr = GetNC("One with var: %s", "Several with vars: %s", 1, "Ctx", v) + tr = GetNC("One with var: %s", "Several with vars: %s", 19, "Ctx", v) if tr != "This one is the plural in a Ctx context: Variable" { t.Errorf("Expected 'This one is the plural in a Ctx context: Variable' but got '%s'", tr) } diff --git a/locale.go b/locale.go index e52c27c..1cb59b7 100644 --- a/locale.go +++ b/locale.go @@ -107,8 +107,7 @@ func (l *Locale) Get(str string, vars ...interface{}) string { return l.GetD("default", str, vars...) } -// GetN retrieves the (N)th plural form translation for the given string in the "default" domain. -// If n == 0, usually the singular form of the string is returned as defined in the PO file. +// GetN retrieves the (N)th plural form of translation for the given string in the "default" domain. // Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. func (l *Locale) GetN(str, plural string, n int, vars ...interface{}) string { return l.GetND("default", str, plural, n, vars...) @@ -117,11 +116,10 @@ func (l *Locale) GetN(str, plural string, n int, vars ...interface{}) string { // GetD returns the corresponding translation in the given domain for the given string. // Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. func (l *Locale) GetD(dom, str string, vars ...interface{}) string { - return l.GetND(dom, str, str, 0, vars...) + return l.GetND(dom, str, str, 1, vars...) } -// GetND retrieves the (N)th plural form translation in the given domain for the given string. -// If n == 0, usually the singular form of the string is returned as defined in the PO file. +// GetND retrieves the (N)th plural form of translation in the given domain for the given string. // 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 { // Sync read @@ -146,8 +144,7 @@ func (l *Locale) GetC(str, ctx string, vars ...interface{}) string { return l.GetDC("default", str, ctx, vars...) } -// GetNC retrieves the (N)th plural form translation for the given string in the given context in the "default" domain. -// If n == 0, usually the singular form of the string is returned as defined in the PO file. +// GetNC retrieves the (N)th plural form of translation for the given string in the given context in the "default" domain. // Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. func (l *Locale) GetNC(str, plural string, n int, ctx string, vars ...interface{}) string { return l.GetNDC("default", str, plural, n, ctx, vars...) @@ -156,11 +153,10 @@ func (l *Locale) GetNC(str, plural string, n int, ctx string, vars ...interface{ // GetDC returns the corresponding translation in the given domain 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 (l *Locale) GetDC(dom, str, ctx string, vars ...interface{}) string { - return l.GetNDC(dom, str, str, 0, ctx, vars...) + return l.GetNDC(dom, str, str, 1, ctx, vars...) } -// GetNDC retrieves the (N)th plural form translation in the given domain for the given string in the given context. -// If n == 0, usually the singular form of the string is returned as defined in the PO file. +// GetNDC retrieves the (N)th plural form of translation in the given domain 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 (l *Locale) GetNDC(dom, str, plural string, n int, ctx string, vars ...interface{}) string { // Sync read diff --git a/locale_test.go b/locale_test.go index ef5df9d..515853d 100644 --- a/locale_test.go +++ b/locale_test.go @@ -8,7 +8,17 @@ import ( func TestLocale(t *testing.T) { // Set PO content - str := `# Some comment + 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" + +# Some comment msgid "My text" msgstr "Translated text" @@ -91,9 +101,9 @@ msgstr "More translation" } // Test plural - tr = l.GetND("my_domain", "One with var: %s", "Several with vars: %s", 2, v) - if tr != "And this is the second plural form: Variable" { - t.Errorf("Expected 'And this is the second plural form: Variable' but got '%s'", tr) + tr = l.GetND("my_domain", "One with var: %s", "Several with vars: %s", 7, v) + if tr != "This one is the plural: Variable" { + t.Errorf("Expected 'This one is the plural: Variable' but got '%s'", tr) } // Test non-existent "deafult" domain responses @@ -137,7 +147,7 @@ msgstr "More translation" } // Test plural - tr = l.GetNDC("my_domain", "One with var: %s", "Several with vars: %s", 1, "Ctx", v) + tr = l.GetNDC("my_domain", "One with var: %s", "Several with vars: %s", 3, "Ctx", v) if tr != "This one is the plural in a Ctx context: Test" { t.Errorf("Expected 'This one is the plural in a Ctx context: Test' but got '%s'", tr) } diff --git a/po.go b/po.go index d22adc7..7896397 100644 --- a/po.go +++ b/po.go @@ -1,8 +1,11 @@ package gotext import ( + "bufio" "fmt" + "github.com/mattn/anko/vm" "io/ioutil" + "net/textproto" "os" "strconv" "strings" @@ -44,8 +47,8 @@ func (t *translation) getN(n int) string { /* Po parses the content of any PO file and provides all the translation functions needed. -It's the base object used by all packafe methods. -And it's safe for concurrent use by multiple goroutines by using the sync package for write locking. +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. Example: @@ -64,6 +67,19 @@ Example: */ type Po struct { + // Headers + RawHeaders string + + // Language header + Language string + + // Plural-Forms header + PluralForms string + + // Parsed Plural-Forms header values + nplurals int + plural string + // Storage translations map[string]*translation contexts map[string]map[string]*translation @@ -123,7 +139,7 @@ func (po *Po) Parse(str string) { } // Skip invalid lines - if !strings.HasPrefix(l, "msgctxt") && !strings.HasPrefix(l, "msgid") && !strings.HasPrefix(l, "msgid_plural") && !strings.HasPrefix(l, "msgstr") { + if !strings.HasPrefix(l, "\"") && !strings.HasPrefix(l, "msgctxt") && !strings.HasPrefix(l, "msgid") && !strings.HasPrefix(l, "msgid_plural") && !strings.HasPrefix(l, "msgstr") { continue } @@ -198,21 +214,21 @@ func (po *Po) Parse(str string) { // Check for indexed translation forms if strings.HasPrefix(l, "[") { - in := strings.Index(l, "]") - if in == -1 { + idx := strings.Index(l, "]") + if idx == -1 { // Skip wrong index formatting continue } // Parse index - i, err := strconv.Atoi(l[1:in]) + i, err := strconv.Atoi(l[1:idx]) if err != nil { // Skip wrong index formatting continue } // Parse translation string - tr.trs[i], _ = strconv.Unquote(strings.TrimSpace(l[in+1:])) + tr.trs[i], _ = strconv.Unquote(strings.TrimSpace(l[idx+1:])) // Loop continue @@ -220,6 +236,31 @@ func (po *Po) Parse(str string) { // Save single translation form under 0 index tr.trs[0], _ = strconv.Unquote(l) + + // Loop + continue + } + + // Multi line strings and headers + if strings.HasPrefix(l, "\"") && strings.HasSuffix(l, "\"") { + // Check for multiline from previously set msgid + if tr.id != "" { + // Append to last translation found + uq, _ := strconv.Unquote(l) + tr.trs[len(tr.trs)-1] += uq + + // Loop + continue + } + + // Otherwise is a header + h, err := strconv.Unquote(strings.TrimSpace(l)) + if err != nil { + continue + } + + po.RawHeaders += h + continue } } @@ -237,6 +278,79 @@ func (po *Po) Parse(str string) { } po.Unlock() } + + // Parse headers + po.RawHeaders += "\n\n" + reader := bufio.NewReader(strings.NewReader(po.RawHeaders)) + tp := textproto.NewReader(reader) + + mimeHeader, err := tp.ReadMIMEHeader() + if err != nil { + return + } + + // Get/save needed headers + po.Language = mimeHeader.Get("Language") + po.PluralForms = mimeHeader.Get("Plural-Forms") + + // Parse Plural-Forms formula + if po.PluralForms == "" { + return + } + + // Split plural form header value + pfs := strings.Split(po.PluralForms, ";") + + // Parse values + for _, i := range pfs { + vs := strings.SplitN(i, "=", 2) + if len(vs) != 2 { + continue + } + + switch strings.TrimSpace(vs[0]) { + case "nplurals": + po.nplurals, _ = strconv.Atoi(vs[1]) + + case "plural": + po.plural = vs[1] + } + } +} + +// pluralForm calculates the plural form index corresponding to n. +// Returns 0 on error +func (po *Po) pluralForm(n int) int { + // Failsafe + if po.nplurals < 1 { + return 0 + } + if po.plural == "" { + return 0 + } + + // Init compiler + var env = vm.NewEnv() + env.Define("n", n) + + // Run script + plural, err := env.Execute(po.plural) + if err != nil { + return 0 + } + if plural.Type().Name() == "bool" { + if plural.Bool() { + return 1 + } else { + return 0 + } + } + + if int(plural.Int()) > po.nplurals { + return 0 + } + + return int(plural.Int()) } // Get retrieves the corresponding translation for the given string. @@ -256,8 +370,7 @@ func (po *Po) Get(str string, vars ...interface{}) string { return fmt.Sprintf(str, vars...) } -// GetN retrieves the (N)th plural form translation for the given string. -// If n == 0, usually the singular form of the string is returned as defined in the PO file. +// 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 @@ -266,7 +379,7 @@ func (po *Po) GetN(str, plural string, n int, vars ...interface{}) string { if po.translations != nil { if _, ok := po.translations[str]; ok { - return fmt.Sprintf(po.translations[str].getN(n), vars...) + return fmt.Sprintf(po.translations[str].getN(po.pluralForm(n)), vars...) } } @@ -295,8 +408,7 @@ func (po *Po) GetC(str, ctx string, vars ...interface{}) string { return fmt.Sprintf(str, vars...) } -// GetNC retrieves the (N)th plural form translation for the given string in the given context. -// If n == 0, usually the singular form of the string is returned as defined in the PO file. +// 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 @@ -307,7 +419,7 @@ 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 fmt.Sprintf(po.contexts[ctx][str].getN(n), vars...) + return fmt.Sprintf(po.contexts[ctx][str].getN(po.pluralForm(n)), vars...) } } } diff --git a/po_test.go b/po_test.go index bdb665f..97d4a66 100644 --- a/po_test.go +++ b/po_test.go @@ -8,7 +8,17 @@ import ( func TestPo(t *testing.T) { // Set PO content - str := `# Some comment + 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" + +# Some comment msgid "My text" msgstr "Translated text" @@ -16,6 +26,11 @@ msgstr "Translated text" msgid "Another string" msgstr "" +#Multi-line string +msgid "Multi-line" +msgstr "Multi " +"line" + msgid "One with var: %s" msgid_plural "Several with vars: %s" msgstr[0] "This one is the singular: %s" @@ -82,10 +97,16 @@ msgstr "More translation" t.Errorf("Expected 'This one is the singular: Variable' but got '%s'", tr) } + // Test multi-line + tr = po.Get("Multi-line") + if tr != "Multi line" { + t.Errorf("Expected 'Multi line' but got '%s'", tr) + } + // Test plural tr = po.GetN("One with var: %s", "Several with vars: %s", 2, v) - if tr != "And this is the second plural form: Variable" { - t.Errorf("Expected 'And this is the second plural form: Variable' but got '%s'", tr) + if tr != "This one is the plural: Variable" { + t.Errorf("Expected 'This one is the plural: Variable' but got '%s'", tr) } // Test inexistent translations @@ -94,7 +115,7 @@ msgstr "More translation" t.Errorf("Expected 'This is a test' but got '%s'", tr) } - tr = po.GetN("This is a test", "This are tests", 1) + tr = po.GetN("This is a test", "This are tests", 100) if tr != "This are tests" { t.Errorf("Expected 'This are tests' but got '%s'", tr) } @@ -105,7 +126,7 @@ msgstr "More translation" t.Errorf("Expected '' but got '%s'", tr) } - tr = po.GetN("This one has invalid syntax translations", "This are tests", 1) + tr = po.GetN("This one has invalid syntax translations", "This are tests", 4) if tr != "Plural index" { t.Errorf("Expected 'Plural index' but got '%s'", tr) } @@ -118,7 +139,7 @@ msgstr "More translation" } // Test plural - tr = po.GetNC("One with var: %s", "Several with vars: %s", 1, "Ctx", v) + tr = po.GetNC("One with var: %s", "Several with vars: %s", 17, "Ctx", v) if tr != "This one is the plural in a Ctx context: Test" { t.Errorf("Expected 'This one is the plural in a Ctx context: Test' but got '%s'", tr) } @@ -131,6 +152,210 @@ msgstr "More translation" } +func TestPoHeaders(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" + +# Some comment +msgid "Example" +msgstr "Translated example" + ` + + // Create po object + po := new(Po) + + // Parse + po.Parse(str) + + // Check headers expected + if po.Language != "en" { + t.Errorf("Expected 'Language: en' but got '%s'", po.Language) + } + + // Check headers expected + if po.PluralForms != "nplurals=2; plural=(n != 1);" { + t.Errorf("Expected 'Plural-Forms: nplurals=2; plural=(n != 1);' but got '%s'", po.PluralForms) + } +} + +func TestPluralForms(t *testing.T) { + // Single form + str := ` +"Plural-Forms: nplurals=1; plural=0;" + +# Some comment +msgid "Singular" +msgid_plural "Plural" +msgstr[0] "Singular form" +msgstr[1] "Plural form 1" +msgstr[2] "Plural form 2" +msgstr[3] "Plural form 3" + ` + + // Create po object + po := new(Po) + + // Parse + po.Parse(str) + + // Check plural form + n := po.pluralForm(0) + if n != 0 { + t.Errorf("Expected 0 for pluralForm(0), got %d", n) + } + n = po.pluralForm(1) + if n != 0 { + t.Errorf("Expected 0 for pluralForm(1), got %d", n) + } + n = po.pluralForm(2) + if n != 0 { + t.Errorf("Expected 0 for pluralForm(2), got %d", n) + } + n = po.pluralForm(3) + if n != 0 { + t.Errorf("Expected 0 for pluralForm(3), got %d", n) + } + n = po.pluralForm(50) + if n != 0 { + t.Errorf("Expected 0 for pluralForm(50), got %d", n) + } + + // ------------------------------------------------------------------------ + // 2 forms + str = ` +"Plural-Forms: nplurals=2; plural=n != 1;" + +# Some comment +msgid "Singular" +msgid_plural "Plural" +msgstr[0] "Singular form" +msgstr[1] "Plural form 1" +msgstr[2] "Plural form 2" +msgstr[3] "Plural form 3" + ` + + // Create po object + po = new(Po) + + // Parse + po.Parse(str) + + // Check plural form + n = po.pluralForm(0) + if n != 1 { + t.Errorf("Expected 1 for pluralForm(0), got %d", n) + } + n = po.pluralForm(1) + if n != 0 { + t.Errorf("Expected 0 for pluralForm(1), got %d", n) + } + n = po.pluralForm(2) + if n != 1 { + t.Errorf("Expected 1 for pluralForm(2), got %d", n) + } + n = po.pluralForm(3) + if n != 1 { + t.Errorf("Expected 1 for pluralForm(3), got %d", n) + } + + // ------------------------------------------------------------------------ + // 3 forms + str = ` +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;" + +# Some comment +msgid "Singular" +msgid_plural "Plural" +msgstr[0] "Singular form" +msgstr[1] "Plural form 1" +msgstr[2] "Plural form 2" +msgstr[3] "Plural form 3" + ` + + // Create po object + po = new(Po) + + // Parse + po.Parse(str) + + // Check plural form + n = po.pluralForm(0) + if n != 2 { + t.Errorf("Expected 2 for pluralForm(0), got %d", n) + } + n = po.pluralForm(1) + if n != 0 { + t.Errorf("Expected 0 for pluralForm(1), got %d", n) + } + n = po.pluralForm(2) + if n != 1 { + t.Errorf("Expected 1 for pluralForm(2), got %d", n) + } + n = po.pluralForm(3) + if n != 1 { + t.Errorf("Expected 1 for pluralForm(3), got %d", n) + } + n = po.pluralForm(100) + if n != 1 { + t.Errorf("Expected 1 for pluralForm(100), got %d", n) + } + n = po.pluralForm(49) + if n != 1 { + t.Errorf("Expected 1 for pluralForm(3), got %d", n) + } + + // ------------------------------------------------------------------------ + // 3 forms special + str = ` +"Plural-Forms: nplurals=3;" +"plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;" + +# Some comment +msgid "Singular" +msgid_plural "Plural" +msgstr[0] "Singular form" +msgstr[1] "Plural form 1" +msgstr[2] "Plural form 2" +msgstr[3] "Plural form 3" + ` + + // Create po object + po = new(Po) + + // Parse + po.Parse(str) + + // Check plural form + n = po.pluralForm(1) + if n != 0 { + t.Errorf("Expected 0 for pluralForm(1), got %d", n) + } + n = po.pluralForm(2) + if n != 1 { + t.Errorf("Expected 1 for pluralForm(2), got %d", n) + } + n = po.pluralForm(4) + if n != 1 { + t.Errorf("Expected 4 for pluralForm(4), got %d", n) + } + n = po.pluralForm(0) + if n != 2 { + t.Errorf("Expected 2 for pluralForm(2), got %d", n) + } + n = po.pluralForm(1000) + if n != 2 { + t.Errorf("Expected 2 for pluralForm(1000), got %d", n) + } +} + func TestTranslationObject(t *testing.T) { tr := newTranslation() str := tr.get()