From 75a1fb9b04fe48df6afeb07a06fdba011fa7c3af Mon Sep 17 00:00:00 2001 From: Ben Sarah Golightly Date: Thu, 23 Jul 2020 00:28:56 +0100 Subject: [PATCH] Add variants of Get functions with existence checks Currently, a translation that doesn't exist just defaults to the passed message ID. It can be helpful to be able to catch these missing cases e.g. to save to a log. This PR implements variants ending in 'E' like GetE, GetNE. This wasn't implemented at the package-level scope, because for quick translations like that the extra flexibility probably isn't needed. --- locale.go | 105 +++++++++++++++++++++++++++++++++++++++++++++++++ mo.go | 84 +++++++++++++++++++++++++++++++++++++++ po.go | 85 +++++++++++++++++++++++++++++++++++++++ po_test.go | 24 +++++++++-- translation.go | 26 ++++++++++++ translator.go | 6 +++ 6 files changed, 326 insertions(+), 4 deletions(-) diff --git a/locale.go b/locale.go index 56f18a3..557e146 100644 --- a/locale.go +++ b/locale.go @@ -271,6 +271,111 @@ func (l *Locale) GetNDC(dom, str, plural string, n int, ctx string, vars ...inte return Printf(plural, vars...) } +// Get Euses 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. +// The second return value is true iff the string was found. +func (l *Locale) GetE(str string, vars ...interface{}) (string, bool) { + return l.GetDE(l.GetDomain(), str, vars...) +} + +// GetNE retrieves the (N)th plural form of Translation for the given string in the "default" domain. +// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. +// The second return value is true iff the string was found. +func (l *Locale) GetNE(str, plural string, n int, vars ...interface{}) (string, bool) { + return l.GetNDE(l.GetDomain(), str, plural, n, vars...) +} + +// GetDE 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. +// The second return value is true iff the string was found. +func (l *Locale) GetDE(dom, str string, vars ...interface{}) (string, bool) { + // Sync read + l.RLock() + defer l.RUnlock() + + if l.Domains != nil { + if _, ok := l.Domains[dom]; ok { + if l.Domains[dom] != nil { + return l.Domains[dom].GetE(str, vars...) + } + } + } + + return "", false +} + +// GetNDE retrieves the (N)th plural form of 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. +// The second return value is true iff the string was found. +func (l *Locale) GetNDE(dom, str, plural string, n int, vars ...interface{}) (string, bool) { + // Sync read + l.RLock() + defer l.RUnlock() + + if l.Domains != nil { + if _, ok := l.Domains[dom]; ok { + if l.Domains[dom] != nil { + return l.Domains[dom].GetNE(str, plural, n, vars...) + } + } + } + + // Use western default rule (plural > 1) to handle missing domain default result. + return "", false +} + +// GetC uses a domain "default" to return the corresponding Translation of the given string in the given context. +// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. +// The second return value is true iff the string was found. +func (l *Locale) GetCE(str, ctx string, vars ...interface{}) (string, bool) { + return l.GetDCE(l.GetDomain(), str, ctx, vars...) +} + +// GetNC retrieves the (N)th plural form of Translation for the given string in the given context in the "default" domain. +// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. +// The second return value is true iff the string was found. +func (l *Locale) GetNCE(str, plural string, n int, ctx string, vars ...interface{}) (string, bool) { + return l.GetNDCE(l.GetDomain(), str, plural, n, ctx, vars...) +} + +// GetDCE 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. +func (l *Locale) GetDCE(dom, str, ctx string, vars ...interface{}) (string, bool) { + // Sync read + l.RLock() + defer l.RUnlock() + + if l.Domains != nil { + if _, ok := l.Domains[dom]; ok { + if l.Domains[dom] != nil { + return l.Domains[dom].GetCE(str, ctx, vars...) + } + } + } + + return "", false +} + +// GetNDCE retrieves the (N)th plural form of 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. +// The second return value is true iff the string was found. +func (l *Locale) GetNDCE(dom, str, plural string, n int, ctx string, vars ...interface{}) (string, bool) { + // Sync read + l.RLock() + defer l.RUnlock() + + if l.Domains != nil { + if _, ok := l.Domains[dom]; ok { + if l.Domains[dom] != nil { + return l.Domains[dom].GetNCE(str, plural, n, ctx, vars...) + } + } + } + + // Use western default rule (plural > 1) to handle missing domain default result. + return "", false +} + // 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 deb21ab..a81901e 100644 --- a/mo.go +++ b/mo.go @@ -427,6 +427,90 @@ func (mo *Mo) GetNC(str, plural string, n int, ctx string, vars ...interface{}) return Printf(plural, vars...) } +// GetE 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. +// The second return value is true iff the string was found. +func (mo *Mo) GetE(str string, vars ...interface{}) (string, bool) { + // Sync read + mo.RLock() + defer mo.RUnlock() + + if mo.translations != nil { + if _, ok := mo.translations[str]; ok { + if fmt, ok := mo.translations[str].GetE(); ok { + return Printf(fmt, vars...), true + } + } + } + + return "", false +} + +// GetNE 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. +// The second return value is true iff the string was found. +func (mo *Mo) GetNE(str, plural string, n int, vars ...interface{}) (string, bool) { + // Sync read + mo.RLock() + defer mo.RUnlock() + + if mo.translations != nil { + if _, ok := mo.translations[str]; ok { + if fmt, ok := mo.translations[str].GetNE(mo.pluralForm(n)); ok { + return Printf(fmt, vars...), true + } + } + } + + return "", false +} + +// GetCE 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. +// The second return value is true iff the string was found. +func (mo *Mo) GetCE(str, ctx string, vars ...interface{}) (string, bool) { + // Sync read + mo.RLock() + defer mo.RUnlock() + + if mo.contexts != nil { + if _, ok := mo.contexts[ctx]; ok { + if mo.contexts[ctx] != nil { + if _, ok := mo.contexts[ctx][str]; ok { + if fmt, ok := mo.contexts[ctx][str].GetE(); ok { + return Printf(fmt, vars...), true + } + } + } + } + } + + return "", false +} + +// GetNCE 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. +// The second return value is true iff the string was found. +func (mo *Mo) GetNCE(str, plural string, n int, ctx string, vars ...interface{}) (string, bool) { + // Sync read + mo.RLock() + defer mo.RUnlock() + + if mo.contexts != nil { + if _, ok := mo.contexts[ctx]; ok { + if mo.contexts[ctx] != nil { + if _, ok := mo.contexts[ctx][str]; ok { + if fmt, ok := mo.contexts[ctx][str].GetNE(mo.pluralForm(n)); ok { + return Printf(fmt, vars...), true + } + } + } + } + } + + return "", false +} + // MarshalBinary implements encoding.BinaryMarshaler interface func (mo *Mo) MarshalBinary() ([]byte, error) { obj := new(TranslatorEncoding) diff --git a/po.go b/po.go index 368672a..ff15f52 100644 --- a/po.go +++ b/po.go @@ -456,6 +456,91 @@ func (po *Po) GetNC(str, plural string, n int, ctx string, vars ...interface{}) return Printf(plural, vars...) } +// Get Eretrieves the corresponding Translation for the given string. +// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. +// The second return value is true iff the string was found. +func (po *Po) GetE(str string, vars ...interface{}) (string, bool) { + // Sync read + po.RLock() + defer po.RUnlock() + + if po.translations != nil { + if _, ok := po.translations[str]; ok { + if fmt, ok := po.translations[str].GetE(); ok { + return Printf(fmt, vars...), true + } + } + } + + return "", false +} + +// GetN Eretrieves 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. +// The second return value is true iff the string was found. +func (po *Po) GetNE(str, plural string, n int, vars ...interface{}) (string, bool) { + // Sync read + po.RLock() + defer po.RUnlock() + + if po.translations != nil { + if _, ok := po.translations[str]; ok { + if fmt, ok := po.translations[str].GetNE(po.pluralForm(n)); ok { + return Printf(fmt, vars...), true + } + } + } + + return "", false +} + +// GetCE 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. +// The second return value is true iff the string was found. +func (po *Po) GetCE(str, ctx string, vars ...interface{}) (string, bool) { + // Sync read + po.RLock() + defer po.RUnlock() + + if po.contexts != nil { + if _, ok := po.contexts[ctx]; ok { + if po.contexts[ctx] != nil { + if _, ok := po.contexts[ctx][str]; ok { + if fmt, ok := po.contexts[ctx][str].GetE(); ok { + return Printf(fmt, vars...), true + } + } + } + } + } + + return "", false +} + +// GetNCE 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. +// The second return value is true iff the string was found. +func (po *Po) GetNCE(str, plural string, n int, ctx string, vars ...interface{}) (string, bool) { + // Sync read + po.RLock() + defer po.RUnlock() + + if po.contexts != nil { + if _, ok := po.contexts[ctx]; ok { + if po.contexts[ctx] != nil { + if _, ok := po.contexts[ctx][str]; ok { + if fmt, ok := po.contexts[ctx][str].GetNE(po.pluralForm(n)); ok { + return Printf(fmt, vars...), true + } + } + } + } + } + + // Parse plural forms to distinguish between plural and singular + return "", false +} + // MarshalBinary implements encoding.BinaryMarshaler interface func (po *Po) MarshalBinary() ([]byte, error) { obj := new(TranslatorEncoding) diff --git a/po_test.go b/po_test.go index 24830f4..1b6d944 100644 --- a/po_test.go +++ b/po_test.go @@ -62,7 +62,7 @@ msgid "" msgstr "id with multiline content" # Multi-line msgid_plural -msgid "" +msgid "" "multi" "line" "plural" @@ -71,7 +71,7 @@ msgstr "plural id with multiline content" #Multi-line string msgid "Multi-line" -msgstr "" +msgstr "" "Multi " "line" @@ -177,6 +177,22 @@ msgstr "More Translation" t.Errorf("Expected 'This are tests' but got '%s'", tr) } + // Test translations with existence check + tr, exists := po.GetE("My text") + if (tr != "Translated text") || (!exists) { + t.Errorf("Expected 'Translated text', true but got '%s', %v", tr, exists) + } + + tr, exists = po.GetE("I don't exist") + if exists { + t.Errorf("Expected 'I don't exist' not to exist but got '%s'", tr) + } + + tr = po.GetN("I don't exist", "We don't exist", 100) + if exists { + t.Errorf("Expected 'I/We don't exist' not to exist but got '%s'", tr) + } + // Test context translations v = "Test" tr = po.GetC("One with var: %s", "Ctx", v) @@ -241,7 +257,7 @@ msgstr[0] "TR Singular: %s" msgstr[1] "TR Plural: %s" msgstr[2] "TR Plural 2: %s" - + ` // Create po object po := new(Po) @@ -271,7 +287,7 @@ msgstr[0] "TR Singular: %s" msgstr[1] "TR Plural: %s" msgstr[2] "TR Plural 2: %s" - + ` // Create po object po := new(Po) diff --git a/translation.go b/translation.go index bc069d4..539dcc1 100644 --- a/translation.go +++ b/translation.go @@ -50,3 +50,29 @@ func (t *Translation) GetN(n int) string { // Return untranslated plural by default return t.PluralID } + +// Get returns the string of the translation. The second return value is true +// iff the string was found. +func (t *Translation) GetE() (string, bool) { + // Look for Translation index 0 + if _, ok := t.Trs[0]; ok { + if t.Trs[0] != "" { + return t.Trs[0], true + } + } + + return "", false +} + +// GetN returns the string of the plural translation. The second return value +// is true iff the string was found. +func (t *Translation) GetNE(n int) (string, bool) { + // Look for Translation index + if _, ok := t.Trs[n]; ok { + if t.Trs[n] != "" { + return t.Trs[n], true + } + } + + return "", false +} diff --git a/translator.go b/translator.go index 982a600..f1b1093 100644 --- a/translator.go +++ b/translator.go @@ -13,11 +13,17 @@ import "net/textproto" type Translator interface { ParseFile(f string) Parse(buf []byte) + Get(str string, vars ...interface{}) string GetN(str, plural string, n int, vars ...interface{}) string GetC(str, ctx string, vars ...interface{}) string GetNC(str, plural string, n int, ctx string, vars ...interface{}) string + GetE(str string, vars ...interface{}) (string, bool) + GetNE(str, plural string, n int, vars ...interface{}) (string, bool) + GetCE(str, ctx string, vars ...interface{}) (string, bool) + GetNCE(str, plural string, n int, ctx string, vars ...interface{}) (string, bool) + MarshalBinary() ([]byte, error) UnmarshalBinary([]byte) error } -- 2.49.1