From 2de9f1582868b1ccc03de105d92f4b2b0e84b170 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Apr 2024 06:15:34 +0000 Subject: [PATCH] build(deps): bump github.com/leonelquinteros/gotext Bumps [github.com/leonelquinteros/gotext](https://github.com/leonelquinteros/gotext) from 1.5.3-0.20230317130943-71a59c05b2c1 to 1.6.0. - [Release notes](https://github.com/leonelquinteros/gotext/releases) - [Commits](https://github.com/leonelquinteros/gotext/commits/v1.6.0) --- updated-dependencies: - dependency-name: github.com/leonelquinteros/gotext dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 +- .../leonelquinteros/gotext/domain.go | 133 ++++++-- .../leonelquinteros/gotext/gotext.go | 303 ++++++++++++++---- .../leonelquinteros/gotext/introspector.go | 25 ++ .../leonelquinteros/gotext/locale.go | 109 ++++++- .../github.com/leonelquinteros/gotext/mo.go | 19 +- .../github.com/leonelquinteros/gotext/po.go | 21 +- .../leonelquinteros/gotext/translation.go | 12 + .../leonelquinteros/gotext/translator.go | 2 +- vendor/modules.txt | 2 +- 11 files changed, 521 insertions(+), 111 deletions(-) create mode 100644 vendor/github.com/leonelquinteros/gotext/introspector.go diff --git a/go.mod b/go.mod index e1ff0d72a..eeccc9f03 100644 --- a/go.mod +++ b/go.mod @@ -55,7 +55,7 @@ require ( github.com/jellydator/ttlcache/v3 v3.2.0 github.com/jinzhu/now v1.1.5 github.com/justinas/alice v1.2.0 - github.com/leonelquinteros/gotext v1.5.3-0.20230317130943-71a59c05b2c1 + github.com/leonelquinteros/gotext v1.6.0 github.com/libregraph/idm v0.4.1-0.20240410123343-a51b459380d0 github.com/libregraph/lico v0.61.3-0.20240322112242-72cf9221d3a7 github.com/mitchellh/mapstructure v1.5.0 diff --git a/go.sum b/go.sum index 2f9037290..1031dd469 100644 --- a/go.sum +++ b/go.sum @@ -1632,8 +1632,8 @@ github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvf github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/leonelquinteros/gotext v1.5.3-0.20230317130943-71a59c05b2c1 h1:k56sFOOJ0CYuQtGoRSeAMhP1R692+iNH+S1dC/CEz0w= -github.com/leonelquinteros/gotext v1.5.3-0.20230317130943-71a59c05b2c1/go.mod h1:AT4NpQrOmyj1L/+hLja6aR0lk81yYYL4ePnj2kp7d6M= +github.com/leonelquinteros/gotext v1.6.0 h1:IYL2+dKsaYYvqGAOafaC7mpAGBhMrD/vKjHUGyp8V64= +github.com/leonelquinteros/gotext v1.6.0/go.mod h1:qQRISjoonXYFdRGrTG1LARQ38Gpibad0IPeB4hpvyyM= github.com/libregraph/idm v0.4.1-0.20240410123343-a51b459380d0 h1:NC6JoX08mr2WOVyplqbLUFFZuGJQHc/Xzsbtcpz0aqA= github.com/libregraph/idm v0.4.1-0.20240410123343-a51b459380d0/go.mod h1:taEqhdiG7u1aXxPNAi1+IQUpxDP0JpoDGrwhfpmHlhs= github.com/libregraph/lico v0.61.3-0.20240322112242-72cf9221d3a7 h1:fcPgiBu7DGyGeokE0Qk+S+GW/3n+QWu1dIjw0TqadhI= diff --git a/vendor/github.com/leonelquinteros/gotext/domain.go b/vendor/github.com/leonelquinteros/gotext/domain.go index 2bd40a671..ad8f9b1d7 100644 --- a/vendor/github.com/leonelquinteros/gotext/domain.go +++ b/vendor/github.com/leonelquinteros/gotext/domain.go @@ -3,14 +3,13 @@ package gotext import ( "bytes" "encoding/gob" + "fmt" "regexp" "sort" "strconv" "strings" "sync" - "golang.org/x/text/language" - "github.com/leonelquinteros/gotext/plurals" ) @@ -21,7 +20,6 @@ type Domain struct { // Language header Language string - tag language.Tag // Plural-Forms header PluralForms string @@ -35,9 +33,9 @@ type Domain struct { pluralforms plurals.Expression // Storage - translations map[string]*Translation - contexts map[string]map[string]*Translation - pluralTranslations map[string]*Translation + translations map[string]*Translation + contextTranslations map[string]map[string]*Translation + pluralTranslations map[string]*Translation // Sync Mutex trMutex sync.RWMutex @@ -84,7 +82,7 @@ func NewDomain() *Domain { domain.Headers = make(HeaderMap) domain.headerComments = make([]string, 0) domain.translations = make(map[string]*Translation) - domain.contexts = make(map[string]map[string]*Translation) + domain.contextTranslations = make(map[string]map[string]*Translation) domain.pluralTranslations = make(map[string]*Translation) return domain @@ -142,7 +140,6 @@ func (do *Domain) parseHeaders() { // Get/save needed headers do.Language = do.Headers.Get(languageKey) - do.tag = language.Make(do.Language) do.PluralForms = do.Headers.Get(pluralFormsKey) // Parse Plural-Forms formula @@ -183,14 +180,14 @@ func (do *Domain) DropStaleTranslations() { defer do.trMutex.Unlock() defer do.pluralMutex.Unlock() - for name, ctx := range do.contexts { + for name, ctx := range do.contextTranslations { for id, trans := range ctx { if trans.IsStale() { delete(ctx, id) } } if len(ctx) == 0 { - delete(do.contexts, name) + delete(do.contextTranslations, name) } } @@ -312,7 +309,7 @@ func (do *Domain) SetC(id, ctx, str string) { defer do.trMutex.Unlock() defer do.pluralMutex.Unlock() - if context, ok := do.contexts[ctx]; ok { + if context, ok := do.contextTranslations[ctx]; ok { if trans, hasTrans := context[id]; hasTrans { trans.Set(str) } else { @@ -325,7 +322,7 @@ func (do *Domain) SetC(id, ctx, str string) { trans := NewTranslation() trans.ID = id trans.Set(str) - do.contexts[ctx] = map[string]*Translation{ + do.contextTranslations[ctx] = map[string]*Translation{ id: trans, } } @@ -337,11 +334,11 @@ func (do *Domain) GetC(str, ctx string, vars ...interface{}) string { do.trMutex.RLock() defer do.trMutex.RUnlock() - if do.contexts != nil { - if _, ok := do.contexts[ctx]; ok { - if do.contexts[ctx] != nil { - if _, ok := do.contexts[ctx][str]; ok { - return Printf(do.contexts[ctx][str].Get(), vars...) + if do.contextTranslations != nil { + if _, ok := do.contextTranslations[ctx]; ok { + if do.contextTranslations[ctx] != nil { + if _, ok := do.contextTranslations[ctx][str]; ok { + return Printf(do.contextTranslations[ctx][str].Get(), vars...) } } } @@ -361,7 +358,7 @@ func (do *Domain) SetNC(id, plural, ctx string, n int, str string) { defer do.trMutex.Unlock() defer do.pluralMutex.Unlock() - if context, ok := do.contexts[ctx]; ok { + if context, ok := do.contextTranslations[ctx]; ok { if trans, hasTrans := context[id]; hasTrans { trans.SetN(pluralForm, str) } else { @@ -374,7 +371,7 @@ func (do *Domain) SetNC(id, plural, ctx string, n int, str string) { trans := NewTranslation() trans.ID = id trans.SetN(pluralForm, str) - do.contexts[ctx] = map[string]*Translation{ + do.contextTranslations[ctx] = map[string]*Translation{ id: trans, } } @@ -386,11 +383,11 @@ func (do *Domain) GetNC(str, plural string, n int, ctx string, vars ...interface do.trMutex.RLock() defer do.trMutex.RUnlock() - if do.contexts != nil { - if _, ok := do.contexts[ctx]; ok { - if do.contexts[ctx] != nil { - if _, ok := do.contexts[ctx][str]; ok { - return Printf(do.contexts[ctx][str].GetN(do.pluralForm(n)), vars...) + if do.contextTranslations != nil { + if _, ok := do.contextTranslations[ctx]; ok { + if do.contextTranslations[ctx] != nil { + if _, ok := do.contextTranslations[ctx][str]; ok { + return Printf(do.contextTranslations[ctx][str].GetN(do.pluralForm(n)), vars...) } } } @@ -402,7 +399,51 @@ func (do *Domain) GetNC(str, plural string, n int, ctx string, vars ...interface return Printf(plural, vars...) } -//GetTranslations returns a copy of every translation in the domain. It does not support contexts. +// IsTranslated reports whether a string is translated +func (do *Domain) IsTranslated(str string) bool { + return do.IsTranslatedN(str, 0) +} + +// IsTranslatedN reports whether a plural string is translated +func (do *Domain) IsTranslatedN(str string, n int) bool { + do.trMutex.RLock() + defer do.trMutex.RUnlock() + + if do.translations == nil { + return false + } + tr, ok := do.translations[str] + if !ok { + return false + } + return tr.IsTranslatedN(n) +} + +// IsTranslatedC reports whether a context string is translated +func (do *Domain) IsTranslatedC(str, ctx string) bool { + return do.IsTranslatedNC(str, 0, ctx) +} + +// IsTranslatedNC reports whether a plural context string is translated +func (do *Domain) IsTranslatedNC(str string, n int, ctx string) bool { + do.trMutex.RLock() + defer do.trMutex.RUnlock() + + if do.contextTranslations == nil { + return false + } + translations, ok := do.contextTranslations[ctx] + if !ok { + return false + } + tr, ok := translations[str] + if !ok { + return false + } + return tr.IsTranslatedN(n) +} + +// GetTranslations returns a copy of every translation in the domain. It does not support contexts. func (do *Domain) GetTranslations() map[string]*Translation { all := make(map[string]*Translation, len(do.translations)) @@ -511,7 +552,7 @@ func (do *Domain) MarshalText() ([]byte, error) { // Just as with headers, output translations in consistent order (to minimise diffs between round-trips), with (first) source reference taking priority, followed by context and finally ID references := make([]SourceReference, 0) - for name, ctx := range do.contexts { + for name, ctx := range do.contextTranslations { for id, trans := range ctx { if id == "" { continue @@ -609,9 +650,39 @@ func (do *Domain) MarshalText() ([]byte, error) { } func EscapeSpecialCharacters(s string) string { - s = regexp.MustCompile(`([^\\])(")`).ReplaceAllString(s, "$1\\\"") // Escape non-escaped double quotation marks - s = strings.ReplaceAll(s, "\n", "\"\n\"") // Convert newlines into multi-line strings - return s + s = regexp.MustCompile(`([^\\])(")`).ReplaceAllString(s, "$1\\\"") // Escape non-escaped double quotation marks + + if strings.Count(s, "\n") == 0 { + return s + } + + // Handle EOL and multi-lines + // Only one line, but finishing with \n + if strings.Count(s, "\n") == 1 && strings.HasSuffix(s, "\n") { + return strings.ReplaceAll(s, "\n", "\\n") + } + + elems := strings.Split(s, "\n") + // Skip last element for multiline which is an empty + var shouldEndWithEOL bool + if elems[len(elems)-1] == "" { + elems = elems[:len(elems)-1] + shouldEndWithEOL = true + } + data := []string{(`"`)} + for i, v := range elems { + l := fmt.Sprintf(`"%s\n"`, v) + // Last element without EOL + if i == len(elems)-1 && !shouldEndWithEOL { + l = fmt.Sprintf(`"%s"`, v) + } + // Remove finale " to last element as the whole string will be quoted + if i == len(elems)-1 { + l = strings.TrimSuffix(l, `"`) + } + data = append(data, l) + } + return strings.Join(data, "\n") } // MarshalBinary implements encoding.BinaryMarshaler interface @@ -623,7 +694,7 @@ func (do *Domain) MarshalBinary() ([]byte, error) { obj.Nplurals = do.nplurals obj.Plural = do.plural obj.Translations = do.translations - obj.Contexts = do.contexts + obj.Contexts = do.contextTranslations var buff bytes.Buffer encoder := gob.NewEncoder(&buff) @@ -649,7 +720,7 @@ func (do *Domain) UnmarshalBinary(data []byte) error { do.nplurals = obj.Nplurals do.plural = obj.Plural do.translations = obj.Translations - do.contexts = obj.Contexts + do.contextTranslations = obj.Contexts if expr, err := plurals.Compile(do.plural); err == nil { do.pluralforms = expr diff --git a/vendor/github.com/leonelquinteros/gotext/gotext.go b/vendor/github.com/leonelquinteros/gotext/gotext.go index fbfc17d7f..e56be5128 100644 --- a/vendor/github.com/leonelquinteros/gotext/gotext.go +++ b/vendor/github.com/leonelquinteros/gotext/gotext.go @@ -24,6 +24,7 @@ package gotext import ( "encoding/gob" + "strings" "sync" ) @@ -31,47 +32,56 @@ import ( type config struct { sync.RWMutex + // Path to library directory where all locale directories and Translation files are. + library string + // Default domain to look at when no domain is specified. Used by package level functions. domain string // Language set. - language string - - // Path to library directory where all locale directories and Translation files are. - library string + languages []string // Storage for package level methods - storage *Locale + locales []*Locale } var globalConfig *config +var FallbackLocale = "en_US" + func init() { // Init default configuration globalConfig = &config{ - domain: "default", - language: "en_US", - library: "/usr/local/share/locale", - storage: nil, + domain: "default", + languages: []string{FallbackLocale}, + library: "/usr/local/share/locale", + locales: nil, } // Register Translator types for gob encoding gob.Register(TranslatorEncoding{}) } -// loadStorage creates a new Locale object at package level based on the Global variables settings. -// It's called automatically when trying to use Get or GetD methods. -func loadStorage(force bool) { +// loadLocales creates a new Locale object for every language (specified using Configure) +// at package level based on the configuration of global configuration . +// It is called when trying to use Get or GetD methods. +func loadLocales(rebuildCache bool) { globalConfig.Lock() - if globalConfig.storage == nil || force { - globalConfig.storage = NewLocale(globalConfig.library, globalConfig.language) + if globalConfig.locales == nil || rebuildCache { + var locales []*Locale + for _, language := range globalConfig.languages { + locales = append(locales, NewLocale(globalConfig.library, language)) + } + globalConfig.locales = locales } - if _, ok := globalConfig.storage.Domains[globalConfig.domain]; !ok || force { - globalConfig.storage.AddDomain(globalConfig.domain) + for _, locale := range globalConfig.locales { + if _, ok := locale.Domains[globalConfig.domain]; !ok || rebuildCache { + locale.AddDomain(globalConfig.domain) + } + locale.SetDomain(globalConfig.domain) } - globalConfig.storage.SetDomain(globalConfig.domain) globalConfig.Unlock() } @@ -80,8 +90,9 @@ func loadStorage(force bool) { func GetDomain() string { var dom string globalConfig.RLock() - if globalConfig.storage != nil { - dom = globalConfig.storage.GetDomain() + if globalConfig.locales != nil { + // All locales have the same domain + dom = globalConfig.locales[0].GetDomain() } if dom == "" { dom = globalConfig.domain @@ -96,31 +107,46 @@ func GetDomain() string { func SetDomain(dom string) { globalConfig.Lock() globalConfig.domain = dom - if globalConfig.storage != nil { - globalConfig.storage.SetDomain(dom) + if globalConfig.locales != nil { + for _, locale := range globalConfig.locales { + locale.SetDomain(dom) + } } globalConfig.Unlock() - loadStorage(true) + loadLocales(true) } -// GetLanguage is the language getter for the package configuration +// GetLanguage returns the language gotext will translate into. +// If multiple languages have been supplied, the first one will be returned. +// If no language has been supplied, the fallback will be returned. func GetLanguage() string { - globalConfig.RLock() - lang := globalConfig.language - globalConfig.RUnlock() - - return lang + languages := GetLanguages() + if len(languages) == 0 { + return FallbackLocale + } + return languages[0] } -// SetLanguage sets the language code to be used at package level. +// GetLanguages returns all languages that have been supplied. +func GetLanguages() []string { + globalConfig.RLock() + defer globalConfig.RUnlock() + return globalConfig.languages +} + +// SetLanguage sets the language code (or colon separated language codes) to be used at package level. // It reloads the corresponding Translation file. func SetLanguage(lang string) { globalConfig.Lock() - globalConfig.language = SimplifiedLocale(lang) + var languages []string + for _, language := range strings.Split(lang, ":") { + languages = append(languages, SimplifiedLocale(language)) + } + globalConfig.languages = languages globalConfig.Unlock() - loadStorage(true) + loadLocales(true) } // GetLibrary is the library getter for the package configuration @@ -132,14 +158,56 @@ func GetLibrary() string { return lib } -// SetLibrary sets the root path for the loale directories and files to be used at package level. +// SetLibrary sets the root path for the locale directories and files to be used at package level. // It reloads the corresponding Translation file. func SetLibrary(lib string) { globalConfig.Lock() globalConfig.library = lib globalConfig.Unlock() - loadStorage(true) + loadLocales(true) +} + +func GetLocales() []*Locale { + globalConfig.RLock() + defer globalConfig.RUnlock() + return globalConfig.locales +} + +// GetStorage is the locale storage getter for the package configuration. +// +// Deprecated: Storage has been renamed to Locale for consistency, use GetLocales instead. +func GetStorage() *Locale { + locales := GetLocales() + if len(locales) > 0 { + return locales[0] + } + return nil +} + +// SetLocales allows for overriding the global Locale objects with ones built manually with +// NewLocale(). This makes it possible to attach custom Domain objects from in-memory po/mo. +// The library, language and domain of the first Locale will set the default global configuration. +func SetLocales(locales []*Locale) { + globalConfig.Lock() + defer globalConfig.Unlock() + + globalConfig.locales = locales + globalConfig.library = locales[0].path + globalConfig.domain = locales[0].defaultDomain + + var languages []string + for _, locale := range locales { + languages = append(languages, locale.lang) + } + globalConfig.languages = languages +} + +// SetStorage allows overriding the global Locale object with one built manually with NewLocale(). +// +// Deprecated: Storage has been renamed to Locale for consistency, use SetLocales instead. +func SetStorage(locale *Locale) { + SetLocales([]*Locale{locale}) } // Configure sets all configuration variables to be used at package level and reloads the corresponding Translation file. @@ -149,11 +217,15 @@ func SetLibrary(lib string) { func Configure(lib, lang, dom string) { globalConfig.Lock() globalConfig.library = lib - globalConfig.language = SimplifiedLocale(lang) + var languages []string + for _, language := range strings.Split(lang, ":") { + languages = append(languages, SimplifiedLocale(language)) + } + globalConfig.languages = languages globalConfig.domain = dom globalConfig.Unlock() - loadStorage(true) + loadLocales(true) } // Get uses the default domain globally set to return the corresponding Translation of a given string. @@ -171,38 +243,46 @@ func GetN(str, plural string, n int, vars ...interface{}) 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 { - // Try to load default package Locale storage - loadStorage(false) + // Try to load default package Locales + loadLocales(false) - // Return Translation globalConfig.RLock() + defer globalConfig.RUnlock() - if _, ok := globalConfig.storage.Domains[dom]; !ok { - globalConfig.storage.AddDomain(dom) + var tr string + for i, locale := range globalConfig.locales { + if _, ok := locale.Domains[dom]; !ok { + locale.AddDomain(dom) + } + if !locale.IsTranslatedD(dom, str) && i < (len(globalConfig.locales)-1) { + continue + } + tr = locale.GetD(dom, str, vars...) + break } - - tr := globalConfig.storage.GetD(dom, str, vars...) - globalConfig.RUnlock() - return tr } // GetND retrieves the (N)th plural form of 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 - loadStorage(false) + // Try to load default package Locales + loadLocales(false) - // Return Translation globalConfig.RLock() + defer globalConfig.RUnlock() - if _, ok := globalConfig.storage.Domains[dom]; !ok { - globalConfig.storage.AddDomain(dom) + var tr string + for i, locale := range globalConfig.locales { + if _, ok := locale.Domains[dom]; !ok { + locale.AddDomain(dom) + } + if !locale.IsTranslatedND(dom, str, n) && i < (len(globalConfig.locales)-1) { + continue + } + tr = locale.GetND(dom, str, plural, n, vars...) + break } - - tr := globalConfig.storage.GetND(dom, str, plural, n, vars...) - globalConfig.RUnlock() - return tr } @@ -221,27 +301,126 @@ func GetNC(str, plural string, n int, ctx string, vars ...interface{}) string { // GetDC 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 GetDC(dom, str, ctx string, vars ...interface{}) string { - // Try to load default package Locale storage - loadStorage(false) + // Try to load default package Locales + loadLocales(false) - // Return Translation globalConfig.RLock() - tr := globalConfig.storage.GetDC(dom, str, ctx, vars...) - globalConfig.RUnlock() + defer globalConfig.RUnlock() + var tr string + for i, locale := range globalConfig.locales { + if !locale.IsTranslatedDC(dom, str, ctx) && i < (len(globalConfig.locales)-1) { + continue + } + tr = locale.GetDC(dom, str, ctx, vars...) + break + } return tr } // GetNDC retrieves the (N)th plural form of 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 GetNDC(dom, str, plural string, n int, ctx string, vars ...interface{}) string { - // Try to load default package Locale storage - loadStorage(false) + // Try to load default package Locales + loadLocales(false) // Return Translation globalConfig.RLock() - tr := globalConfig.storage.GetNDC(dom, str, plural, n, ctx, vars...) - globalConfig.RUnlock() + defer globalConfig.RUnlock() + var tr string + for i, locale := range globalConfig.locales { + if !locale.IsTranslatedNDC(dom, str, n, ctx) && i < (len(globalConfig.locales)-1) { + continue + } + tr = locale.GetNDC(dom, str, plural, n, ctx, vars...) + break + } return tr } + +// 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...) +} + +// IsTranslatedN reports whether a plural string is translated in given languages. +// When the langs argument is omitted, the output of GetLanguages is used. +func IsTranslatedN(str string, n int, langs ...string) bool { + return IsTranslatedND(GetDomain(), str, n, langs...) +} + +// 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...) +} + +// IsTranslatedND reports whether a plural domain string is translated in any of given languages. +// When the langs argument is omitted, the output of GetLanguages is used. +func IsTranslatedND(dom, str string, n int, langs ...string) bool { + if len(langs) == 0 { + langs = GetLanguages() + } + + loadLocales(false) + + globalConfig.RLock() + defer globalConfig.RUnlock() + + for _, lang := range langs { + lang = SimplifiedLocale(lang) + + for _, supportedLocale := range globalConfig.locales { + if lang != supportedLocale.GetActualLanguage(dom) { + continue + } + return supportedLocale.IsTranslatedND(dom, str, n) + } + } + return false +} + +// 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...) +} + +// IsTranslatedNC reports whether a plural context string is translated in given languages. +// When the langs argument is omitted, the output of GetLanguages is used. +func IsTranslatedNC(str string, n int, ctx string, langs ...string) bool { + return IsTranslatedNDC(GetDomain(), str, n, ctx, langs...) +} + +// IsTranslatedDC reports whether a domain context string is translated in given languages. +// When the langs argument is omitted, the output of GetLanguages is used. +func IsTranslatedDC(dom, str, ctx string, langs ...string) bool { + return IsTranslatedNDC(dom, str, 0, ctx, langs...) +} + +// IsTranslatedNDC reports whether a plural domain context string is translated in any of given languages. +// When the langs argument is omitted, the output of GetLanguages is used. +func IsTranslatedNDC(dom, str string, n int, ctx string, langs ...string) bool { + if len(langs) == 0 { + langs = GetLanguages() + } + + loadLocales(false) + + globalConfig.RLock() + defer globalConfig.RUnlock() + + for _, lang := range langs { + lang = SimplifiedLocale(lang) + + for _, locale := range globalConfig.locales { + if lang != locale.GetActualLanguage(dom) { + continue + } + return locale.IsTranslatedNDC(dom, str, n, ctx) + } + } + return false +} diff --git a/vendor/github.com/leonelquinteros/gotext/introspector.go b/vendor/github.com/leonelquinteros/gotext/introspector.go new file mode 100644 index 000000000..5305dbfdf --- /dev/null +++ b/vendor/github.com/leonelquinteros/gotext/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/vendor/github.com/leonelquinteros/gotext/locale.go b/vendor/github.com/leonelquinteros/gotext/locale.go index d7202bbe7..ac204dddc 100644 --- a/vendor/github.com/leonelquinteros/gotext/locale.go +++ b/vendor/github.com/leonelquinteros/gotext/locale.go @@ -111,15 +111,44 @@ func (l *Locale) findExt(dom, ext string) string { return "" } +// GetActualLanguage inspects the filesystem and decides whether to strip +// a CC part of the ll_CC locale string. +func (l *Locale) GetActualLanguage(dom string) string { + extensions := []string{"mo", "po"} + var fp string + for _, ext := range extensions { + // 'll' (or 'll_CC') exists, and it was specified as-is + fp = path.Join(l.path, l.lang, "LC_MESSAGES", dom+"."+ext) + if l.fileExists(fp) { + return l.lang + } + // 'll' exists, but 'll_CC' was specified + if len(l.lang) > 2 { + fp = path.Join(l.path, l.lang[:2], "LC_MESSAGES", dom+"."+ext) + if l.fileExists(fp) { + return l.lang[:2] + } + } + // 'll' (or 'll_CC') exists outside of LC_category, and it was specified as-is + fp = path.Join(l.path, l.lang, dom+"."+ext) + if l.fileExists(fp) { + return l.lang + } + // 'll' exists outside of LC_category, but 'll_CC' was specified + if len(l.lang) > 2 { + fp = path.Join(l.path, l.lang[:2], dom+"."+ext) + if l.fileExists(fp) { + return l.lang[:2] + } + } + } + return "" +} + func (l *Locale) fileExists(filename string) bool { if l.fs != nil { - f, err := l.fs.Open(filename) - if err != nil { - return false - } - _, err = f.Stat() + _, err := fs.Stat(l.fs, filename) return err == nil - } _, err := os.Stat(filename) return err == nil @@ -192,6 +221,14 @@ func (l *Locale) SetDomain(dom string) { l.Unlock() } +// GetLanguage is the lang getter for Locale configuration +func (l *Locale) GetLanguage() string { + l.RLock() + lang := l.lang + l.RUnlock() + return lang +} + // 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 { @@ -311,6 +348,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/vendor/github.com/leonelquinteros/gotext/mo.go b/vendor/github.com/leonelquinteros/gotext/mo.go index bcb122c49..e80998d71 100644 --- a/vendor/github.com/leonelquinteros/gotext/mo.go +++ b/vendor/github.com/leonelquinteros/gotext/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() } @@ -263,10 +276,10 @@ func (mo *Mo) addTranslation(msgid, msgstr []byte) { if len(msgctxt) > 0 { // With context... - if _, ok := mo.domain.contexts[string(msgctxt)]; !ok { - mo.domain.contexts[string(msgctxt)] = make(map[string]*Translation) + if _, ok := mo.domain.contextTranslations[string(msgctxt)]; !ok { + mo.domain.contextTranslations[string(msgctxt)] = make(map[string]*Translation) } - mo.domain.contexts[string(msgctxt)][translation.ID] = translation + mo.domain.contextTranslations[string(msgctxt)][translation.ID] = translation } else { mo.domain.translations[translation.ID] = translation } diff --git a/vendor/github.com/leonelquinteros/gotext/po.go b/vendor/github.com/leonelquinteros/gotext/po.go index 95f098974..a7d656d3e 100644 --- a/vendor/github.com/leonelquinteros/gotext/po.go +++ b/vendor/github.com/leonelquinteros/gotext/po.go @@ -25,7 +25,7 @@ Example: func main() { // Create po object - po := gotext.NewPoTranslator() + po := gotext.NewPo() // Parse .po file po.ParseFile("/path/to/po/file/translations.po") @@ -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() } @@ -223,10 +236,10 @@ func (po *Po) saveBuffer() { po.domain.translations[po.domain.trBuffer.ID] = po.domain.trBuffer } else { // With context... - if _, ok := po.domain.contexts[po.domain.ctxBuffer]; !ok { - po.domain.contexts[po.domain.ctxBuffer] = make(map[string]*Translation) + if _, ok := po.domain.contextTranslations[po.domain.ctxBuffer]; !ok { + po.domain.contextTranslations[po.domain.ctxBuffer] = make(map[string]*Translation) } - po.domain.contexts[po.domain.ctxBuffer][po.domain.trBuffer.ID] = po.domain.trBuffer + po.domain.contextTranslations[po.domain.ctxBuffer][po.domain.trBuffer.ID] = po.domain.trBuffer // Cleanup current context buffer if needed if po.domain.trBuffer.ID != "" { diff --git a/vendor/github.com/leonelquinteros/gotext/translation.go b/vendor/github.com/leonelquinteros/gotext/translation.go index 26c30cf3c..0e15cc0f8 100644 --- a/vendor/github.com/leonelquinteros/gotext/translation.go +++ b/vendor/github.com/leonelquinteros/gotext/translation.go @@ -78,3 +78,15 @@ func (t *Translation) GetN(n int) string { // Return untranslated plural by default return t.PluralID } + +// IsTranslated reports whether a string is translated +func (t *Translation) IsTranslated() bool { + tr, ok := t.Trs[0] + return tr != "" && ok +} + +// IsTranslatedN reports whether a plural string is translated +func (t *Translation) IsTranslatedN(n int) bool { + tr, ok := t.Trs[n] + return tr != "" && ok +} diff --git a/vendor/github.com/leonelquinteros/gotext/translator.go b/vendor/github.com/leonelquinteros/gotext/translator.go index e5d940ed3..a53776d52 100644 --- a/vendor/github.com/leonelquinteros/gotext/translator.go +++ b/vendor/github.com/leonelquinteros/gotext/translator.go @@ -61,7 +61,7 @@ func (te *TranslatorEncoding) GetTranslator() Translator { po.domain.nplurals = te.Nplurals po.domain.plural = te.Plural po.domain.translations = te.Translations - po.domain.contexts = te.Contexts + po.domain.contextTranslations = te.Contexts return po } diff --git a/vendor/modules.txt b/vendor/modules.txt index a7165f81e..5e7c2ef33 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1256,7 +1256,7 @@ github.com/klauspost/cpuid/v2 ## explicit; go 1.18 github.com/leodido/go-urn github.com/leodido/go-urn/scim/schema -# github.com/leonelquinteros/gotext v1.5.3-0.20230317130943-71a59c05b2c1 +# github.com/leonelquinteros/gotext v1.6.0 ## explicit; go 1.13 github.com/leonelquinteros/gotext github.com/leonelquinteros/gotext/plurals