not tracked not covered covered

    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)
    }