Plural-Forms formula support. Headers parsing. Multiline strings support.

This commit is contained in:
Leonel Quinteros
2016-07-15 19:04:59 -03:00
parent af707140e3
commit 74daa24696
8 changed files with 424 additions and 54 deletions

9
.travis.yml Normal file
View File

@@ -0,0 +1,9 @@
language: go
script: go test -v -race ./...
go:
- 1.3
- 1.4
- 1.5
- 1.6
- tip

View File

@@ -4,16 +4,17 @@
[GNU gettext utilities](https://www.gnu.org/software/gettext) for Go. [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 # Features
- Implements GNU gettext support in native Go. - Implements GNU gettext support in native Go.
- Complete support for [PO files](https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html). - Complete support for [PO files](https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html) including:
- Support for [pluralization rules](https://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html). - Support for multiline strings and headers.
- 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 syntax](https://golang.org/pkg/fmt/).
- Support for variables inside translation strings using Go's [fmt package 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. - 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. - It works with UTF-8 encoding as it's the default for Go language.
- Unit tests available. - Unit tests available.
@@ -256,6 +257,10 @@ msgstr "This one sets the var: %s"
## Use plural forms of translations ## Use plural forms of translations
PO format supports defining one or more plural forms for the same translation. 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 ```go
import "github.com/leonelquinteros/gotext" import "github.com/leonelquinteros/gotext"
@@ -263,6 +268,12 @@ import "github.com/leonelquinteros/gotext"
func main() { func main() {
// Set PO content // Set PO content
str := ` str := `
msgid ""
msgstr ""
# Header below
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "Translate this" msgid "Translate this"
msgstr "Translated text" msgstr "Translated text"
@@ -273,15 +284,14 @@ msgid "One with var: %s"
msgid_plural "Several with vars: %s" msgid_plural "Several with vars: %s"
msgstr[0] "This one is the singular: %s" msgstr[0] "This one is the singular: %s"
msgstr[1] "This one is the plural: %s" msgstr[1] "This one is the plural: %s"
msgstr[2] "And this is the second plural form: %s"
` `
// Create Po object // Create Po object
po := new(Po) po := new(Po)
po.Parse(str) po.Parse(str)
println(po.GetN("One with var: %s", "Several with vars: %s", 2, v)) println(po.GetN("One with var: %s", "Several with vars: %s", 54, v))
// "And this is the second plural form: Variable" // "This one is the plural: Variable"
} }
``` ```

View File

@@ -100,8 +100,7 @@ func Get(str string, vars ...interface{}) string {
return GetD(domain, str, vars...) return GetD(domain, str, vars...)
} }
// GetN retrieves the (N)th plural form translation for the given string in the "default" domain. // GetN retrieves the (N)th plural form of 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.
// 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 GetN(str, plural string, n int, vars ...interface{}) string { func GetN(str, plural string, n int, vars ...interface{}) string {
return GetND("default", str, plural, n, vars...) 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. // 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. // 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 { 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. // 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 { func GetND(dom, str, plural string, n int, vars ...interface{}) string {
// Try to load default package Locale storage // 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...) 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. // GetNC retrieves the (N)th plural form of 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.
// 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 GetNC(str, plural string, n int, ctx string, vars ...interface{}) string { func GetNC(str, plural string, n int, ctx string, vars ...interface{}) string {
return GetNDC("default", str, plural, n, ctx, vars...) 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. // 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. // 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 { 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. // 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 { func GetNDC(dom, str, plural string, n int, ctx string, vars ...interface{}) string {
// Try to load default package Locale storage // Try to load default package Locale storage

View File

@@ -31,7 +31,17 @@ func TestGettersSetters(t *testing.T) {
func TestPackageFunctions(t *testing.T) { func TestPackageFunctions(t *testing.T) {
// Set PO content // 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" msgid "My text"
msgstr "Translated text" msgstr "Translated text"
@@ -109,8 +119,8 @@ msgstr "More translation"
// Test plural // Test plural
tr = GetN("One with var: %s", "Several with vars: %s", 2, v) tr = GetN("One with var: %s", "Several with vars: %s", 2, v)
if tr != "And this is the second plural form: Variable" { if tr != "This one is the plural: Variable" {
t.Errorf("Expected 'And this is the second plural form: Variable' but got '%s'", tr) t.Errorf("Expected 'This one is the plural: Variable' but got '%s'", tr)
} }
// Test context translations // 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) 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" { 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) t.Errorf("Expected 'This one is the plural in a Ctx context: Variable' but got '%s'", tr)
} }

View File

@@ -107,8 +107,7 @@ func (l *Locale) Get(str string, vars ...interface{}) string {
return l.GetD("default", str, vars...) return l.GetD("default", str, vars...)
} }
// GetN retrieves the (N)th plural form translation for the given string in the "default" domain. // GetN retrieves the (N)th plural form of 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.
// 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) GetN(str, plural string, n int, vars ...interface{}) string { func (l *Locale) GetN(str, plural string, n int, vars ...interface{}) string {
return l.GetND("default", str, plural, n, vars...) 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. // 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. // 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 { 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. // GetND retrieves the (N)th plural form of 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.
// 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 // Sync read
@@ -146,8 +144,7 @@ func (l *Locale) GetC(str, ctx string, vars ...interface{}) string {
return l.GetDC("default", str, ctx, vars...) 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. // GetNC retrieves the (N)th plural form of 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.
// 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) GetNC(str, plural string, n int, ctx string, vars ...interface{}) string { func (l *Locale) GetNC(str, plural string, n int, ctx string, vars ...interface{}) string {
return l.GetNDC("default", str, plural, n, ctx, vars...) 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. // 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. // 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 { 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. // GetNDC retrieves the (N)th plural form of 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.
// 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) GetNDC(dom, str, plural string, n int, ctx string, vars ...interface{}) string { func (l *Locale) GetNDC(dom, str, plural string, n int, ctx string, vars ...interface{}) string {
// Sync read // Sync read

View File

@@ -8,7 +8,17 @@ import (
func TestLocale(t *testing.T) { func TestLocale(t *testing.T) {
// Set PO content // 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" msgid "My text"
msgstr "Translated text" msgstr "Translated text"
@@ -91,9 +101,9 @@ msgstr "More translation"
} }
// Test plural // Test plural
tr = l.GetND("my_domain", "One with var: %s", "Several with vars: %s", 2, v) tr = l.GetND("my_domain", "One with var: %s", "Several with vars: %s", 7, v)
if tr != "And this is the second plural form: Variable" { if tr != "This one is the plural: Variable" {
t.Errorf("Expected 'And this is the second plural form: Variable' but got '%s'", tr) t.Errorf("Expected 'This one is the plural: Variable' but got '%s'", tr)
} }
// Test non-existent "deafult" domain responses // Test non-existent "deafult" domain responses
@@ -137,7 +147,7 @@ msgstr "More translation"
} }
// Test plural // 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" { 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) t.Errorf("Expected 'This one is the plural in a Ctx context: Test' but got '%s'", tr)
} }

138
po.go
View File

@@ -1,8 +1,11 @@
package gotext package gotext
import ( import (
"bufio"
"fmt" "fmt"
"github.com/mattn/anko/vm"
"io/ioutil" "io/ioutil"
"net/textproto"
"os" "os"
"strconv" "strconv"
"strings" "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. 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. 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 write locking. And it's safe for concurrent use by multiple goroutines by using the sync package for locking.
Example: Example:
@@ -64,6 +67,19 @@ Example:
*/ */
type Po struct { 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 // Storage
translations map[string]*translation translations map[string]*translation
contexts map[string]map[string]*translation contexts map[string]map[string]*translation
@@ -123,7 +139,7 @@ func (po *Po) Parse(str string) {
} }
// Skip invalid lines // 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 continue
} }
@@ -198,21 +214,21 @@ func (po *Po) Parse(str string) {
// Check for indexed translation forms // Check for indexed translation forms
if strings.HasPrefix(l, "[") { if strings.HasPrefix(l, "[") {
in := strings.Index(l, "]") idx := strings.Index(l, "]")
if in == -1 { if idx == -1 {
// Skip wrong index formatting // Skip wrong index formatting
continue continue
} }
// Parse index // Parse index
i, err := strconv.Atoi(l[1:in]) i, err := strconv.Atoi(l[1:idx])
if err != nil { if err != nil {
// Skip wrong index formatting // Skip wrong index formatting
continue continue
} }
// Parse translation string // Parse translation string
tr.trs[i], _ = strconv.Unquote(strings.TrimSpace(l[in+1:])) tr.trs[i], _ = strconv.Unquote(strings.TrimSpace(l[idx+1:]))
// Loop // Loop
continue continue
@@ -220,6 +236,31 @@ func (po *Po) Parse(str string) {
// Save single translation form under 0 index // Save single translation form under 0 index
tr.trs[0], _ = strconv.Unquote(l) 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() 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. // 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...) return fmt.Sprintf(str, vars...)
} }
// GetN retrieves the (N)th plural form translation for the given string. // GetN retrieves the (N)th plural form of translation for the given string.
// 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 // Sync read
@@ -266,7 +379,7 @@ func (po *Po) GetN(str, plural string, n int, vars ...interface{}) string {
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(po.pluralForm(n)), vars...)
} }
} }
@@ -295,8 +408,7 @@ func (po *Po) GetC(str, ctx string, vars ...interface{}) string {
return fmt.Sprintf(str, vars...) return fmt.Sprintf(str, vars...)
} }
// GetNC retrieves the (N)th plural form 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.
// 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) GetNC(str, plural string, n int, ctx string, vars ...interface{}) string { func (po *Po) GetNC(str, plural string, n int, ctx string, vars ...interface{}) string {
// Sync read // 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 _, ok := po.contexts[ctx]; ok {
if po.contexts[ctx] != nil { if po.contexts[ctx] != nil {
if _, ok := po.contexts[ctx][str]; ok { 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...)
} }
} }
} }

View File

@@ -8,7 +8,17 @@ import (
func TestPo(t *testing.T) { func TestPo(t *testing.T) {
// Set PO content // 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" msgid "My text"
msgstr "Translated text" msgstr "Translated text"
@@ -16,6 +26,11 @@ msgstr "Translated text"
msgid "Another string" msgid "Another string"
msgstr "" msgstr ""
#Multi-line string
msgid "Multi-line"
msgstr "Multi "
"line"
msgid "One with var: %s" msgid "One with var: %s"
msgid_plural "Several with vars: %s" msgid_plural "Several with vars: %s"
msgstr[0] "This one is the singular: %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) 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 // Test plural
tr = po.GetN("One with var: %s", "Several with vars: %s", 2, v) tr = po.GetN("One with var: %s", "Several with vars: %s", 2, v)
if tr != "And this is the second plural form: Variable" { if tr != "This one is the plural: Variable" {
t.Errorf("Expected 'And this is the second plural form: Variable' but got '%s'", tr) t.Errorf("Expected 'This one is the plural: Variable' but got '%s'", tr)
} }
// Test inexistent translations // Test inexistent translations
@@ -94,7 +115,7 @@ msgstr "More translation"
t.Errorf("Expected 'This is a test' but got '%s'", tr) 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" { if tr != "This are tests" {
t.Errorf("Expected 'This are tests' but got '%s'", tr) t.Errorf("Expected 'This are tests' but got '%s'", tr)
} }
@@ -105,7 +126,7 @@ msgstr "More translation"
t.Errorf("Expected '' but got '%s'", tr) 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" { if tr != "Plural index" {
t.Errorf("Expected 'Plural index' but got '%s'", tr) t.Errorf("Expected 'Plural index' but got '%s'", tr)
} }
@@ -118,7 +139,7 @@ msgstr "More translation"
} }
// Test plural // 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" { 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) 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) { func TestTranslationObject(t *testing.T) {
tr := newTranslation() tr := newTranslation()
str := tr.get() str := tr.get()