forked from toolshed/abra
		
	
		
			
				
	
	
		
			428 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			428 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Package gotext implements GNU gettext utilities.
 | 
						|
 | 
						|
For quick/simple translations you can use the package level functions directly.
 | 
						|
 | 
						|
	    import (
 | 
						|
		    "fmt"
 | 
						|
		    "github.com/leonelquinteros/gotext"
 | 
						|
	    )
 | 
						|
 | 
						|
	    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"))
 | 
						|
	    }
 | 
						|
*/
 | 
						|
package gotext
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/gob"
 | 
						|
	"strings"
 | 
						|
	"sync"
 | 
						|
)
 | 
						|
 | 
						|
// Global environment variables
 | 
						|
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.
 | 
						|
	languages []string
 | 
						|
 | 
						|
	// Storage for package level methods
 | 
						|
	locales []*Locale
 | 
						|
}
 | 
						|
 | 
						|
var globalConfig *config
 | 
						|
 | 
						|
// FallbackLocale is the default language to be used when no language is set.
 | 
						|
var FallbackLocale = "en_US"
 | 
						|
 | 
						|
func init() {
 | 
						|
	// Init default configuration
 | 
						|
	globalConfig = &config{
 | 
						|
		domain:    "default",
 | 
						|
		languages: []string{FallbackLocale},
 | 
						|
		library:   "/usr/local/share/locale",
 | 
						|
		locales:   nil,
 | 
						|
	}
 | 
						|
 | 
						|
	// Register Translator types for gob encoding
 | 
						|
	gob.Register(TranslatorEncoding{})
 | 
						|
}
 | 
						|
 | 
						|
// 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.locales == nil || rebuildCache {
 | 
						|
		var locales []*Locale
 | 
						|
		for _, language := range globalConfig.languages {
 | 
						|
			locales = append(locales, NewLocale(globalConfig.library, language))
 | 
						|
		}
 | 
						|
		globalConfig.locales = locales
 | 
						|
	}
 | 
						|
 | 
						|
	for _, locale := range globalConfig.locales {
 | 
						|
		if _, ok := locale.Domains[globalConfig.domain]; !ok || rebuildCache {
 | 
						|
			locale.AddDomain(globalConfig.domain)
 | 
						|
		}
 | 
						|
		locale.SetDomain(globalConfig.domain)
 | 
						|
	}
 | 
						|
 | 
						|
	globalConfig.Unlock()
 | 
						|
}
 | 
						|
 | 
						|
// GetDomain is the domain getter for the package configuration
 | 
						|
func GetDomain() string {
 | 
						|
	var dom string
 | 
						|
	globalConfig.RLock()
 | 
						|
	if globalConfig.locales != nil {
 | 
						|
		// All locales have the same domain
 | 
						|
		dom = globalConfig.locales[0].GetDomain()
 | 
						|
	}
 | 
						|
	if dom == "" {
 | 
						|
		dom = globalConfig.domain
 | 
						|
	}
 | 
						|
	globalConfig.RUnlock()
 | 
						|
 | 
						|
	return dom
 | 
						|
}
 | 
						|
 | 
						|
// SetDomain sets the name for the domain to be used at package level.
 | 
						|
// It reloads the corresponding Translation file.
 | 
						|
func SetDomain(dom string) {
 | 
						|
	globalConfig.Lock()
 | 
						|
	globalConfig.domain = dom
 | 
						|
	if globalConfig.locales != nil {
 | 
						|
		for _, locale := range globalConfig.locales {
 | 
						|
			locale.SetDomain(dom)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	globalConfig.Unlock()
 | 
						|
 | 
						|
	loadLocales(true)
 | 
						|
}
 | 
						|
 | 
						|
// 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 {
 | 
						|
	languages := GetLanguages()
 | 
						|
	if len(languages) == 0 {
 | 
						|
		return FallbackLocale
 | 
						|
	}
 | 
						|
	return languages[0]
 | 
						|
}
 | 
						|
 | 
						|
// 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()
 | 
						|
	var languages []string
 | 
						|
	for _, language := range strings.Split(lang, ":") {
 | 
						|
		languages = append(languages, SimplifiedLocale(language))
 | 
						|
	}
 | 
						|
	globalConfig.languages = languages
 | 
						|
	globalConfig.Unlock()
 | 
						|
 | 
						|
	loadLocales(true)
 | 
						|
}
 | 
						|
 | 
						|
// GetLibrary is the library getter for the package configuration
 | 
						|
func GetLibrary() string {
 | 
						|
	globalConfig.RLock()
 | 
						|
	lib := globalConfig.library
 | 
						|
	globalConfig.RUnlock()
 | 
						|
 | 
						|
	return lib
 | 
						|
}
 | 
						|
 | 
						|
// 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()
 | 
						|
 | 
						|
	loadLocales(true)
 | 
						|
}
 | 
						|
 | 
						|
// GetLocales returns the locales that have been set for the package configuration.
 | 
						|
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.
 | 
						|
// It receives the library path, language code and domain name.
 | 
						|
// This function is recommended to be used when changing more than one setting,
 | 
						|
// as using each setter will introduce a I/O overhead because the Translation file will be loaded after each set.
 | 
						|
func Configure(lib, lang, dom string) {
 | 
						|
	globalConfig.Lock()
 | 
						|
	globalConfig.library = lib
 | 
						|
	var languages []string
 | 
						|
	for _, language := range strings.Split(lang, ":") {
 | 
						|
		languages = append(languages, SimplifiedLocale(language))
 | 
						|
	}
 | 
						|
	globalConfig.languages = languages
 | 
						|
	globalConfig.domain = dom
 | 
						|
	globalConfig.Unlock()
 | 
						|
 | 
						|
	loadLocales(true)
 | 
						|
}
 | 
						|
 | 
						|
// Get uses the default domain globally set 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 Get(str string, vars ...interface{}) string {
 | 
						|
	return GetD(GetDomain(), str, vars...)
 | 
						|
}
 | 
						|
 | 
						|
// GetN 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.
 | 
						|
func GetN(str, plural string, n int, vars ...interface{}) string {
 | 
						|
	return GetND(GetDomain(), str, plural, n, vars...)
 | 
						|
}
 | 
						|
 | 
						|
// 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 Locales
 | 
						|
	loadLocales(false)
 | 
						|
 | 
						|
	globalConfig.RLock()
 | 
						|
	defer globalConfig.RUnlock()
 | 
						|
 | 
						|
	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
 | 
						|
	}
 | 
						|
	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 Locales
 | 
						|
	loadLocales(false)
 | 
						|
 | 
						|
	globalConfig.RLock()
 | 
						|
	defer globalConfig.RUnlock()
 | 
						|
 | 
						|
	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
 | 
						|
	}
 | 
						|
	return tr
 | 
						|
}
 | 
						|
 | 
						|
// GetC uses the default domain globally set 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.
 | 
						|
func GetC(str, ctx string, vars ...interface{}) string {
 | 
						|
	return GetDC(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.
 | 
						|
func GetNC(str, plural string, n int, ctx string, vars ...interface{}) string {
 | 
						|
	return GetNDC(GetDomain(), str, plural, n, ctx, vars...)
 | 
						|
}
 | 
						|
 | 
						|
// 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 Locales
 | 
						|
	loadLocales(false)
 | 
						|
 | 
						|
	globalConfig.RLock()
 | 
						|
	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 Locales
 | 
						|
	loadLocales(false)
 | 
						|
 | 
						|
	// Return Translation
 | 
						|
	globalConfig.RLock()
 | 
						|
	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, 1, 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, 1, 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, 1, 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
 | 
						|
}
 |