Add support for plural forms
This commit is contained in:
17
gotext.go
17
gotext.go
@@ -79,15 +79,30 @@ func Configure(lib, lang, dom string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get uses the default domain globally set to return the corresponding translation of a given string.
|
// Get uses the default domain globally set to return the corresponding translation of a given string.
|
||||||
|
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||||
func Get(str string, vars ...interface{}) string {
|
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.
|
||||||
|
// 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.
|
||||||
|
func GetN(str, plural string, n int, vars ...interface{}) string {
|
||||||
|
return GetND("default", str, plural, n, vars...)
|
||||||
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
func GetD(dom, str string, vars ...interface{}) string {
|
func GetD(dom, str string, vars ...interface{}) string {
|
||||||
|
return GetND(dom, str, str, 0, vars...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetND retrieves the (N)th plural form 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
|
// Try to load default package Locale storage
|
||||||
loadStorage(false)
|
loadStorage(false)
|
||||||
|
|
||||||
// Return translation
|
// Return translation
|
||||||
return storage.GetD(dom, str, vars...)
|
return storage.GetND(dom, str, plural, n, vars...)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ msgid "Another string"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "One with var: %s"
|
msgid "One with var: %s"
|
||||||
msgstr "This one sets the 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"
|
||||||
|
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -50,7 +53,13 @@ msgstr "This one sets the var: %s"
|
|||||||
|
|
||||||
v := "Variable"
|
v := "Variable"
|
||||||
tr = Get("One with var: %s", v)
|
tr = Get("One with var: %s", v)
|
||||||
if tr != "This one sets the var: Variable" {
|
if tr != "This one is the singular: Variable" {
|
||||||
t.Errorf("Expected 'This one sets the var: Variable' but got '%s'", tr)
|
t.Errorf("Expected 'This one is the singular: Variable' but got '%s'", tr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
19
locale.go
19
locale.go
@@ -53,20 +53,35 @@ func (l *Locale) AddDomain(dom string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get uses a domain "default" to return the corresponding translation of a given string.
|
// Get uses a domain "default" to return the corresponding translation of a given string.
|
||||||
|
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||||
func (l *Locale) Get(str string, vars ...interface{}) string {
|
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.
|
||||||
|
// 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.
|
||||||
|
func (l *Locale) GetN(str, plural string, n int, vars ...interface{}) string {
|
||||||
|
return l.GetND("default", str, plural, n, vars...)
|
||||||
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
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...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetND retrieves the (N)th plural form 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 (l *Locale) GetND(dom, str, plural string, n int, vars ...interface{}) string {
|
||||||
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 {
|
||||||
return l.domains[dom].Get(str, vars...)
|
return l.domains[dom].GetN(str, plural, n, vars...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the same we received by default
|
// Return the same we received by default
|
||||||
return fmt.Sprintf(str, vars...)
|
return fmt.Sprintf(plural, vars...)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ msgid "Another string"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "One with var: %s"
|
msgid "One with var: %s"
|
||||||
msgstr "This one sets the 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"
|
||||||
|
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -56,7 +59,13 @@ msgstr "This one sets the var: %s"
|
|||||||
|
|
||||||
v := "Variable"
|
v := "Variable"
|
||||||
tr = l.GetD("my_domain", "One with var: %s", v)
|
tr = l.GetD("my_domain", "One with var: %s", v)
|
||||||
if tr != "This one sets the var: Variable" {
|
if tr != "This one is the singular: Variable" {
|
||||||
t.Errorf("Expected 'This one sets the var: Variable' but got '%s'", tr)
|
t.Errorf("Expected 'This one is the singular: Variable' but got '%s'", tr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
126
po.go
126
po.go
@@ -9,10 +9,44 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type translation struct {
|
||||||
|
id string
|
||||||
|
pluralId string
|
||||||
|
trs map[int]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTranslation() *translation {
|
||||||
|
tr := new(translation)
|
||||||
|
tr.trs = make(map[int]string)
|
||||||
|
|
||||||
|
return tr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *translation) get() string {
|
||||||
|
// Look for translation index 0
|
||||||
|
if _, ok := t.trs[0]; ok {
|
||||||
|
return t.trs[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return unstranlated id by default
|
||||||
|
return t.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *translation) getN(n int) string {
|
||||||
|
// Look for translation index
|
||||||
|
if _, ok := t.trs[n]; ok {
|
||||||
|
return t.trs[n]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return unstranlated plural by default
|
||||||
|
return t.pluralId
|
||||||
|
}
|
||||||
|
|
||||||
|
// Po type handles the content of any PO file.
|
||||||
type Po struct {
|
type Po struct {
|
||||||
// Storage
|
// Storage
|
||||||
translations map[string]string
|
translations map[string]*translation
|
||||||
|
|
||||||
// Sync Mutex
|
// Sync Mutex
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
@@ -41,16 +75,16 @@ func (po *Po) ParseFile(f string) {
|
|||||||
|
|
||||||
// Parse loads the translations specified in the provided string (str)
|
// Parse loads the translations specified in the provided string (str)
|
||||||
func (po *Po) Parse(str string) {
|
func (po *Po) Parse(str string) {
|
||||||
po.Lock()
|
po.Lock()
|
||||||
defer po.Unlock()
|
defer po.Unlock()
|
||||||
|
|
||||||
if po.translations == nil {
|
if po.translations == nil {
|
||||||
po.translations = make(map[string]string)
|
po.translations = make(map[string]*translation)
|
||||||
}
|
}
|
||||||
|
|
||||||
lines := strings.Split(str, "\n")
|
lines := strings.Split(str, "\n")
|
||||||
|
|
||||||
var msgid, msgstr string
|
tr := newTranslation()
|
||||||
|
|
||||||
for _, l := range lines {
|
for _, l := range lines {
|
||||||
// Trim spaces
|
// Trim spaces
|
||||||
@@ -62,35 +96,93 @@ func (po *Po) Parse(str string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Skip invalid lines
|
// Skip invalid lines
|
||||||
if !strings.HasPrefix(l, "msgid") && !strings.HasPrefix(l, "msgstr") {
|
if !strings.HasPrefix(l, "msgid") && !strings.HasPrefix(l, "msgid_plural") && !strings.HasPrefix(l, "msgstr") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buffer msgid and continue
|
// Buffer msgid and continue
|
||||||
if strings.HasPrefix(l, "msgid") {
|
if strings.HasPrefix(l, "msgid") && !strings.HasPrefix(l, "msgid_plural") {
|
||||||
msgid = strings.TrimSpace(strings.TrimPrefix(l, "msgid"))
|
// Save current translation buffer.
|
||||||
msgid, _ = strconv.Unquote(msgid)
|
po.translations[tr.id] = tr
|
||||||
|
|
||||||
|
// Flush buffer
|
||||||
|
tr = newTranslation()
|
||||||
|
|
||||||
|
// Set id
|
||||||
|
tr.id, _ = strconv.Unquote(strings.TrimSpace(strings.TrimPrefix(l, "msgid")))
|
||||||
|
|
||||||
|
// Loop
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save translation for buffered msgid
|
// Check for plural form
|
||||||
if strings.HasPrefix(l, "msgstr") {
|
if strings.HasPrefix(l, "msgid_plural") {
|
||||||
msgstr = strings.TrimSpace(strings.TrimPrefix(l, "msgstr"))
|
tr.pluralId, _ = strconv.Unquote(strings.TrimSpace(strings.TrimPrefix(l, "msgid_plural")))
|
||||||
msgstr, _ = strconv.Unquote(msgstr)
|
|
||||||
|
|
||||||
po.translations[msgid] = msgstr
|
// Loop
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save translation
|
||||||
|
if strings.HasPrefix(l, "msgstr") {
|
||||||
|
l = strings.TrimSpace(strings.TrimPrefix(l, "msgstr"))
|
||||||
|
|
||||||
|
// Check for indexed translation forms
|
||||||
|
if strings.HasPrefix(l, "[") {
|
||||||
|
in := strings.Index(l, "]")
|
||||||
|
if in == -1 {
|
||||||
|
// Skip wrong index formatting
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse index
|
||||||
|
i, err := strconv.Atoi(l[1:in])
|
||||||
|
if err != nil {
|
||||||
|
// Skip wrong index formatting
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse translation string
|
||||||
|
tr.trs[i], _ = strconv.Unquote(strings.TrimSpace(l[in+1:]))
|
||||||
|
|
||||||
|
// Loop
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save single translation form under 0 index
|
||||||
|
tr.trs[0], _ = strconv.Unquote(l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save last translation buffer.
|
||||||
|
if tr.id != "" {
|
||||||
|
po.translations[tr.id] = tr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
func (po *Po) Get(str string, vars ...interface{}) string {
|
func (po *Po) Get(str string, 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], vars...)
|
return fmt.Sprintf(po.translations[str].get(), vars...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the same we received by default
|
// Return the same we received by default
|
||||||
return fmt.Sprintf(str, vars...)
|
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.
|
||||||
|
// 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 {
|
||||||
|
if po.translations != nil {
|
||||||
|
if _, ok := po.translations[str]; ok {
|
||||||
|
return fmt.Sprintf(po.translations[str].getN(n), vars...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the plural string we received by default
|
||||||
|
return fmt.Sprintf(plural, vars...)
|
||||||
|
}
|
||||||
|
|||||||
15
po_test.go
15
po_test.go
@@ -17,7 +17,10 @@ msgid "Another string"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "One with var: %s"
|
msgid "One with var: %s"
|
||||||
msgstr "This one sets the 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"
|
||||||
|
|
||||||
`
|
`
|
||||||
// Write PO content to file
|
// Write PO content to file
|
||||||
@@ -46,7 +49,13 @@ msgstr "This one sets the var: %s"
|
|||||||
|
|
||||||
v := "Variable"
|
v := "Variable"
|
||||||
tr = po.Get("One with var: %s", v)
|
tr = po.Get("One with var: %s", v)
|
||||||
if tr != "This one sets the var: Variable" {
|
if tr != "This one is the singular: Variable" {
|
||||||
t.Errorf("Expected 'This one sets the var: Variable' but got '%s'", tr)
|
t.Errorf("Expected 'This one is the singular: Variable' 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user