Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix IsTranslated* functions using n directly rather than using the nth plural form #95

Merged
merged 3 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 91 additions & 4 deletions domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ type Domain struct {
trBuffer *Translation
ctxBuffer string
refBuffer string

customPluralResolver func(int) int
}

// Preserve MIMEHeader behaviour, without the canonicalisation
Expand Down Expand Up @@ -88,13 +90,21 @@ func NewDomain() *Domain {
return domain
}

func (do *Domain) SetPluralResolver(f func(int) int) {
do.customPluralResolver = f
}

func (do *Domain) pluralForm(n int) int {
// do we really need locking here? not sure how this plurals.Expression works, so sticking with it for now
do.pluralMutex.RLock()
defer do.pluralMutex.RUnlock()

// Failure fallback
if do.pluralforms == nil {
if do.customPluralResolver != nil {
return do.customPluralResolver(n)
}

/* Use the Germanic plural rule. */
if n == 1 {
return 0
Expand Down Expand Up @@ -261,6 +271,21 @@ func (do *Domain) Get(str string, vars ...interface{}) string {
return Printf(str, vars...)
}

func (do *Domain) Append(b []byte, str string, vars ...interface{}) []byte {
// Sync read
do.trMutex.RLock()
defer do.trMutex.RUnlock()

if do.translations != nil {
if _, ok := do.translations[str]; ok {
return Appendf(b, do.translations[str].Get(), vars...)
}
}

// Return the same we received by default
return Appendf(b, str, vars...)
}

// Set the (N)th plural form for the given string
func (do *Domain) SetN(id, plural string, n int, str string) {
// Get plural form _before_ lock down
Expand Down Expand Up @@ -302,6 +327,26 @@ func (do *Domain) GetN(str, plural string, n int, vars ...interface{}) string {
return Printf(plural, vars...)
}

// 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 (do *Domain) AppendN(b []byte, str, plural string, n int, vars ...interface{}) []byte {
// Sync read
do.trMutex.RLock()
defer do.trMutex.RUnlock()

if do.translations != nil {
if _, ok := do.translations[str]; ok {
return Appendf(b, do.translations[str].GetN(do.pluralForm(n)), vars...)
}
}

// Parse plural forms to distinguish between plural and singular
if do.pluralForm(n) == 0 {
return Appendf(b, str, vars...)
}
return Appendf(b, plural, vars...)
}

// Set the translation for the given string in the given context
func (do *Domain) SetC(id, ctx, str string) {
do.trMutex.Lock()
Expand Down Expand Up @@ -348,6 +393,26 @@ func (do *Domain) GetC(str, ctx string, vars ...interface{}) string {
return Printf(str, vars...)
}

// AppendC retrieves the corresponding Translation for a given string in the given context.
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
func (do *Domain) AppendC(b []byte, str, ctx string, vars ...interface{}) []byte {
do.trMutex.RLock()
defer do.trMutex.RUnlock()

if do.contextTranslations != nil {
if _, ok := do.contextTranslations[ctx]; ok {
if do.contextTranslations[ctx] != nil {
if _, ok := do.contextTranslations[ctx][str]; ok {
return Appendf(b, do.contextTranslations[ctx][str].Get(), vars...)
}
}
}
}

// Return the string we received by default
return Appendf(b, str, vars...)
}

// Set the (N)th plural form for the given string in the given context
func (do *Domain) SetNC(id, plural, ctx string, n int, str string) {
// Get plural form _before_ lock down
Expand Down Expand Up @@ -399,9 +464,31 @@ func (do *Domain) GetNC(str, plural string, n int, ctx string, vars ...interface
return Printf(plural, vars...)
}

// AppendNC 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 (do *Domain) AppendNC(b []byte, str, plural string, n int, ctx string, vars ...interface{}) []byte {
do.trMutex.RLock()
defer do.trMutex.RUnlock()

if do.contextTranslations != nil {
if _, ok := do.contextTranslations[ctx]; ok {
if do.contextTranslations[ctx] != nil {
if _, ok := do.contextTranslations[ctx][str]; ok {
return Appendf(b, do.contextTranslations[ctx][str].GetN(do.pluralForm(n)), vars...)
}
}
}
}

if n == 1 {
return Appendf(b, str, vars...)
}
return Appendf(b, plural, vars...)
}

// IsTranslated reports whether a string is translated
func (do *Domain) IsTranslated(str string) bool {
return do.IsTranslatedN(str, 0)
return do.IsTranslatedN(str, 1)
}

// IsTranslatedN reports whether a plural string is translated
Expand All @@ -416,12 +503,12 @@ func (do *Domain) IsTranslatedN(str string, n int) bool {
if !ok {
return false
}
return tr.IsTranslatedN(n)
return tr.IsTranslatedN(do.pluralForm(n))
}

// IsTranslatedC reports whether a context string is translated
func (do *Domain) IsTranslatedC(str, ctx string) bool {
return do.IsTranslatedNC(str, 0, ctx)
return do.IsTranslatedNC(str, 1, ctx)
}

// IsTranslatedNC reports whether a plural context string is translated
Expand All @@ -440,7 +527,7 @@ func (do *Domain) IsTranslatedNC(str string, n int, ctx string) bool {
if !ok {
return false
}
return tr.IsTranslatedN(n)
return tr.IsTranslatedN(do.pluralForm(n))
}

// GetTranslations returns a copy of every translation in the domain. It does not support contexts.
Expand Down
19 changes: 8 additions & 11 deletions domain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const (
arFixture = "fixtures/ar/categories.po"
)

//since both Po and Mo just pass-through to Domain for MarshalBinary and UnmarshalBinary, test it here
// since both Po and Mo just pass-through to Domain for MarshalBinary and UnmarshalBinary, test it here
func TestBinaryEncoding(t *testing.T) {
// Create po objects
po := NewPo()
Expand Down Expand Up @@ -85,11 +85,11 @@ func TestDomain_IsTranslated(t *testing.T) {
if english.IsTranslated("Another string") {
t.Error("'Another string' should be reported as not translated.")
}
if !english.IsTranslatedN("Empty plural form singular", 0) {
t.Error("'Empty plural form singular' should be reported as translated for n=0.")
if !english.IsTranslatedN("Empty plural form singular", 1) {
t.Error("'Empty plural form singular' should be reported as translated for n=1.")
}
if english.IsTranslatedN("Empty plural form singular", 1) {
t.Error("'Empty plural form singular' should be reported as not translated for n=1.")
if english.IsTranslatedN("Empty plural form singular", 0) {
t.Error("'Empty plural form singular' should be reported as not translated for n=0.")
}

arabicPo := NewPo()
Expand All @@ -106,11 +106,8 @@ func TestDomain_IsTranslated(t *testing.T) {
if !arabic.IsTranslatedN("Load %d more document", 1) {
t.Error("Arabic plural should be reported as translated for n=1.")
}
if !arabic.IsTranslatedN("Load %d more document", 5) {
t.Error("Arabic plural should be reported as translated for n=5.")
}
if arabic.IsTranslatedN("Load %d more document", 6) {
t.Error("Arabic plural should be reported as not translated for n=6.")
if !arabic.IsTranslatedN("Load %d more document", 100) {
t.Error("Arabic plural should be reported as translated for n=100.")
}

// context
Expand All @@ -120,7 +117,7 @@ func TestDomain_IsTranslated(t *testing.T) {
if !english.IsTranslatedNC("One with var: %s", 0, "Ctx") {
t.Error("Context plural should be reported as translated for n=0")
}
if english.IsTranslatedNC("One with var: %s", 2, "Ctx") {
if !english.IsTranslatedNC("One with var: %s", 2, "Ctx") {
t.Error("Context plural should be reported as translated for n=2")
}
}
Expand Down
31 changes: 15 additions & 16 deletions gotext.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,21 @@ Package gotext implements GNU gettext utilities.

For quick/simple translations you can use the package level functions directly.

import (
"fmt"
"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")
func main() {
// Configure package
gotext.Configure("/path/to/locales/root/dir", "en_UK", "domain-name")

// Translate text from default domain
fmt.Println(gotext.Get("My text on 'domain-name' domain"))

// Translate text from a different domain without reconfigure
fmt.Println(gotext.GetD("domain2", "Another text on a different domain"))
}
// Translate text from default domain
fmt.Println(gotext.Get("My text on 'domain-name' domain"))

// Translate text from a different domain without reconfigure
fmt.Println(gotext.GetD("domain2", "Another text on a different domain"))
}
*/
package gotext

Expand Down Expand Up @@ -342,7 +341,7 @@ func GetNDC(dom, str, plural string, n int, ctx string, vars ...interface{}) str
// IsTranslated reports whether a string is translated in given languages.
// When the langs argument is omitted, the output of GetLanguages is used.
func IsTranslated(str string, langs ...string) bool {
return IsTranslatedND(GetDomain(), str, 0, langs...)
return IsTranslatedND(GetDomain(), str, 1, langs...)
}

// IsTranslatedN reports whether a plural string is translated in given languages.
Expand All @@ -354,7 +353,7 @@ func IsTranslatedN(str string, n int, langs ...string) bool {
// IsTranslatedD reports whether a domain string is translated in given languages.
// When the langs argument is omitted, the output of GetLanguages is used.
func IsTranslatedD(dom, str string, langs ...string) bool {
return IsTranslatedND(dom, str, 0, langs...)
return IsTranslatedND(dom, str, 1, langs...)
}

// IsTranslatedND reports whether a plural domain string is translated in any of given languages.
Expand Down Expand Up @@ -385,7 +384,7 @@ func IsTranslatedND(dom, str string, n int, langs ...string) bool {
// IsTranslatedC reports whether a context string is translated in given languages.
// When the langs argument is omitted, the output of GetLanguages is used.
func IsTranslatedC(str, ctx string, langs ...string) bool {
return IsTranslatedNDC(GetDomain(), str, 0, ctx, langs...)
return IsTranslatedNDC(GetDomain(), str, 1, ctx, langs...)
}

// IsTranslatedNC reports whether a plural context string is translated in given languages.
Expand Down
113 changes: 112 additions & 1 deletion gotext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ func TestPackageArabicTranslation(t *testing.T) {
t.Errorf("Expected to get '%%d selected', but got '%s'", tr)
}

//Plurals formula present + Plural translation string present and complete
// Plurals formula present + Plural translation string present and complete
tr = GetND("categories", "Load %d more document", "Load %d more documents", 0)
if tr != "حمّل %d مستندات إضافيّة" {
t.Errorf("Expected to get 'msgstr[0]', but got '%s'", tr)
Expand Down Expand Up @@ -596,3 +596,114 @@ func TestPackageArabicMissingPluralForm(t *testing.T) {
t.Errorf("Expected to get 'الكحول والتبغ', but got '%s'", tr)
}
}

func BenchmarkGoText(b *testing.B) {

l := NewLocale("fixtures", "de_DE")
l.AddDomain("default")
l.SetDomain("default")
d := l.Domains["default"].(AppendTranslator)

b.ResetTimer()

b.Run("strings", func(b *testing.B) {

for i := 0; i < b.N; i++ {
tr := d.Get("My text")
if tr != "Translated text" {
b.Errorf("Expected to get 'Translated text', but got '%s'", tr)
}

tr = d.Get("Some random")
if tr != "Some random translation" {
b.Errorf("Expected to get 'Some random translation', but got '%s'", tr)
}

tr = d.Get("More")
if tr != "More translation" {
b.Errorf("Expected to get 'More translation', but got '%s'", tr)
}

tr = d.Get("Multi-line")
if tr != "Multi \nline" {
b.Errorf("Expected to get 'Multi \nline', but got '%s'", tr)
}

tr = d.Get("Another string")
if tr != "Another string" {
b.Errorf("Expected to get 'Another string', but got '%s'", tr)
}

tr = d.GetN("One with var: %s", "Several with vars: %s", 1, "1")
if tr != "This one is the singular: 1" {
b.Errorf("Expected to get 'This one is the singular: 1', but got '%s'", tr)
}

tr = d.GetN("One with var: %s", "Several with vars: %s", 2, "2")
if tr != "This one is the plural: 2" {
b.Errorf("Expected to get 'This one is the plural: 2', but got '%s'", tr)
}

tr = d.GetN("Empty plural form singular", "Empty plural form", 1)
if tr != "Singular translated" {
b.Errorf("Expected to get 'Singular translated', but got '%s'", tr)
}

tr = d.GetNC("One with var: %s", "Several with vars: %s", 1, "Ctx", "1")
if tr != "This one is the singular in a Ctx context: 1" {
b.Errorf("Expected to get This one is the singular in a Ctx context: 1', but got '%s'", tr)
}
}
})

p := make([]byte, 256)
b.Run("byte-slices", func(b *testing.B) {
for i := 0; i < b.N; i++ {
tr := d.Append(p[:0], "My text")
if string(tr) != "Translated text" {
b.Errorf("Expected to get 'Translated text', but got '%s'", tr)
}

tr = d.Append(p[:0], "Some random")
if string(tr) != "Some random translation" {
b.Errorf("Expected to get 'Some random translation', but got '%s'", tr)
}

tr = d.Append(p[:0], "More")
if string(tr) != "More translation" {
b.Errorf("Expected to get 'More translation', but got '%s'", tr)
}

tr = d.Append(p[:0], "Multi-line")
if string(tr) != "Multi \nline" {
b.Errorf("Expected to get 'Multi \nline', but got '%s'", tr)
}

tr = d.Append(p[:0], "Another string")
if string(tr) != "Another string" {
b.Errorf("Expected to get 'Another string', but got '%s'", tr)
}

tr = d.AppendN(p[:0], "One with var: %s", "Several with vars: %s", 1, "1")
if string(tr) != "This one is the singular: 1" {
b.Errorf("Expected to get 'This one is the singular: 1', but got '%s'", tr)
}

tr = d.AppendN(p[:0], "One with var: %s", "Several with vars: %s", 2, "2")
if string(tr) != "This one is the plural: 2" {
b.Errorf("Expected to get 'This one is the plural: 2', but got '%s'", tr)
}

tr = d.AppendN(p[:0], "Empty plural form singular", "Empty plural form", 1)
if string(tr) != "Singular translated" {
b.Errorf("Expected to get 'Singular translated', but got '%s'", tr)
}

tr = d.AppendNC(p[:0], "One with var: %s", "Several with vars: %s", 1, "Ctx", "1")
if string(tr) != "This one is the singular in a Ctx context: 1" {
b.Errorf("Expected to get This one is the singular in a Ctx context: 1', but got '%s'", tr)
}
}
})

}
Loading