diff --git a/cobra.go b/cobra.go index a6b160ce..5c3eaa2b 100644 --- a/cobra.go +++ b/cobra.go @@ -232,7 +232,7 @@ func stringInSlice(a string, list []string) bool { // CheckErr prints the msg with the prefix 'Error:' and exits with error code 1. If the msg is nil, it does nothing. func CheckErr(msg interface{}) { if msg != nil { - fmt.Fprintln(os.Stderr, "Error:", msg) + fmt.Fprintln(os.Stderr, i18nError()+":", msg) os.Exit(1) } } diff --git a/flag_groups.go b/flag_groups.go index 0671ec5f..2fa327c4 100644 --- a/flag_groups.go +++ b/flag_groups.go @@ -201,7 +201,7 @@ func validateExclusiveFlagGroups(data map[string]map[string]bool) error { // Sort values, so they can be tested/scripted against consistently. sort.Strings(set) - return fmt.Errorf("if any flags in the group [%v] are set none of the others can be; %v were all set", flagList, set) + return fmt.Errorf(i18nExclusiveFlagsValidationError(), flagList, set) } return nil } diff --git a/go.mod b/go.mod index a79e66a1..58942970 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,12 @@ module github.com/spf13/cobra -go 1.15 +go 1.16 require ( + github.com/BurntSushi/toml v1.0.0 github.com/cpuguy83/go-md2man/v2 v2.0.3 github.com/inconshreveable/mousetrap v1.1.0 + github.com/nicksnyder/go-i18n/v2 v2.2.1 github.com/spf13/pflag v1.0.5 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/localizer.go b/localizer.go new file mode 100644 index 00000000..037c407e --- /dev/null +++ b/localizer.go @@ -0,0 +1,94 @@ +package cobra + +import ( + "embed" + "fmt" + "github.com/BurntSushi/toml" + "github.com/nicksnyder/go-i18n/v2/i18n" + "golang.org/x/text/language" + "os" +) + +var defaultLanguage = language.English + +// envVariablesHoldingLocale is sorted by decreasing priority +// These environment variables are expected to hold a parsable locale (fr_FR, es, en-US, …) +var envVariablesHoldingLocale = []string{ + "LANGUAGE", + "LC_ALL", + "LANG", +} + +// localeFS points to an embedded filesystem of TOML translation files +// +//go:embed translations/*.toml +var localeFS embed.FS + +// Localizer can be used to fetch localized messages +var localizer *i18n.Localizer + +func i18nError() string { + return localizeMessage(&i18n.Message{ + ID: "Error", + Description: "prefix of error messages", + Other: "Error", + }) +} + +func i18nExclusiveFlagsValidationError() string { + return localizeMessage(&i18n.Message{ + ID: "ExclusiveFlagsValidationError", + Description: "error shown when multiple exclusive flags are provided (group flags, offending flags)", + Other: "if any flags in the group [%v] are set none of the others can be; %v were all set", + }) +} + +// … lots more translations here + +func localizeMessage(message *i18n.Message) string { + localizedValue, err := localizer.Localize(&i18n.LocalizeConfig{ + DefaultMessage: message, + }) + if err != nil { + return message.Other + } + + return localizedValue +} + +func loadTranslationFiles(bundle *i18n.Bundle, langs []string) { + for _, lang := range langs { + _, _ = bundle.LoadMessageFileFS(localeFS, fmt.Sprintf("translations/main.%s.toml", lang)) + } +} + +func detectLangs() []string { + var detectedLangs []string + for _, envKey := range envVariablesHoldingLocale { + lang := os.Getenv(envKey) + if lang != "" { + detectedLang := language.Make(lang) + appendLang(&detectedLangs, detectedLang) + } + } + appendLang(&detectedLangs, defaultLanguage) + + return detectedLangs +} + +func appendLang(langs *[]string, lang language.Tag) { + langString := lang.String() + langBase, _ := lang.Base() + *langs = append(*langs, langString) + *langs = append(*langs, langBase.ISO3()) + *langs = append(*langs, langBase.String()) +} + +func init() { + bundle := i18n.NewBundle(defaultLanguage) + bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal) + detectedLangs := detectLangs() + //fmt.Println("Detected languages", detectedLangs) + loadTranslationFiles(bundle, detectedLangs) + localizer = i18n.NewLocalizer(bundle, detectedLangs...) +} diff --git a/translations/main.en.toml b/translations/main.en.toml new file mode 100644 index 00000000..212e2007 --- /dev/null +++ b/translations/main.en.toml @@ -0,0 +1,7 @@ +[Error] +description = "prefix of error messages" +other = "Error" + +[ExclusiveFlagsValidationError] +description = "error shown when multiple exclusive flags are provided (group flags, offending flags)" +other = "if any flags in the group [%v] are set none of the others can be; %v were all set" diff --git a/translations/main.fr.toml b/translations/main.fr.toml new file mode 100644 index 00000000..10d2aa24 --- /dev/null +++ b/translations/main.fr.toml @@ -0,0 +1,7 @@ +[Error] +description = "prefix of error messages" +other = "Erreur" + +[ExclusiveFlagsValidationError] +description = "error shown when multiple exclusive flags are provided (group flags, offending flags)" +other = "les options [%v] sont exclusives, mais les options %v ont été fournies"