Detect translations in high-level structs

This patch adds .IsTranslated(), .IsTranslatedN() and related functions
to following objects:
- Po
- Mo
- locale
- gotext
and it creates helper interfaces in introspector.go.

This makes it possible to detect whether a string is translatable or not
during runtime.

Resolves #42
This commit is contained in:
Matyas Horky 2023-04-04 17:22:21 +02:00
parent 8941f8bb1c
commit 3a68971094
6 changed files with 192 additions and 0 deletions

View file

@ -245,3 +245,57 @@ func GetNDC(dom, str, plural string, n int, ctx string, vars ...interface{}) str
return tr
}
// IsTranslated reports whether a string is translated
func IsTranslated(str string) bool {
return IsTranslatedND(GetDomain(), str, 0)
}
// IsTranslatedN reports whether a plural string is translated
func IsTranslatedN(str string, n int) bool {
return IsTranslatedND(GetDomain(), str, n)
}
// IsTranslatedD reports whether a domain string is translated
func IsTranslatedD(dom, str string) bool {
return IsTranslatedND(dom, str, 0)
}
// IsTranslatedND reports whether a plural domain string is translated
func IsTranslatedND(dom, str string, n int) bool {
loadStorage(false)
globalConfig.RLock()
defer globalConfig.RUnlock()
if _, ok := globalConfig.storage.Domains[dom]; !ok {
globalConfig.storage.AddDomain(dom)
}
return globalConfig.storage.IsTranslatedND(dom, str, n)
}
// IsTranslatedC reports whether a context string is translated
func IsTranslatedC(str, ctx string) bool {
return IsTranslatedNDC(GetDomain(), str, 0, ctx)
}
// IsTranslatedNC reports whether a plural context string is translated
func IsTranslatedNC(str string, n int, ctx string) bool {
return IsTranslatedNDC(GetDomain(), str, n, ctx)
}
// IsTranslatedDC reports whether a domain context string is translated
func IsTranslatedDC(dom, str, ctx string) bool {
return IsTranslatedNDC(dom, str, 0, ctx)
}
// IsTranslatedNDC reports whether a plural domain context string is translated
func IsTranslatedNDC(dom, str string, n int, ctx string) bool {
loadStorage(false)
globalConfig.RLock()
defer globalConfig.RUnlock()
return globalConfig.storage.IsTranslatedNDC(dom, str, n, ctx)
}

View file

