From 3a689710948709ff4bf401717db24f78f02b3b13 Mon Sep 17 00:00:00 2001 From: Matyas Horky Date: Tue, 4 Apr 2023 17:22:21 +0200 Subject: [PATCH] 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 --- gotext.go | 54 ++++++++++++++++++++++++++++++++++++++++++++ gotext_test.go | 27 ++++++++++++++++++++++ introspector.go | 25 +++++++++++++++++++++ locale.go | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ mo.go | 13 +++++++++++ po.go | 13 +++++++++++ 6 files changed, 192 insertions(+) create mode 100644 introspector.go diff --git a/gotext.go b/gotext.go index fbfc17d..95c7b11 100644 --- a/gotext.go +++ b/gotext.go @@ -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) +} diff --git a/gotext_test.go b/gotext_test.go index 31ece80..37d8c7a 100644 --- a/gotext_test.go +++ b/gotext_test.go @@ -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) { diff --git a/introspector.go b/introspector.go new file mode 100644 index 0000000..5305dbf --- /dev/null +++ b/introspector.go @@ -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 +} diff --git a/locale.go b/locale.go index d7202bb..455e71f 100644 --- a/locale.go +++ b/locale.go @@ -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 diff --git a/mo.go b/mo.go index 3c88b3f..e80998d 100644 --- a/mo.go +++ b/mo.go @@ -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() } diff --git a/po.go b/po.go index 612f27c..4fe146e 100644 --- a/po.go +++ b/po.go @@ -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() }