mirror of
https://github.com/spf13/cobra
synced 2024-11-24 14:47:12 +00:00
feat(i18n): draft of i18n with embedded TOML files
Supports rfc4646 language tags in env vars. If region is detected (en-US, fr-CA), will try to load regional files first.
This commit is contained in:
parent
a0a6ae020b
commit
ccbdd223d1
6 changed files with 113 additions and 3 deletions
2
cobra.go
2
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
4
go.mod
4
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
|
||||
)
|
||||
|
|
94
localizer.go
Normal file
94
localizer.go
Normal file
|
@ -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...)
|
||||
}
|
7
translations/main.en.toml
Normal file
7
translations/main.en.toml
Normal file
|
@ -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"
|
7
translations/main.fr.toml
Normal file
7
translations/main.fr.toml
Normal file
|
@ -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"
|
Loading…
Reference in a new issue