@ -176,6 +176,33 @@ msgstr "Another text on another domain"
if tr != "Another text on another domain" {
t.Errorf("Expected 'Another text on another domain' but got '%s'", tr)
}
// Test IsTranslation functions
if !IsTranslated("My text") {
t.Error("'My text' should be reported as translated.")
}
if IsTranslated("Another string") {
t.Error("'Another string' should be reported as not translated.")
}
plural := "One with var: %s"
if !IsTranslated(plural) {
t.Errorf("'%s' should be reported as translated for singular.", plural)
}
if !IsTranslatedN(plural, 0) {
t.Errorf("'%s' should be reported as translated for n=0.", plural)
}
if !IsTranslatedN(plural, 2) {
t.Errorf("'%s' should be reported as translated for n=2.", plural)
}
if !IsTranslatedC("Some random in a context", "Ctx") {
t.Errorf("'Some random in a context' should be reported as translated under context.")
}
if !IsTranslatedC(plural, "Ctx") {
t.Errorf("'%s' should be reported as translated for singular under context.", plural)
}
if !IsTranslatedNC(plural, 0, "Ctx") {
t.Errorf("'%s' should be reported as translated for n=0 under context.", plural)
}
}
func TestUntranslated(t *testing.T) {

25
introspector.go Normal file
View file

@ -0,0 +1,25 @@
package gotext
// IsTranslatedIntrospector is able to determine whether a given string is translated.
// Examples of this introspector are Po and Mo, which are specific to their domain.
// Locale holds multiple domains and also implements IsTranslatedDomainIntrospector.
type IsTranslatedIntrospector interface {
IsTranslated(str string) bool
IsTranslatedN(str string, n int) bool
IsTranslatedC(str, ctx string) bool
IsTranslatedNC(str string, n int, ctx string) bool
}
// IsTranslatedDomainIntrospector is able to determine whether a given string is translated.
// Example of this introspector is Locale, which holds multiple domains.
// Simpler objects that are domain-specific, like Po or Mo, implement IsTranslatedIntrospector.
type IsTranslatedDomainIntrospector interface {
IsTranslated(str string) bool
IsTranslatedN(str string, n int) bool
IsTranslatedD(dom, str string) bool
IsTranslatedND(dom, str string, n int) bool
IsTranslatedC(str, ctx string) bool
IsTranslatedNC(str string, n int, ctx string) bool
IsTranslatedDC(dom, str, ctx string) bool
IsTranslatedNDC(dom, str string, n int, ctx string) bool
}

View file

@ -311,6 +311,66 @@ func (l *Locale) GetTranslations() map[string]*Translation {
return all
}
// IsTranslated reports whether a string is translated
func (l *Locale) IsTranslated(str string) bool {
return l.IsTranslatedND(l.GetDomain(), str, 0)
}
// IsTranslatedN reports whether a plural string is translated
func (l *Locale) IsTranslatedN(str string, n int) bool {
return l.IsTranslatedND(l.GetDomain(), str, n)
}
// IsTranslatedD reports whether a domain string is translated
func (l *Locale) IsTranslatedD(dom, str string) bool {
return l.IsTranslatedND(dom, str, 0)
}
// IsTranslatedND reports whether a plural domain string is translated
func (l *Locale) IsTranslatedND(dom, str string, n int) bool {
l.RLock()
defer l.RUnlock()
if l.Domains == nil {
return false
}
translator, ok := l.Domains[dom]
if !ok {
return false
}
return translator.GetDomain().IsTranslatedN(str, n)
}
// IsTranslatedC reports whether a context string is translated
func (l *Locale) IsTranslatedC(str, ctx string) bool {
return l.IsTranslatedNDC(l.GetDomain(), str, 0, ctx)
}
// IsTranslatedNC reports whether a plural context string is translated
func (l *Locale) IsTranslatedNC(str string, n int, ctx string) bool {
return l.IsTranslatedNDC(l.GetDomain(), str, n, ctx)
}
// IsTranslatedDC reports whether a domain context string is translated
func (l *Locale) IsTranslatedDC(dom, str, ctx string) bool {
return l.IsTranslatedNDC(dom, str, 0, ctx)
}
// IsTranslatedNDC reports whether a plural domain context string is translated
func (l *Locale) IsTranslatedNDC(dom string, str string, n int, ctx string) bool {
l.RLock()
defer l.RUnlock()
if l.Domains == nil {
return false
}
translator, ok := l.Domains[dom]
if !ok {
return false
}
return translator.GetDomain().IsTranslatedNC(str, n, ctx)
}
// LocaleEncoding is used as intermediary storage to encode Locale objects to Gob.
type LocaleEncoding struct {
Path string

13
mo.go
View file

@ -92,6 +92,19 @@ func (mo *Mo) GetNC(str, plural string, n int, ctx string, vars ...interface{})
return mo.domain.GetNC(str, plural, n, ctx, vars...)
}
func (mo *Mo) IsTranslated(str string) bool {
return mo.domain.IsTranslated(str)
}
func (mo *Mo) IsTranslatedN(str string, n int) bool {
return mo.domain.IsTranslatedN(str, n)
}
func (mo *Mo) IsTranslatedC(str, ctx string) bool {
return mo.domain.IsTranslatedC(str, ctx)
}
func (mo *Mo) IsTranslatedNC(str string, n int, ctx string) bool {
return mo.domain.IsTranslatedNC(str, n, ctx)
}
func (mo *Mo) MarshalBinary() ([]byte, error) {
return mo.domain.MarshalBinary()
}

13
po.go
View file

@ -114,6 +114,19 @@ func (po *Po) GetNC(str, plural string, n int, ctx string, vars ...interface{})
return po.domain.GetNC(str, plural, n, ctx, vars...)
}
func (po *Po) IsTranslated(str string) bool {
return po.domain.IsTranslated(str)
}
func (po *Po) IsTranslatedN(str string, n int) bool {
return po.domain.IsTranslatedN(str, n)
}
func (po *Po) IsTranslatedC(str, ctx string) bool {
return po.domain.IsTranslatedC(str, ctx)
}
func (po *Po) IsTranslatedNC(str string, n int, ctx string) bool {
return po.domain.IsTranslatedNC(str, n, ctx)
}
func (po *Po) MarshalText() ([]byte, error) {
return po.domain.MarshalText()
}