package i18n /* * This package is inspired from https://github.com/snapcore/snapd/blob/master/i18n, with other snap dependecies removed * and adapted to follow common go best practices. */ //go:generate go run generate-locales.go update-po ../../po //go:generate go run generate-locales.go generate-mo ../../po ../../generated import ( "fmt" "os" "path/filepath" "strings" "github.com/snapcore/go-gettext" ) type i18n struct { domain string localeDir string loc string gettext.Catalog translations gettext.Translations } var ( locale i18n // G is the shorthand for Gettext G = func(msgid string) string { return msgid } // NG is the shorthand for NGettext NG = func(msgid string, msgidPlural string, n uint32) string { return msgid } ) // InitI18nDomain calls bind + set locale to system values func InitI18nDomain(domain string, options ...func(l *i18n)) { locale = i18n{ domain: domain, localeDir: "/usr/share/locale", } for _, option := range options { option(&locale) } locale.bindTextDomain(locale.domain, locale.localeDir) locale.setLocale(locale.loc) G = locale.Gettext NG = locale.NGettext } // langpackResolver tries to fetch locale mo file path. // It first checks for the real locale (e.g. de_DE) and then // tries to simplify the locale (e.g. de_DE -> de) func langpackResolver(root string, locale string, domain string) string { for _, locale := range []string{locale, strings.SplitN(locale, "_", 2)[0]} { r := filepath.Join(locale, "LC_MESSAGES", fmt.Sprintf("%s.mo", domain)) // look into the generated mo files path first for translations, then the system var candidateDirs []string // Ubuntu uses /usr/share/locale-langpack and patches the glibc gettext implementation candidateDirs = append(candidateDirs, filepath.Join(root, "..", "locale-langpack")) candidateDirs = append(candidateDirs, root) for _, dir := range candidateDirs { candidateMo := filepath.Join(dir, r) // Only load valid candidates, if we can't access it or have perm issues, ignore if _, err := os.Stat(candidateMo); err != nil { continue } return candidateMo } } return "" } func (l *i18n) bindTextDomain(domain, dir string) { l.translations = gettext.NewTranslations(dir, domain, langpackResolver) } // setLocale initializes the locale name and simplify it. // If empty, it defaults to system ones set in LC_MESSAGES and LANG. func (l *i18n) setLocale(loc string) { if loc == "" { loc = os.Getenv("LC_MESSAGES") if loc == "" { loc = os.Getenv("LANG") } } // de_DE.UTF-8, de_DE@euro all need to get simplified loc = strings.Split(loc, "@")[0] loc = strings.Split(loc, ".")[0] l.Catalog = l.translations.Locale(loc) }