mirror of
https://github.com/spf13/cobra
synced 2025-04-27 17:17:20 +00:00
Merge 9d4b134da1
into 283e32d889
This commit is contained in:
commit
43b590284a
13 changed files with 780 additions and 39 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -19,6 +19,9 @@ _cgo_export.*
|
||||||
|
|
||||||
_testmain.go
|
_testmain.go
|
||||||
|
|
||||||
|
# Translation files
|
||||||
|
translations/translate.*.toml
|
||||||
|
|
||||||
# Vim files https://github.com/github/gitignore/blob/master/Global/Vim.gitignore
|
# Vim files https://github.com/github/gitignore/blob/master/Global/Vim.gitignore
|
||||||
# swap
|
# swap
|
||||||
[._]*.s[a-w][a-z]
|
[._]*.s[a-w][a-z]
|
||||||
|
|
9
Makefile
9
Makefile
|
@ -33,3 +33,12 @@ install_deps:
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BIN)
|
rm -rf $(BIN)
|
||||||
|
|
||||||
|
i18n_extract:
|
||||||
|
$(info ******************** extracting translation files ********************)
|
||||||
|
goi18n extract -outdir translations
|
||||||
|
goi18n merge -outdir translations translations/*
|
||||||
|
|
||||||
|
i18n_merge:
|
||||||
|
$(info ******************** merging translation files ********************)
|
||||||
|
goi18n merge -outdir translations translations/*
|
||||||
|
|
14
args.go
14
args.go
|
@ -33,7 +33,7 @@ func legacyArgs(cmd *Command, args []string) error {
|
||||||
|
|
||||||
// root command with subcommands, do subcommand checking.
|
// root command with subcommands, do subcommand checking.
|
||||||
if !cmd.HasParent() && len(args) > 0 {
|
if !cmd.HasParent() && len(args) > 0 {
|
||||||
return fmt.Errorf("unknown command %q for %q%s", args[0], cmd.CommandPath(), cmd.findSuggestions(args[0]))
|
return fmt.Errorf(i18nLegacyArgsValidationError(), args[0], cmd.CommandPath(), cmd.findSuggestions(args[0]))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ func legacyArgs(cmd *Command, args []string) error {
|
||||||
// NoArgs returns an error if any args are included.
|
// NoArgs returns an error if any args are included.
|
||||||
func NoArgs(cmd *Command, args []string) error {
|
func NoArgs(cmd *Command, args []string) error {
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
return fmt.Errorf("unknown command %q for %q", args[0], cmd.CommandPath())
|
return fmt.Errorf(i18nNoArgsValidationError(), args[0], cmd.CommandPath())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ func OnlyValidArgs(cmd *Command, args []string) error {
|
||||||
}
|
}
|
||||||
for _, v := range args {
|
for _, v := range args {
|
||||||
if !stringInSlice(v, validArgs) {
|
if !stringInSlice(v, validArgs) {
|
||||||
return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.findSuggestions(args[0]))
|
return fmt.Errorf(i18nOnlyValidArgsValidationError(), v, cmd.CommandPath(), cmd.findSuggestions(args[0]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ func ArbitraryArgs(cmd *Command, args []string) error {
|
||||||
func MinimumNArgs(n int) PositionalArgs {
|
func MinimumNArgs(n int) PositionalArgs {
|
||||||
return func(cmd *Command, args []string) error {
|
return func(cmd *Command, args []string) error {
|
||||||
if len(args) < n {
|
if len(args) < n {
|
||||||
return fmt.Errorf("requires at least %d arg(s), only received %d", n, len(args))
|
return fmt.Errorf(i18nMinimumNArgsValidationError(n), n, len(args))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ func MinimumNArgs(n int) PositionalArgs {
|
||||||
func MaximumNArgs(n int) PositionalArgs {
|
func MaximumNArgs(n int) PositionalArgs {
|
||||||
return func(cmd *Command, args []string) error {
|
return func(cmd *Command, args []string) error {
|
||||||
if len(args) > n {
|
if len(args) > n {
|
||||||
return fmt.Errorf("accepts at most %d arg(s), received %d", n, len(args))
|
return fmt.Errorf(i18nMaximumNArgsValidationError(n), n, len(args))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ func MaximumNArgs(n int) PositionalArgs {
|
||||||
func ExactArgs(n int) PositionalArgs {
|
func ExactArgs(n int) PositionalArgs {
|
||||||
return func(cmd *Command, args []string) error {
|
return func(cmd *Command, args []string) error {
|
||||||
if len(args) != n {
|
if len(args) != n {
|
||||||
return fmt.Errorf("accepts %d arg(s), received %d", n, len(args))
|
return fmt.Errorf(i18nExactArgsValidationError(n), n, len(args))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ func ExactArgs(n int) PositionalArgs {
|
||||||
func RangeArgs(min int, max int) PositionalArgs {
|
func RangeArgs(min int, max int) PositionalArgs {
|
||||||
return func(cmd *Command, args []string) error {
|
return func(cmd *Command, args []string) error {
|
||||||
if len(args) < min || len(args) > max {
|
if len(args) < min || len(args) > max {
|
||||||
return fmt.Errorf("accepts between %d and %d arg(s), received %d", min, max, len(args))
|
return fmt.Errorf(i18nRangeArgsValidationError(max), min, max, len(args))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ func minimumNArgsWithLessArgs(err error, t *testing.T) {
|
||||||
t.Fatal("Expected an error")
|
t.Fatal("Expected an error")
|
||||||
}
|
}
|
||||||
got := err.Error()
|
got := err.Error()
|
||||||
expected := "requires at least 2 arg(s), only received 1"
|
expected := "requires at least 2 args, only received 1"
|
||||||
if got != expected {
|
if got != expected {
|
||||||
t.Fatalf("Expected %q, got %q", expected, got)
|
t.Fatalf("Expected %q, got %q", expected, got)
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ func maximumNArgsWithMoreArgs(err error, t *testing.T) {
|
||||||
t.Fatal("Expected an error")
|
t.Fatal("Expected an error")
|
||||||
}
|
}
|
||||||
got := err.Error()
|
got := err.Error()
|
||||||
expected := "accepts at most 2 arg(s), received 3"
|
expected := "accepts at most 2 args, received 3"
|
||||||
if got != expected {
|
if got != expected {
|
||||||
t.Fatalf("Expected %q, got %q", expected, got)
|
t.Fatalf("Expected %q, got %q", expected, got)
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ func exactArgsWithInvalidCount(err error, t *testing.T) {
|
||||||
t.Fatal("Expected an error")
|
t.Fatal("Expected an error")
|
||||||
}
|
}
|
||||||
got := err.Error()
|
got := err.Error()
|
||||||
expected := "accepts 2 arg(s), received 3"
|
expected := "accepts 2 args, received 3"
|
||||||
if got != expected {
|
if got != expected {
|
||||||
t.Fatalf("Expected %q, got %q", expected, got)
|
t.Fatalf("Expected %q, got %q", expected, got)
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ func rangeArgsWithInvalidCount(err error, t *testing.T) {
|
||||||
t.Fatal("Expected an error")
|
t.Fatal("Expected an error")
|
||||||
}
|
}
|
||||||
got := err.Error()
|
got := err.Error()
|
||||||
expected := "accepts between 2 and 4 arg(s), received 1"
|
expected := "accepts between 2 and 4 args, received 1"
|
||||||
if got != expected {
|
if got != expected {
|
||||||
t.Fatalf("Expected %q, got %q", expected, got)
|
t.Fatalf("Expected %q, got %q", expected, got)
|
||||||
}
|
}
|
||||||
|
|
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.
|
// 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{}) {
|
func CheckErr(msg interface{}) {
|
||||||
if msg != nil {
|
if msg != nil {
|
||||||
fmt.Fprintln(os.Stderr, "Error:", msg)
|
fmt.Fprintln(os.Stderr, i18nError()+":", msg)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
49
command.go
49
command.go
|
@ -432,7 +432,11 @@ func (c *Command) UsageFunc() (f func(*Command) error) {
|
||||||
}
|
}
|
||||||
return func(c *Command) error {
|
return func(c *Command) error {
|
||||||
c.mergePersistentFlags()
|
c.mergePersistentFlags()
|
||||||
err := tmpl(c.OutOrStderr(), c.UsageTemplate(), c)
|
data := CommandUsageTemplateData{
|
||||||
|
Command: c,
|
||||||
|
I18n: getCommandGlossary(),
|
||||||
|
}
|
||||||
|
err := tmpl(c.OutOrStderr(), c.UsageTemplate(), data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.PrintErrln(err)
|
c.PrintErrln(err)
|
||||||
}
|
}
|
||||||
|
@ -549,35 +553,35 @@ func (c *Command) UsageTemplate() string {
|
||||||
if c.HasParent() {
|
if c.HasParent() {
|
||||||
return c.parent.UsageTemplate()
|
return c.parent.UsageTemplate()
|
||||||
}
|
}
|
||||||
return `Usage:{{if .Runnable}}
|
return `{{.I18n.SectionUsage}}:{{if .Runnable}}
|
||||||
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
|
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
|
||||||
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
|
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
|
||||||
|
|
||||||
Aliases:
|
{{.I18n.SectionAliases}}:
|
||||||
{{.NameAndAliases}}{{end}}{{if .HasExample}}
|
{{.NameAndAliases}}{{end}}{{if .HasExample}}
|
||||||
|
|
||||||
Examples:
|
{{.I18n.SectionExamples}}:
|
||||||
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}}
|
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}}
|
||||||
|
|
||||||
Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
|
{{.I18n.SectionAvailableCommands}}:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
|
||||||
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}}
|
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}}
|
||||||
|
|
||||||
{{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}}
|
{{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}}
|
||||||
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}}
|
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}}
|
||||||
|
|
||||||
Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}}
|
{{.I18n.SectionAdditionalCommands}}:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}}
|
||||||
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
|
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
|
||||||
|
|
||||||
Flags:
|
{{.I18n.SectionFlags}}:
|
||||||
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
|
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
|
||||||
|
|
||||||
Global Flags:
|
{{.I18n.SectionGlobalFlags}}:
|
||||||
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
|
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
|
||||||
|
|
||||||
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
|
{{.I18n.SectionAdditionalHelpTopics}}:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
|
||||||
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
|
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
|
||||||
|
|
||||||
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
|
{{.I18n.Use}} "{{.CommandPath}} [command] --help" {{.I18n.ForInfoAboutCommand}}.{{end}}
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -756,7 +760,7 @@ func (c *Command) findSuggestions(arg string) string {
|
||||||
}
|
}
|
||||||
suggestionsString := ""
|
suggestionsString := ""
|
||||||
if suggestions := c.SuggestionsFor(arg); len(suggestions) > 0 {
|
if suggestions := c.SuggestionsFor(arg); len(suggestions) > 0 {
|
||||||
suggestionsString += "\n\nDid you mean this?\n"
|
suggestionsString += "\n\n" + i18nDidYouMeanThis() + "\n"
|
||||||
for _, s := range suggestions {
|
for _, s := range suggestions {
|
||||||
suggestionsString += fmt.Sprintf("\t%v\n", s)
|
suggestionsString += fmt.Sprintf("\t%v\n", s)
|
||||||
}
|
}
|
||||||
|
@ -877,7 +881,7 @@ func (c *Command) execute(a []string) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.Deprecated) > 0 {
|
if len(c.Deprecated) > 0 {
|
||||||
c.Printf("Command %q is deprecated, %s\n", c.Name(), c.Deprecated)
|
c.Printf(i18nCommandDeprecatedWarning()+"\n", c.Name(), c.Deprecated)
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize help and version flag at the last point possible to allow for user
|
// initialize help and version flag at the last point possible to allow for user
|
||||||
|
@ -1096,7 +1100,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
|
||||||
}
|
}
|
||||||
if !c.SilenceErrors {
|
if !c.SilenceErrors {
|
||||||
c.PrintErrln(c.ErrPrefix(), err.Error())
|
c.PrintErrln(c.ErrPrefix(), err.Error())
|
||||||
c.PrintErrf("Run '%v --help' for usage.\n", c.CommandPath())
|
c.PrintErrf(i18nRunHelpTip()+"\n", c.CommandPath())
|
||||||
}
|
}
|
||||||
return c, err
|
return c, err
|
||||||
}
|
}
|
||||||
|
@ -1162,7 +1166,7 @@ func (c *Command) ValidateRequiredFlags() error {
|
||||||
})
|
})
|
||||||
|
|
||||||
if len(missingFlagNames) > 0 {
|
if len(missingFlagNames) > 0 {
|
||||||
return fmt.Errorf(`required flag(s) "%s" not set`, strings.Join(missingFlagNames, `", "`))
|
return fmt.Errorf(i18nFlagNotSetError(len(missingFlagNames)), strings.Join(missingFlagNames, `", "`))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1186,9 +1190,9 @@ func (c *Command) checkCommandGroups() {
|
||||||
func (c *Command) InitDefaultHelpFlag() {
|
func (c *Command) InitDefaultHelpFlag() {
|
||||||
c.mergePersistentFlags()
|
c.mergePersistentFlags()
|
||||||
if c.Flags().Lookup("help") == nil {
|
if c.Flags().Lookup("help") == nil {
|
||||||
usage := "help for "
|
usage := i18nHelpFor() + " "
|
||||||
if c.Name() == "" {
|
if c.Name() == "" {
|
||||||
usage += "this command"
|
usage += i18nThisCommand()
|
||||||
} else {
|
} else {
|
||||||
usage += c.Name()
|
usage += c.Name()
|
||||||
}
|
}
|
||||||
|
@ -1208,9 +1212,9 @@ func (c *Command) InitDefaultVersionFlag() {
|
||||||
|
|
||||||
c.mergePersistentFlags()
|
c.mergePersistentFlags()
|
||||||
if c.Flags().Lookup("version") == nil {
|
if c.Flags().Lookup("version") == nil {
|
||||||
usage := "version for "
|
usage := i18nVersionFor() + " "
|
||||||
if c.Name() == "" {
|
if c.Name() == "" {
|
||||||
usage += "this command"
|
usage += i18nThisCommand()
|
||||||
} else {
|
} else {
|
||||||
usage += c.Name()
|
usage += c.Name()
|
||||||
}
|
}
|
||||||
|
@ -1233,10 +1237,9 @@ func (c *Command) InitDefaultHelpCmd() {
|
||||||
|
|
||||||
if c.helpCommand == nil {
|
if c.helpCommand == nil {
|
||||||
c.helpCommand = &Command{
|
c.helpCommand = &Command{
|
||||||
Use: "help [command]",
|
Use: fmt.Sprintf("help [%s]", i18nCommand()),
|
||||||
Short: "Help about any command",
|
Short: i18nCommandHelpShort(),
|
||||||
Long: `Help provides help for any command in the application.
|
Long: fmt.Sprintf(i18nCommandHelpLong(), c.Name()+fmt.Sprintf(" help [%s]", i18nPathToCommand())),
|
||||||
Simply type ` + c.Name() + ` help [path to command] for full details.`,
|
|
||||||
ValidArgsFunction: func(c *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
ValidArgsFunction: func(c *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
||||||
var completions []string
|
var completions []string
|
||||||
cmd, _, e := c.Root().Find(args)
|
cmd, _, e := c.Root().Find(args)
|
||||||
|
@ -1259,7 +1262,7 @@ Simply type ` + c.Name() + ` help [path to command] for full details.`,
|
||||||
Run: func(c *Command, args []string) {
|
Run: func(c *Command, args []string) {
|
||||||
cmd, _, e := c.Root().Find(args)
|
cmd, _, e := c.Root().Find(args)
|
||||||
if cmd == nil || e != nil {
|
if cmd == nil || e != nil {
|
||||||
c.Printf("Unknown help topic %#q\n", args)
|
c.Printf(i18nCommandHelpUnknownTopicError()+"\n", args)
|
||||||
CheckErr(c.Root().Usage())
|
CheckErr(c.Root().Usage())
|
||||||
} else {
|
} else {
|
||||||
cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown
|
cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown
|
||||||
|
|
|
@ -815,6 +815,21 @@ func TestPersistentFlagsOnChild(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRequiredFlag(t *testing.T) {
|
||||||
|
c := &Command{Use: "c", Run: emptyRun}
|
||||||
|
c.Flags().String("foo1", "", "")
|
||||||
|
assertNoErr(t, c.MarkFlagRequired("foo1"))
|
||||||
|
|
||||||
|
expected := fmt.Sprintf("required flag %q is not set", "foo1")
|
||||||
|
|
||||||
|
_, err := executeCommand(c)
|
||||||
|
got := err.Error()
|
||||||
|
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("Expected error: %q, got: %q", expected, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRequiredFlags(t *testing.T) {
|
func TestRequiredFlags(t *testing.T) {
|
||||||
c := &Command{Use: "c", Run: emptyRun}
|
c := &Command{Use: "c", Run: emptyRun}
|
||||||
c.Flags().String("foo1", "", "")
|
c.Flags().String("foo1", "", "")
|
||||||
|
@ -823,7 +838,7 @@ func TestRequiredFlags(t *testing.T) {
|
||||||
assertNoErr(t, c.MarkFlagRequired("foo2"))
|
assertNoErr(t, c.MarkFlagRequired("foo2"))
|
||||||
c.Flags().String("bar", "", "")
|
c.Flags().String("bar", "", "")
|
||||||
|
|
||||||
expected := fmt.Sprintf("required flag(s) %q, %q not set", "foo1", "foo2")
|
expected := fmt.Sprintf("required flags %q, %q are not set", "foo1", "foo2")
|
||||||
|
|
||||||
_, err := executeCommand(c)
|
_, err := executeCommand(c)
|
||||||
got := err.Error()
|
got := err.Error()
|
||||||
|
@ -850,7 +865,7 @@ func TestPersistentRequiredFlags(t *testing.T) {
|
||||||
|
|
||||||
parent.AddCommand(child)
|
parent.AddCommand(child)
|
||||||
|
|
||||||
expected := fmt.Sprintf("required flag(s) %q, %q, %q, %q not set", "bar1", "bar2", "foo1", "foo2")
|
expected := fmt.Sprintf("required flags %q, %q, %q, %q are not set", "bar1", "bar2", "foo1", "foo2")
|
||||||
|
|
||||||
_, err := executeCommand(parent, "child")
|
_, err := executeCommand(parent, "child")
|
||||||
if err.Error() != expected {
|
if err.Error() != expected {
|
||||||
|
|
|
@ -201,7 +201,7 @@ func validateExclusiveFlagGroups(data map[string]map[string]bool) error {
|
||||||
|
|
||||||
// Sort values, so they can be tested/scripted against consistently.
|
// Sort values, so they can be tested/scripted against consistently.
|
||||||
sort.Strings(set)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
5
go.mod
5
go.mod
|
@ -1,10 +1,13 @@
|
||||||
module github.com/spf13/cobra
|
module github.com/spf13/cobra
|
||||||
|
|
||||||
go 1.15
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/BurntSushi/toml v1.0.0
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.3
|
github.com/cpuguy83/go-md2man/v2 v2.0.3
|
||||||
github.com/inconshreveable/mousetrap v1.1.0
|
github.com/inconshreveable/mousetrap v1.1.0
|
||||||
|
github.com/nicksnyder/go-i18n/v2 v2.2.1
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
|
golang.org/x/text v0.4.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
31
go.sum
31
go.sum
|
@ -1,12 +1,43 @@
|
||||||
|
github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU=
|
||||||
|
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
|
github.com/nicksnyder/go-i18n/v2 v2.2.1 h1:aOzRCdwsJuoExfZhoiXHy4bjruwCMdt5otbYojM/PaA=
|
||||||
|
github.com/nicksnyder/go-i18n/v2 v2.2.1/go.mod h1:fF2++lPHlo+/kPaj3nB0uxtPwzlPm+BlgwGX7MkeGj0=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||||
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
390
localizer.go
Normal file
390
localizer.go
Normal file
|
@ -0,0 +1,390 @@
|
||||||
|
package cobra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/toml"
|
||||||
|
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
type i18nCommandGlossary struct {
|
||||||
|
SectionUsage string
|
||||||
|
SectionAliases string
|
||||||
|
SectionExamples string
|
||||||
|
SectionAvailableCommands string
|
||||||
|
SectionAdditionalCommands string
|
||||||
|
SectionFlags string
|
||||||
|
SectionGlobalFlags string
|
||||||
|
SectionAdditionalHelpTopics string
|
||||||
|
Use string
|
||||||
|
ForInfoAboutCommand string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommandUsageTemplateData struct {
|
||||||
|
*Command
|
||||||
|
I18n *i18nCommandGlossary
|
||||||
|
}
|
||||||
|
|
||||||
|
var commonCommandGlossary *i18nCommandGlossary
|
||||||
|
|
||||||
|
func getCommandGlossary() *i18nCommandGlossary {
|
||||||
|
if commonCommandGlossary == nil {
|
||||||
|
commonCommandGlossary = &i18nCommandGlossary{
|
||||||
|
SectionUsage: i18nSectionUsage(),
|
||||||
|
SectionAliases: i18nSectionAliases(),
|
||||||
|
SectionExamples: i18nSectionExamples(),
|
||||||
|
SectionAvailableCommands: i18nSectionAvailableCommands(),
|
||||||
|
SectionAdditionalCommands: i18nSectionAdditionalCommands(),
|
||||||
|
SectionFlags: i18nSectionFlags(),
|
||||||
|
SectionGlobalFlags: i18nSectionGlobalFlags(),
|
||||||
|
SectionAdditionalHelpTopics: i18nSectionAdditionalHelpTopics(),
|
||||||
|
Use: i18nUse(),
|
||||||
|
ForInfoAboutCommand: i18nForInfoAboutCommand(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return commonCommandGlossary
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nSectionUsage() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "SectionUsage",
|
||||||
|
Description: "title of the section in the usage template",
|
||||||
|
Other: "Usage",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nSectionAliases() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "SectionAliases",
|
||||||
|
Description: "title of the section in the usage template",
|
||||||
|
Other: "Aliases",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nSectionExamples() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "SectionExamples",
|
||||||
|
Description: "title of the section in the usage template",
|
||||||
|
Other: "Examples",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nSectionAvailableCommands() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "SectionAvailableCommands",
|
||||||
|
Description: "title of the section in the usage template",
|
||||||
|
Other: "Available Commands",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nSectionAdditionalCommands() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "SectionAdditionalCommands",
|
||||||
|
Description: "title of the section in the usage template",
|
||||||
|
Other: "Additional Commands",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nSectionFlags() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "SectionFlags",
|
||||||
|
Description: "title of the section in the usage template",
|
||||||
|
Other: "Flags",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nSectionGlobalFlags() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "SectionGlobalFlags",
|
||||||
|
Description: "title of the section in the usage template",
|
||||||
|
Other: "Global Flags",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nSectionAdditionalHelpTopics() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "SectionAdditionalHelpTopics",
|
||||||
|
Description: "title of the section in the usage template",
|
||||||
|
Other: "Additional Help Topics",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nUse() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "Use",
|
||||||
|
Description: "beginning of a sentence like 'Use <this> to do <that>'",
|
||||||
|
Other: "Use",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nForInfoAboutCommand() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "ForInfoAboutCommand",
|
||||||
|
Description: "end of a sentence",
|
||||||
|
Other: "for more information about a command",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nCommand() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "Command",
|
||||||
|
Description: "lowercase",
|
||||||
|
Other: "command",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nPathToCommand() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "PathToCommand",
|
||||||
|
Description: "lowercase",
|
||||||
|
Other: "path to command",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nCommandHelpShort() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "CommandHelpShort",
|
||||||
|
Description: "short help for command help",
|
||||||
|
Other: "Help about any command",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nCommandHelpLong() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "CommandHelpLong",
|
||||||
|
Description: "long help for command help (cmd example)",
|
||||||
|
Other: `Help provides help for any command in the application.
|
||||||
|
Simply type %s for full details.`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nCommandHelpUnknownTopicError() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "CommandHelpUnknownTopicError",
|
||||||
|
Description: "shown when help topic is unknown (args)",
|
||||||
|
Other: "Unknown help topic %#q",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nHelpFor() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "HelpFor",
|
||||||
|
Description: "lowercase, beginning of sentence",
|
||||||
|
Other: "help for",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nVersionFor() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "VersionFor",
|
||||||
|
Description: "lowercase, beginning of sentence",
|
||||||
|
Other: "version for",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nThisCommand() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "ThisCommand",
|
||||||
|
Description: "lowercase, end of sentence, used when command name is undefined",
|
||||||
|
Other: "this command",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nDidYouMeanThis() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "DidYouMeanThis",
|
||||||
|
Description: "shown as suggestion",
|
||||||
|
Other: "Did you mean this?",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nCommandDeprecatedWarning() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "CommandDeprecatedWarning",
|
||||||
|
Description: "printed when a deprecated command is executed (cmd, deprecation message)",
|
||||||
|
Other: "Command %q is deprecated, %s",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nError() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "Error",
|
||||||
|
Description: "prefix of error messages",
|
||||||
|
Other: "Error",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nLegacyArgsValidationError() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "LegacyArgsValidationError",
|
||||||
|
Description: "error shown when args are not understood (subcmd, cmd, suggestion)",
|
||||||
|
Other: "unknown command %q for %q%s",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nNoArgsValidationError() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "NoArgsValidationError",
|
||||||
|
Description: "error shown when args are present but should not (subcmd, cmd)",
|
||||||
|
Other: "unknown command %q for %q",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nOnlyValidArgsValidationError() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "OnlyValidArgsValidationError",
|
||||||
|
Description: "error shown when arg is invalid (arg, cmd, suggestion)",
|
||||||
|
Other: "invalid argument %q for %q%s",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nMinimumNArgsValidationError(amountRequired int) string {
|
||||||
|
return localizeMessageWithPlural(&i18n.Message{
|
||||||
|
ID: "MinimumNArgsValidationError",
|
||||||
|
Description: "error shown when arg count is too low (expected amount, actual amount)",
|
||||||
|
Other: "requires at least %d args, only received %d",
|
||||||
|
One: "requires at least %d arg, only received %d",
|
||||||
|
}, amountRequired)
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nMaximumNArgsValidationError(amountRequired int) string {
|
||||||
|
return localizeMessageWithPlural(&i18n.Message{
|
||||||
|
ID: "MaximumNArgsValidationError",
|
||||||
|
Description: "error shown when arg count is too low (expected amount, actual amount)",
|
||||||
|
Other: "accepts at most %d args, received %d",
|
||||||
|
One: "accepts at most %d arg, received %d",
|
||||||
|
}, amountRequired)
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nExactArgsValidationError(amountRequired int) string {
|
||||||
|
return localizeMessageWithPlural(&i18n.Message{
|
||||||
|
ID: "ExactArgsValidationError",
|
||||||
|
Description: "error shown when arg count is not exact (expected amount, actual amount)",
|
||||||
|
Other: "accepts %d args, received %d",
|
||||||
|
One: "accepts %d arg, received %d",
|
||||||
|
}, amountRequired)
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nRangeArgsValidationError(amountMax int) string {
|
||||||
|
return localizeMessageWithPlural(&i18n.Message{
|
||||||
|
ID: "RangeArgsValidationError",
|
||||||
|
Description: "error shown when arg count is not in range (expected min, expected max, actual amount)",
|
||||||
|
Other: "accepts between %d and %d args, received %d",
|
||||||
|
One: "accepts between %d and %d arg, received %d",
|
||||||
|
}, amountMax)
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nRunHelpTip() string {
|
||||||
|
return localizeMessage(&i18n.Message{
|
||||||
|
ID: "RunHelpTip",
|
||||||
|
Description: "tip shown when a command fails (command path)",
|
||||||
|
Other: "Run '%v --help' for usage.",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func i18nFlagNotSetError(amountFlags int) string {
|
||||||
|
return localizeMessageWithPlural(&i18n.Message{
|
||||||
|
ID: "FlagNotSetError",
|
||||||
|
Description: "error shown when required flags are not set (flags)",
|
||||||
|
Other: "required flags \"%s\" are not set",
|
||||||
|
One: "required flag \"%s\" is not set",
|
||||||
|
}, amountFlags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// … 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 localizeMessageWithPlural(message *i18n.Message, pluralCount int) string {
|
||||||
|
localizedValue, err := localizer.Localize(&i18n.LocalizeConfig{
|
||||||
|
PluralCount: pluralCount,
|
||||||
|
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/active.%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()
|
||||||
|
*langs = append(*langs, langString)
|
||||||
|
|
||||||
|
langBase, confidentInBase := lang.Base()
|
||||||
|
if confidentInBase != language.No {
|
||||||
|
*langs = append(*langs, langBase.String())
|
||||||
|
*langs = append(*langs, langBase.ISO3())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupLocalizer() {
|
||||||
|
bundle := i18n.NewBundle(defaultLanguage)
|
||||||
|
bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)
|
||||||
|
detectedLangs := detectLangs()
|
||||||
|
//fmt.Println("Detected languages", detectedLangs)
|
||||||
|
loadTranslationFiles(bundle, detectedLangs)
|
||||||
|
localizer = i18n.NewLocalizer(bundle, detectedLangs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
setupLocalizer() // FIXME: perhaps hook this somewhere else? (not init)
|
||||||
|
}
|
128
translations/active.en.toml
Normal file
128
translations/active.en.toml
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
[Command]
|
||||||
|
description = "lowercase"
|
||||||
|
other = "command"
|
||||||
|
|
||||||
|
[CommandDeprecatedWarning]
|
||||||
|
description = "printed when a deprecated command is executed (cmd, deprecation message)"
|
||||||
|
other = "Command %q is deprecated, %s"
|
||||||
|
|
||||||
|
[CommandHelpLong]
|
||||||
|
description = "long help for command help (cmd example)"
|
||||||
|
other = "Help provides help for any command in the application.\nSimply type %s for full details."
|
||||||
|
|
||||||
|
[CommandHelpShort]
|
||||||
|
description = "short help for command help"
|
||||||
|
other = "Help about any command"
|
||||||
|
|
||||||
|
[CommandHelpUnknownTopicError]
|
||||||
|
description = "shown when help topic is unknown (args)"
|
||||||
|
other = "Unknown help topic %#q"
|
||||||
|
|
||||||
|
[DidYouMeanThis]
|
||||||
|
description = "shown as suggestion"
|
||||||
|
other = "Did you mean this?"
|
||||||
|
|
||||||
|
[Error]
|
||||||
|
description = "prefix of error messages"
|
||||||
|
other = "Error"
|
||||||
|
|
||||||
|
[ExactArgsValidationError]
|
||||||
|
description = "error shown when arg count is not exact (expected amount, actual amount)"
|
||||||
|
one = "accepts %d arg, received %d"
|
||||||
|
other = "accepts %d args, received %d"
|
||||||
|
|
||||||
|
[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"
|
||||||
|
|
||||||
|
[FlagNotSetError]
|
||||||
|
description = "error shown when required flags are not set (flags)"
|
||||||
|
one = "required flag \"%s\" is not set"
|
||||||
|
other = "required flags \"%s\" are not set"
|
||||||
|
|
||||||
|
[ForInfoAboutCommand]
|
||||||
|
description = "end of a sentence"
|
||||||
|
other = "for more information about a command"
|
||||||
|
|
||||||
|
[HelpFor]
|
||||||
|
description = "lowercase, beginning of sentence"
|
||||||
|
other = "help for"
|
||||||
|
|
||||||
|
[LegacyArgsValidationError]
|
||||||
|
description = "error shown when args are not understood (subcmd, cmd, suggestion)"
|
||||||
|
other = "unknown command %q for %q%s"
|
||||||
|
|
||||||
|
[MaximumNArgsValidationError]
|
||||||
|
description = "error shown when arg count is too low (expected amount, actual amount)"
|
||||||
|
one = "accepts at most %d arg, received %d"
|
||||||
|
other = "accepts at most %d args, received %d"
|
||||||
|
|
||||||
|
[MinimumNArgsValidationError]
|
||||||
|
description = "error shown when arg count is too low (expected amount, actual amount)"
|
||||||
|
one = "requires at least %d arg, only received %d"
|
||||||
|
other = "requires at least %d args, only received %d"
|
||||||
|
|
||||||
|
[NoArgsValidationError]
|
||||||
|
description = "error shown when args are present but should not (subcmd, cmd)"
|
||||||
|
other = "unknown command %q for %q"
|
||||||
|
|
||||||
|
[OnlyValidArgsValidationError]
|
||||||
|
description = "error shown when arg is invalid (arg, cmd, suggestion)"
|
||||||
|
other = "invalid argument %q for %q%s"
|
||||||
|
|
||||||
|
[PathToCommand]
|
||||||
|
description = "lowercase"
|
||||||
|
other = "path to command"
|
||||||
|
|
||||||
|
[RangeArgsValidationError]
|
||||||
|
description = "error shown when arg count is not in range (expected min, expected max, actual amount)"
|
||||||
|
one = "accepts between %d and %d arg, received %d"
|
||||||
|
other = "accepts between %d and %d args, received %d"
|
||||||
|
|
||||||
|
[RunHelpTip]
|
||||||
|
description = "tip shown when a command fails (command path)"
|
||||||
|
other = "Run '%v --help' for usage."
|
||||||
|
|
||||||
|
[SectionAdditionalCommands]
|
||||||
|
description = "title of the section in the usage template"
|
||||||
|
other = "Additional Commands"
|
||||||
|
|
||||||
|
[SectionAdditionalHelpTopics]
|
||||||
|
description = "title of the section in the usage template"
|
||||||
|
other = "Additional Help Topics"
|
||||||
|
|
||||||
|
[SectionAliases]
|
||||||
|
description = "title of the section in the usage template"
|
||||||
|
other = "Aliases"
|
||||||
|
|
||||||
|
[SectionAvailableCommands]
|
||||||
|
description = "title of the section in the usage template"
|
||||||
|
other = "Available Commands"
|
||||||
|
|
||||||
|
[SectionExamples]
|
||||||
|
description = "title of the section in the usage template"
|
||||||
|
other = "Examples"
|
||||||
|
|
||||||
|
[SectionFlags]
|
||||||
|
description = "title of the section in the usage template"
|
||||||
|
other = "Flags"
|
||||||
|
|
||||||
|
[SectionGlobalFlags]
|
||||||
|
description = "title of the section in the usage template"
|
||||||
|
other = "Global Flags"
|
||||||
|
|
||||||
|
[SectionUsage]
|
||||||
|
description = "title of the section in the usage template"
|
||||||
|
other = "Usage"
|
||||||
|
|
||||||
|
[ThisCommand]
|
||||||
|
description = "lowercase, end of sentence, used when command name is undefined"
|
||||||
|
other = "this command"
|
||||||
|
|
||||||
|
[Use]
|
||||||
|
description = "beginning of a sentence like 'Use <this> to do <that>'"
|
||||||
|
other = "Use"
|
||||||
|
|
||||||
|
[VersionFor]
|
||||||
|
description = "lowercase, beginning of sentence"
|
||||||
|
other = "version for"
|
159
translations/active.fr.toml
Normal file
159
translations/active.fr.toml
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
[Command]
|
||||||
|
description = "lowercase"
|
||||||
|
hash = "sha1-c90bde9bb93ee6452afbe579d55cd32ba0159c8d"
|
||||||
|
other = "commande"
|
||||||
|
|
||||||
|
[CommandDeprecatedWarning]
|
||||||
|
description = "printed when a deprecated command is executed (cmd, deprecation message)"
|
||||||
|
hash = "sha1-e8b974a255eafe86d4a4fe15a0e79aaab345354c"
|
||||||
|
other = "La commande %q est dépréciée, %s"
|
||||||
|
|
||||||
|
[CommandHelpLong]
|
||||||
|
description = "long help for command help (cmd example)"
|
||||||
|
hash = "sha1-dc993d9f12d54910147c477318fa82c8210b6669"
|
||||||
|
other = "Help fournit de l'aide pour n'importe quelle commande de l'application.\nTapez '%s' pour obtenir une aide détaillée."
|
||||||
|
|
||||||
|
[CommandHelpShort]
|
||||||
|
description = "short help for command help"
|
||||||
|
hash = "sha1-5bab4a463f36ea8e33681f523445bbd614e9891c"
|
||||||
|
other = "Obtenir de l'aide au sujet d'une commande"
|
||||||
|
|
||||||
|
[CommandHelpUnknownTopicError]
|
||||||
|
description = "shown when help topic is unknown (args)"
|
||||||
|
hash = "sha1-d3b6cb615eee5770e0e8e62fbf7a3293502dd7a8"
|
||||||
|
other = "Sujet d'aide %#q inconnu"
|
||||||
|
|
||||||
|
[DidYouMeanThis]
|
||||||
|
description = "shown as suggestion"
|
||||||
|
hash = "sha1-eb9504fe384499caafe690853204c7fb7ac8b531"
|
||||||
|
other = "Vouliez-vous dire ceci ?"
|
||||||
|
|
||||||
|
[Error]
|
||||||
|
description = "prefix of error messages"
|
||||||
|
hash = "sha1-7dcb56355a3ddc7ff7e5ccd6522507999ca7f238"
|
||||||
|
other = "Erreur"
|
||||||
|
|
||||||
|
[ExactArgsValidationError]
|
||||||
|
description = "error shown when arg count is not exact (expected amount, actual amount)"
|
||||||
|
hash = "sha1-207d771f1d5dc4ed5c4094dcd29a4c80e31a6260"
|
||||||
|
one = "accepte %d arg, mais en a reçu %d"
|
||||||
|
other = "accepte %d args, mais en en reçu %d"
|
||||||
|
|
||||||
|
[ExclusiveFlagsValidationError]
|
||||||
|
description = "error shown when multiple exclusive flags are provided (group flags, offending flags)"
|
||||||
|
hash = "sha1-221b98bada52cfc2932f9aa5142b653b46baded6"
|
||||||
|
other = "les options [%v] sont exclusives, mais les options %v ont été fournies"
|
||||||
|
|
||||||
|
[FlagNotSetError]
|
||||||
|
description = "error shown when required flags are not set (flags)"
|
||||||
|
hash = "sha1-f5340c4bfceb0066ec70944b57e6458b6f8cc97b"
|
||||||
|
one = "l'option requise \"%s\" n'est pas présente"
|
||||||
|
other = "les options requises \"%s\" ne sont pas présentes"
|
||||||
|
|
||||||
|
[ForInfoAboutCommand]
|
||||||
|
description = "end of a sentence"
|
||||||
|
hash = "sha1-923f6d48908b3a6981fb5504b0e5078700a312c0"
|
||||||
|
other = "pour plus d'information au sujet d'une commande"
|
||||||
|
|
||||||
|
[HelpFor]
|
||||||
|
description = "lowercase, beginning of sentence"
|
||||||
|
hash = "sha1-18fbe8d2780935dbe6a71f998821c22f283cbd25"
|
||||||
|
other = "aide pour"
|
||||||
|
|
||||||
|
[LegacyArgsValidationError]
|
||||||
|
description = "error shown when args are not understood (subcmd, cmd, suggestion)"
|
||||||
|
hash = "sha1-c601c68bdcb9687109e793112b789b1858953b15"
|
||||||
|
other = "commande %q inconnue pour %q%s"
|
||||||
|
|
||||||
|
[MaximumNArgsValidationError]
|
||||||
|
description = "error shown when arg count is too low (expected amount, actual amount)"
|
||||||
|
hash = "sha1-86d71b8bc054ea8aad7006808bc9d96551052ab4"
|
||||||
|
one = "accepte au plus %d arg, mais en a reçu %d"
|
||||||
|
other = "accepte au plus %d args, mais en a reçu %d"
|
||||||
|
|
||||||
|
[MinimumNArgsValidationError]
|
||||||
|
description = "error shown when arg count is too low (expected amount, actual amount)"
|
||||||
|
hash = "sha1-7cc9093bf167d6f0601dd0763a8d3f10d71c8889"
|
||||||
|
one = "requiert au moins %d arg, mais en a reçu %d"
|
||||||
|
other = "requiert au moins %d args, mais en a reçu %d"
|
||||||
|
|
||||||
|
[NoArgsValidationError]
|
||||||
|
description = "error shown when args are present but should not (subcmd, cmd)"
|
||||||
|
hash = "sha1-551d8d237dc2ab9a853fcfbe7ef85318a0f78720"
|
||||||
|
other = "commande %q inconnue pour %q"
|
||||||
|
|
||||||
|
[OnlyValidArgsValidationError]
|
||||||
|
description = "error shown when arg is invalid (arg, cmd, suggestion)"
|
||||||
|
hash = "sha1-60b40e5782dd252c78ef3d585065cb99197ec22e"
|
||||||
|
other = "argument %q invalide pour %q%s"
|
||||||
|
|
||||||
|
[PathToCommand]
|
||||||
|
description = "lowercase"
|
||||||
|
hash = "sha1-7dcca04d5dcb7ff289a5483bea3e4f1e4119c1f4"
|
||||||
|
other = "commande"
|
||||||
|
|
||||||
|
[RangeArgsValidationError]
|
||||||
|
description = "error shown when arg count is not in range (expected min, expected max, actual amount)"
|
||||||
|
hash = "sha1-aa81e0aee17a3439b479cdf47169eb194706cd14"
|
||||||
|
one = "accepte entre %d et %d arg, mais en a reçu %d"
|
||||||
|
other = "accepte entre %d et %d args, mais en a reçu %d"
|
||||||
|
|
||||||
|
[RunHelpTip]
|
||||||
|
description = "tip shown when a command fails (command path)"
|
||||||
|
hash = "sha1-e1d2c4cccd484df365c3249347d5172981929b88"
|
||||||
|
other = "Essayez '%v --help' pour obtenir de l'aide."
|
||||||
|
|
||||||
|
[SectionAdditionalCommands]
|
||||||
|
description = "title of the section in the usage template"
|
||||||
|
hash = "sha1-730484f22c7ce0885ca13a3b8222b231dc29a4a9"
|
||||||
|
other = "Commandes Connexes"
|
||||||
|
|
||||||
|
[SectionAdditionalHelpTopics]
|
||||||
|
description = "title of the section in the usage template"
|
||||||
|
hash = "sha1-615c9551f4dcd30836fbe04506e0b807c59dd901"
|
||||||
|
other = "Autres Sujets"
|
||||||
|
|
||||||
|
[SectionAliases]
|
||||||
|
description = "title of the section in the usage template"
|
||||||
|
hash = "sha1-3c66c036f454cb3d23f757c430a5c05b148dc01f"
|
||||||
|
other = "Alias"
|
||||||
|
|
||||||
|
[SectionAvailableCommands]
|
||||||
|
description = "title of the section in the usage template"
|
||||||
|
hash = "sha1-fc33c38777373e51c9c84c8594efb5de4df0f23e"
|
||||||
|
other = "Commandes Disponibles"
|
||||||
|
|
||||||
|
[SectionExamples]
|
||||||
|
description = "title of the section in the usage template"
|
||||||
|
hash = "sha1-6e2f0c7dddc3ce6aacb1184b3b21694492d6595a"
|
||||||
|
other = "Exemples"
|
||||||
|
|
||||||
|
[SectionFlags]
|
||||||
|
description = "title of the section in the usage template"
|
||||||
|
hash = "sha1-5513ed25ed4e1ad2e569ff4c963bba347320e9f7"
|
||||||
|
other = "Options"
|
||||||
|
|
||||||
|
[SectionGlobalFlags]
|
||||||
|
description = "title of the section in the usage template"
|
||||||
|
hash = "sha1-705f1ebcdce1374e23f41bc8e0e14e5197ac1742"
|
||||||
|
other = "Options Globales"
|
||||||
|
|
||||||
|
[SectionUsage]
|
||||||
|
description = "title of the section in the usage template"
|
||||||
|
hash = "sha1-5fdae7bd171315a453063384887ee4300363cf20"
|
||||||
|
other = "Usage"
|
||||||
|
|
||||||
|
[ThisCommand]
|
||||||
|
description = "lowercase, end of sentence, used when command name is undefined"
|
||||||
|
hash = "sha1-5cfa4629fdf18b89471b3d07a0d051a8a10afe70"
|
||||||
|
other = "cette commande"
|
||||||
|
|
||||||
|
[Use]
|
||||||
|
description = "beginning of a sentence like 'Use <this> to do <that>'"
|
||||||
|
hash = "sha1-f42c92acc9934eec952161dfa338a95ec0f02b75"
|
||||||
|
other = "Utilisez"
|
||||||
|
|
||||||
|
[VersionFor]
|
||||||
|
description = "lowercase, beginning of sentence"
|
||||||
|
hash = "sha1-e7f03028ab18076d111cd3343a24bbd8113d5431"
|
||||||
|
other = "version de"
|
Loading…
Add table
Reference in a new issue