mirror of
https://github.com/spf13/cobra
synced 2024-11-24 14:47:12 +00:00
feat(i18n): implement localization using gettext files
- Recipe to extract new translations from the Go code: `make i18n_extract` - Embedded `MO` files - Detect language from environment variables - Some strings were pluralized
This commit is contained in:
parent
236f3c0418
commit
ab14efa558
18 changed files with 954 additions and 50 deletions
6
Makefile
6
Makefile
|
@ -21,7 +21,7 @@ lint:
|
||||||
|
|
||||||
test: install_deps
|
test: install_deps
|
||||||
$(info ******************** running tests ********************)
|
$(info ******************** running tests ********************)
|
||||||
go test -v ./...
|
LANGUAGE="en" go test -v ./...
|
||||||
|
|
||||||
richtest: install_deps
|
richtest: install_deps
|
||||||
$(info ******************** running tests with kyoh86/richgo ********************)
|
$(info ******************** running tests with kyoh86/richgo ********************)
|
||||||
|
@ -33,3 +33,7 @@ install_deps:
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BIN)
|
rm -rf $(BIN)
|
||||||
|
|
||||||
|
i18n_extract:
|
||||||
|
$(info ******************** extracting translation files ********************)
|
||||||
|
xgotext -v -in . -out locales
|
||||||
|
|
15
args.go
15
args.go
|
@ -16,6 +16,7 @@ package cobra
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/leonelquinteros/gotext"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -33,7 +34,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(gotext.Get("LegacyArgsValidationError"), args[0], cmd.CommandPath(), cmd.findSuggestions(args[0]))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -41,7 +42,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(gotext.Get("NoArgsValidationError"), args[0], cmd.CommandPath())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -58,7 +59,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(gotext.Get("OnlyValidArgsValidationError"), v, cmd.CommandPath(), cmd.findSuggestions(args[0]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,7 +75,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(gotext.GetN("MinimumNArgsValidationError", "MinimumNArgsValidationErrorPlural", n), n, len(args))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -84,7 +85,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(gotext.GetN("MaximumNArgsValidationError", "MaximumNArgsValidationErrorPlural", n), n, len(args))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -94,7 +95,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(gotext.GetN("ExactArgsValidationError", "ExactArgsValidationErrorPlural", n), n, len(args))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -104,7 +105,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(gotext.GetN("RangeArgsValidationError", "RangeArgsValidationErrorPlural", 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)
|
||||||
}
|
}
|
||||||
|
|
3
cobra.go
3
cobra.go
|
@ -19,6 +19,7 @@ package cobra
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/leonelquinteros/gotext"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -230,7 +231,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, gotext.Get("Error")+":", msg)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
56
command.go
56
command.go
|
@ -21,6 +21,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/leonelquinteros/gotext"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -44,6 +45,12 @@ type Group struct {
|
||||||
Title string
|
Title string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommandUsageTemplateData is the data passed to the template of command usage
|
||||||
|
type CommandUsageTemplateData struct {
|
||||||
|
*Command
|
||||||
|
I18n *i18nCommandGlossary
|
||||||
|
}
|
||||||
|
|
||||||
// Command is just that, a command for your application.
|
// Command is just that, a command for your application.
|
||||||
// E.g. 'go run ...' - 'run' is the command. Cobra requires
|
// E.g. 'go run ...' - 'run' is the command. Cobra requires
|
||||||
// you to define the usage and description as part of your command
|
// you to define the usage and description as part of your command
|
||||||
|
@ -432,7 +439,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 +560,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 +767,7 @@ func (c *Command) findSuggestions(arg string) string {
|
||||||
}
|
}
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
if suggestions := c.SuggestionsFor(arg); len(suggestions) > 0 {
|
if suggestions := c.SuggestionsFor(arg); len(suggestions) > 0 {
|
||||||
sb.WriteString("\n\nDid you mean this?\n")
|
sb.WriteString("\n\n" + gotext.Get("DidYouMeanThis") + "\n")
|
||||||
for _, s := range suggestions {
|
for _, s := range suggestions {
|
||||||
_, _ = fmt.Fprintf(&sb, "\t%v\n", s)
|
_, _ = fmt.Fprintf(&sb, "\t%v\n", s)
|
||||||
}
|
}
|
||||||
|
@ -877,7 +888,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(gotext.Get("CommandDeprecatedWarning")+"\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 +1107,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(gotext.Get("RunHelpTip")+"\n", c.CommandPath())
|
||||||
}
|
}
|
||||||
return c, err
|
return c, err
|
||||||
}
|
}
|
||||||
|
@ -1162,7 +1173,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(gotext.GetN("FlagNotSetError", "FlagNotSetErrorPlural", len(missingFlagNames)), strings.Join(missingFlagNames, `", "`))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1186,9 +1197,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 := gotext.Get("HelpFor") + " "
|
||||||
if c.Name() == "" {
|
if c.Name() == "" {
|
||||||
usage += "this command"
|
usage += gotext.Get("ThisCommand")
|
||||||
} else {
|
} else {
|
||||||
usage += c.Name()
|
usage += c.Name()
|
||||||
}
|
}
|
||||||
|
@ -1208,9 +1219,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 := gotext.Get("VersionFor") + " "
|
||||||
if c.Name() == "" {
|
if c.Name() == "" {
|
||||||
usage += "this command"
|
usage += gotext.Get("ThisCommand")
|
||||||
} else {
|
} else {
|
||||||
usage += c.Name()
|
usage += c.Name()
|
||||||
}
|
}
|
||||||
|
@ -1233,10 +1244,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]", gotext.Get("command")),
|
||||||
Short: "Help about any command",
|
Short: gotext.Get("CommandHelpShort"),
|
||||||
Long: `Help provides help for any command in the application.
|
Long: fmt.Sprintf(gotext.Get("CommandHelpLong"), c.Name()+fmt.Sprintf(" help [%s]", gotext.Get("command"))),
|
||||||
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 +1269,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(gotext.Get("CommandHelpUnknownTopicError")+"\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 {
|
||||||
|
|
|
@ -16,6 +16,7 @@ package cobra
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/leonelquinteros/gotext"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -48,7 +49,7 @@ type flagCompError struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *flagCompError) Error() string {
|
func (e *flagCompError) Error() string {
|
||||||
return "Subcommand '" + e.subCommand + "' does not support flag '" + e.flagName + "'"
|
return fmt.Sprintf(gotext.Get("CompletionSubcommandUnsupportedFlagError"), e.subCommand, e.flagName)
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -97,7 +98,6 @@ const (
|
||||||
// Constants for the completion command
|
// Constants for the completion command
|
||||||
compCmdName = "completion"
|
compCmdName = "completion"
|
||||||
compCmdNoDescFlagName = "no-descriptions"
|
compCmdNoDescFlagName = "no-descriptions"
|
||||||
compCmdNoDescFlagDesc = "disable completion descriptions"
|
|
||||||
compCmdNoDescFlagDefault = false
|
compCmdNoDescFlagDefault = false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -199,9 +199,8 @@ func (c *Command) initCompleteCmd(args []string) {
|
||||||
Hidden: true,
|
Hidden: true,
|
||||||
DisableFlagParsing: true,
|
DisableFlagParsing: true,
|
||||||
Args: MinimumNArgs(1),
|
Args: MinimumNArgs(1),
|
||||||
Short: "Request shell completion choices for the specified command-line",
|
Short: gotext.Get("CompletionCommandShellShort"),
|
||||||
Long: fmt.Sprintf("%[2]s is a special command that is used by the shell completion logic\n%[1]s",
|
Long: fmt.Sprintf(gotext.Get("CompletionCommandShellLong"), ShellCompRequestCmd),
|
||||||
"to request completion choices for the specified command-line.", ShellCompRequestCmd),
|
|
||||||
Run: func(cmd *Command, args []string) {
|
Run: func(cmd *Command, args []string) {
|
||||||
finalCmd, completions, directive, err := cmd.getCompletions(args)
|
finalCmd, completions, directive, err := cmd.getCompletions(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -248,7 +247,7 @@ func (c *Command) initCompleteCmd(args []string) {
|
||||||
|
|
||||||
// Print some helpful info to stderr for the user to understand.
|
// Print some helpful info to stderr for the user to understand.
|
||||||
// Output from stderr must be ignored by the completion script.
|
// Output from stderr must be ignored by the completion script.
|
||||||
fmt.Fprintf(finalCmd.ErrOrStderr(), "Completion ended with directive: %s\n", directive.string())
|
fmt.Fprintf(finalCmd.ErrOrStderr(), fmt.Sprintf(gotext.Get("CompletionCommandShellDirectiveTip"), directive.string())+"\n")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
c.AddCommand(completeCmd)
|
c.AddCommand(completeCmd)
|
||||||
|
@ -742,7 +741,7 @@ You will need to start a new shell for this setup to take effect.
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if haveNoDescFlag {
|
if haveNoDescFlag {
|
||||||
bash.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
|
bash.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, gotext.Get("CompletionSubcommandNoDescFlagDesc"))
|
||||||
}
|
}
|
||||||
|
|
||||||
zsh := &Command{
|
zsh := &Command{
|
||||||
|
@ -781,7 +780,7 @@ You will need to start a new shell for this setup to take effect.
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if haveNoDescFlag {
|
if haveNoDescFlag {
|
||||||
zsh.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
|
zsh.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, gotext.Get("CompletionSubcommandNoDescFlagDesc"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fish := &Command{
|
fish := &Command{
|
||||||
|
@ -806,7 +805,7 @@ You will need to start a new shell for this setup to take effect.
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if haveNoDescFlag {
|
if haveNoDescFlag {
|
||||||
fish.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
|
fish.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, gotext.Get("CompletionSubcommandNoDescFlagDesc"))
|
||||||
}
|
}
|
||||||
|
|
||||||
powershell := &Command{
|
powershell := &Command{
|
||||||
|
@ -832,7 +831,7 @@ to your powershell profile.
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if haveNoDescFlag {
|
if haveNoDescFlag {
|
||||||
powershell.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
|
powershell.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, gotext.Get("CompletionSubcommandNoDescFlagDesc"))
|
||||||
}
|
}
|
||||||
|
|
||||||
completionCmd.AddCommand(bash, zsh, fish, powershell)
|
completionCmd.AddCommand(bash, zsh, fish, powershell)
|
||||||
|
|
|
@ -16,6 +16,7 @@ package cobra
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/leonelquinteros/gotext"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -201,7 +202,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(gotext.Get("ExclusiveFlagsValidationError"), flagList, set)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -1,10 +1,12 @@
|
||||||
module github.com/spf13/cobra
|
module github.com/spf13/cobra
|
||||||
|
|
||||||
go 1.15
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
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/leonelquinteros/gotext v1.5.3-0.20231003122255-12a99145a351
|
||||||
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
|
||||||
)
|
)
|
||||||
|
|
27
go.sum
27
go.sum
|
@ -2,10 +2,37 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0q
|
||||||
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/leonelquinteros/gotext v1.5.3-0.20231003122255-12a99145a351 h1:Rk+RkO4xEZMkEok69CbeA6cgXKyVCsgF3qGGGR46pd8=
|
||||||
|
github.com/leonelquinteros/gotext v1.5.3-0.20231003122255-12a99145a351/go.mod h1:qQRISjoonXYFdRGrTG1LARQ38Gpibad0IPeB4hpvyyM=
|
||||||
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.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
|
38
locales/README.md
Normal file
38
locales/README.md
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# Locales
|
||||||
|
|
||||||
|
Localization uses embedded _gettext_ files, defaulting to English
|
||||||
|
when locale cannot be guessed from environment variables.
|
||||||
|
|
||||||
|
|
||||||
|
## Development Flow
|
||||||
|
|
||||||
|
1. Add calls to `gotext.Get(…)` somewhere in the codebase
|
||||||
|
2. Run `make i18n_extract`
|
||||||
|
3. Update the `PO` files with some software like [Poedit]
|
||||||
|
4. Make sure your software has also updated the `MO` files
|
||||||
|
|
||||||
|
[Poedit]: https://poedit.net/
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
### POT files
|
||||||
|
|
||||||
|
The `*.pot` file(s) are automatically generated by the following command :
|
||||||
|
|
||||||
|
make i18n_extract
|
||||||
|
|
||||||
|
They are named `<domain>.pot`, and when the domain is not specified, it is `default`.
|
||||||
|
|
||||||
|
### PO & MO files
|
||||||
|
|
||||||
|
The actual translation files, in _gettext_ format (`*.po` and `*.mo`), are in the directory `<domain>/`.
|
||||||
|
They are named `<language>.po` and `<language>.mo`.
|
||||||
|
|
||||||
|
The supported `<language>` formats are :
|
||||||
|
- [ISO 639-3](https://fr.wikipedia.org/wiki/ISO_639-3) _(eg: eng, fra, …)_
|
||||||
|
- [BCP 47](https://fr.wiktionary.org/wiki/Wiktionnaire:BCP_47/language-2) _(eg: en, fr, …)_
|
||||||
|
|
||||||
|
The `*.po` files are plain text, and are the authoritative sources of translations.
|
||||||
|
|
||||||
|
The `*.mo` files are the ones actually packaged in cobra as embedded files, because they are smaller.
|
||||||
|
|
163
locales/default.pot
Normal file
163
locales/default.pot
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Language: \n"
|
||||||
|
"X-Generator: xgotext\n"
|
||||||
|
|
||||||
|
#: command.go:891
|
||||||
|
msgid "CommandDeprecatedWarning"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: command.go:1249
|
||||||
|
msgid "CommandHelpLong"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: command.go:1248
|
||||||
|
msgid "CommandHelpShort"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: command.go:1272
|
||||||
|
msgid "CommandHelpUnknownTopicError"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: completions.go:250
|
||||||
|
msgid "CompletionCommandShellDirectiveTip"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: completions.go:203
|
||||||
|
msgid "CompletionCommandShellLong"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: completions.go:202
|
||||||
|
msgid "CompletionCommandShellShort"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: completions.go:745
|
||||||
|
#: completions.go:784
|
||||||
|
#: completions.go:809
|
||||||
|
#: completions.go:835
|
||||||
|
msgid "CompletionSubcommandNoDescFlagDesc"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: completions.go:52
|
||||||
|
msgid "CompletionSubcommandUnsupportedFlagError"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: command.go:770
|
||||||
|
msgid "DidYouMeanThis"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: cobra.go:234
|
||||||
|
msgid "Error"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: args.go:98
|
||||||
|
msgid "ExactArgsValidationError"
|
||||||
|
msgid_plural "ExactArgsValidationErrorPlural"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
#: flag_groups.go:205
|
||||||
|
msgid "ExclusiveFlagsValidationError"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: command.go:1176
|
||||||
|
msgid "FlagNotSetError"
|
||||||
|
msgid_plural "FlagNotSetErrorPlural"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
#: localizer.go:66
|
||||||
|
msgid "ForInfoAboutCommand"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: command.go:1200
|
||||||
|
msgid "HelpFor"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: args.go:37
|
||||||
|
msgid "LegacyArgsValidationError"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: args.go:88
|
||||||
|
msgid "MaximumNArgsValidationError"
|
||||||
|
msgid_plural "MaximumNArgsValidationErrorPlural"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
#: args.go:78
|
||||||
|
msgid "MinimumNArgsValidationError"
|
||||||
|
msgid_plural "MinimumNArgsValidationErrorPlural"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
#: args.go:45
|
||||||
|
msgid "NoArgsValidationError"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: args.go:62
|
||||||
|
msgid "OnlyValidArgsValidationError"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: args.go:108
|
||||||
|
msgid "RangeArgsValidationError"
|
||||||
|
msgid_plural "RangeArgsValidationErrorPlural"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
#: command.go:1110
|
||||||
|
msgid "RunHelpTip"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: localizer.go:61
|
||||||
|
msgid "SectionAdditionalCommands"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: localizer.go:64
|
||||||
|
msgid "SectionAdditionalHelpTopics"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: localizer.go:58
|
||||||
|
msgid "SectionAliases"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: localizer.go:60
|
||||||
|
msgid "SectionAvailableCommands"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: localizer.go:59
|
||||||
|
msgid "SectionExamples"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: localizer.go:62
|
||||||
|
msgid "SectionFlags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: localizer.go:63
|
||||||
|
msgid "SectionGlobalFlags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: localizer.go:57
|
||||||
|
msgid "SectionUsage"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: command.go:1202
|
||||||
|
#: command.go:1224
|
||||||
|
msgid "ThisCommand"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: localizer.go:65
|
||||||
|
msgid "Use"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: command.go:1222
|
||||||
|
msgid "VersionFor"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: command.go:1247
|
||||||
|
#: command.go:1249
|
||||||
|
msgid "command"
|
||||||
|
msgstr ""
|
BIN
locales/default/en.mo
Normal file
BIN
locales/default/en.mo
Normal file
Binary file not shown.
172
locales/default/en.po
Normal file
172
locales/default/en.po
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: \n"
|
||||||
|
"POT-Creation-Date: \n"
|
||||||
|
"PO-Revision-Date: \n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"Language: en\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Generator: Poedit 3.0.1\n"
|
||||||
|
|
||||||
|
#: command.go:891
|
||||||
|
msgid "CommandDeprecatedWarning"
|
||||||
|
msgstr "Command %q is deprecated, %s"
|
||||||
|
|
||||||
|
#: command.go:1249
|
||||||
|
msgid "CommandHelpLong"
|
||||||
|
msgstr ""
|
||||||
|
"Help provides help for any command in the application.\n"
|
||||||
|
"Simply type %s for full details."
|
||||||
|
|
||||||
|
#: command.go:1248
|
||||||
|
msgid "CommandHelpShort"
|
||||||
|
msgstr "Help about any command"
|
||||||
|
|
||||||
|
#: command.go:1272
|
||||||
|
msgid "CommandHelpUnknownTopicError"
|
||||||
|
msgstr "Unknown help topic %#q"
|
||||||
|
|
||||||
|
#: completions.go:250
|
||||||
|
msgid "CompletionCommandShellDirectiveTip"
|
||||||
|
msgstr "Completion ended with directive: %s"
|
||||||
|
|
||||||
|
#: completions.go:203
|
||||||
|
msgid "CompletionCommandShellLong"
|
||||||
|
msgstr ""
|
||||||
|
"%s is a special command that is used by the shell completion logic\n"
|
||||||
|
"to request completion choices for the specified command-line."
|
||||||
|
|
||||||
|
#: completions.go:202
|
||||||
|
msgid "CompletionCommandShellShort"
|
||||||
|
msgstr "Request shell completion choices for the specified command-line"
|
||||||
|
|
||||||
|
#: completions.go:745 completions.go:784 completions.go:809 completions.go:835
|
||||||
|
msgid "CompletionSubcommandNoDescFlagDesc"
|
||||||
|
msgstr "disable completion descriptions"
|
||||||
|
|
||||||
|
#: completions.go:52
|
||||||
|
msgid "CompletionSubcommandUnsupportedFlagError"
|
||||||
|
msgstr "subcommand '%s' does not support flag '%s'"
|
||||||
|
|
||||||
|
#: command.go:770
|
||||||
|
msgid "DidYouMeanThis"
|
||||||
|
msgstr "Did you mean this?"
|
||||||
|
|
||||||
|
#: cobra.go:234
|
||||||
|
msgid "Error"
|
||||||
|
msgstr "Error"
|
||||||
|
|
||||||
|
#: args.go:98
|
||||||
|
msgid "ExactArgsValidationError"
|
||||||
|
msgid_plural "ExactArgsValidationErrorPlural"
|
||||||
|
msgstr[0] "accepts %d arg, received %d"
|
||||||
|
msgstr[1] "accepts %d args, received %d"
|
||||||
|
|
||||||
|
#: flag_groups.go:205
|
||||||
|
msgid "ExclusiveFlagsValidationError"
|
||||||
|
msgstr ""
|
||||||
|
"if any flags in the group [%v] are set none of the others can be; %v were "
|
||||||
|
"all set"
|
||||||
|
|
||||||
|
#: command.go:1176
|
||||||
|
msgid "FlagNotSetError"
|
||||||
|
msgid_plural "FlagNotSetErrorPlural"
|
||||||
|
msgstr[0] "required flag \"%s\" is not set"
|
||||||
|
msgstr[1] "required flags \"%s\" are not set"
|
||||||
|
|
||||||
|
#: localizer.go:66
|
||||||
|
msgid "ForInfoAboutCommand"
|
||||||
|
msgstr "for more information about a command"
|
||||||
|
|
||||||
|
#: command.go:1200
|
||||||
|
msgid "HelpFor"
|
||||||
|
msgstr "help for"
|
||||||
|
|
||||||
|
#: args.go:37
|
||||||
|
msgid "LegacyArgsValidationError"
|
||||||
|
msgstr "unknown command %q for %q%s"
|
||||||
|
|
||||||
|
#: args.go:88
|
||||||
|
msgid "MaximumNArgsValidationError"
|
||||||
|
msgid_plural "MaximumNArgsValidationErrorPlural"
|
||||||
|
msgstr[0] "accepts at most %d arg, received %d"
|
||||||
|
msgstr[1] "accepts at most %d args, received %d"
|
||||||
|
|
||||||
|
#: args.go:78
|
||||||
|
msgid "MinimumNArgsValidationError"
|
||||||
|
msgid_plural "MinimumNArgsValidationErrorPlural"
|
||||||
|
msgstr[0] "requires at least %d arg, only received %d"
|
||||||
|
msgstr[1] "requires at least %d args, only received %d"
|
||||||
|
|
||||||
|
#: args.go:45
|
||||||
|
msgid "NoArgsValidationError"
|
||||||
|
msgstr "unknown command %q for %q"
|
||||||
|
|
||||||
|
#: args.go:62
|
||||||
|
msgid "OnlyValidArgsValidationError"
|
||||||
|
msgstr "invalid argument %q for %q%s"
|
||||||
|
|
||||||
|
#: args.go:108
|
||||||
|
msgid "RangeArgsValidationError"
|
||||||
|
msgid_plural "RangeArgsValidationErrorPlural"
|
||||||
|
msgstr[0] "accepts between %d and %d arg, received %d"
|
||||||
|
msgstr[1] "accepts between %d and %d args, received %d"
|
||||||
|
|
||||||
|
#: command.go:1110
|
||||||
|
msgid "RunHelpTip"
|
||||||
|
msgstr "Run '%v --help' for usage."
|
||||||
|
|
||||||
|
#: localizer.go:61
|
||||||
|
msgid "SectionAdditionalCommands"
|
||||||
|
msgstr "Additional Commands"
|
||||||
|
|
||||||
|
#: localizer.go:64
|
||||||
|
msgid "SectionAdditionalHelpTopics"
|
||||||
|
msgstr "Additional Help Topics"
|
||||||
|
|
||||||
|
#: localizer.go:58
|
||||||
|
msgid "SectionAliases"
|
||||||
|
msgstr "Aliases"
|
||||||
|
|
||||||
|
#: localizer.go:60
|
||||||
|
msgid "SectionAvailableCommands"
|
||||||
|
msgstr "Available Commands"
|
||||||
|
|
||||||
|
#: localizer.go:59
|
||||||
|
msgid "SectionExamples"
|
||||||
|
msgstr "Examples"
|
||||||
|
|
||||||
|
#: localizer.go:62
|
||||||
|
msgid "SectionFlags"
|
||||||
|
msgstr "Flags"
|
||||||
|
|
||||||
|
#: localizer.go:63
|
||||||
|
msgid "SectionGlobalFlags"
|
||||||
|
msgstr "Global Flags"
|
||||||
|
|
||||||
|
#: localizer.go:57
|
||||||
|
msgid "SectionUsage"
|
||||||
|
msgstr "Usage"
|
||||||
|
|
||||||
|
#: command.go:1202 command.go:1224
|
||||||
|
msgid "ThisCommand"
|
||||||
|
msgstr "this command"
|
||||||
|
|
||||||
|
#: localizer.go:65
|
||||||
|
msgid "Use"
|
||||||
|
msgstr "Use"
|
||||||
|
|
||||||
|
#: command.go:1222
|
||||||
|
msgid "VersionFor"
|
||||||
|
msgstr "version for"
|
||||||
|
|
||||||
|
#: command.go:1247 command.go:1249
|
||||||
|
msgid "command"
|
||||||
|
msgstr "command"
|
||||||
|
|
||||||
|
#~ msgid "PathToCommand"
|
||||||
|
#~ msgstr "path to command"
|
BIN
locales/default/fr.mo
Normal file
BIN
locales/default/fr.mo
Normal file
Binary file not shown.
172
locales/default/fr.po
Normal file
172
locales/default/fr.po
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: \n"
|
||||||
|
"POT-Creation-Date: \n"
|
||||||
|
"PO-Revision-Date: \n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"Language: fr\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||||
|
"X-Generator: Poedit 3.0.1\n"
|
||||||
|
|
||||||
|
#: command.go:891
|
||||||
|
msgid "CommandDeprecatedWarning"
|
||||||
|
msgstr "La commande %q est dépréciée, %s"
|
||||||
|
|
||||||
|
#: command.go:1249
|
||||||
|
msgid "CommandHelpLong"
|
||||||
|
msgstr ""
|
||||||
|
"Help fournit de l'aide pour n'importe quelle commande de l'application.\n"
|
||||||
|
"Tapez '%s' pour obtenir une aide détaillée."
|
||||||
|
|
||||||
|
#: command.go:1248
|
||||||
|
msgid "CommandHelpShort"
|
||||||
|
msgstr "Obtenir de l'aide au sujet d'une commande"
|
||||||
|
|
||||||
|
#: command.go:1272
|
||||||
|
msgid "CommandHelpUnknownTopicError"
|
||||||
|
msgstr "Sujet d'aide %#q inconnu"
|
||||||
|
|
||||||
|
#: completions.go:250
|
||||||
|
msgid "CompletionCommandShellDirectiveTip"
|
||||||
|
msgstr "Auto-complétion achevée par la directive : %s"
|
||||||
|
|
||||||
|
#: completions.go:203
|
||||||
|
msgid "CompletionCommandShellLong"
|
||||||
|
msgstr ""
|
||||||
|
"%s est une commande spéciale utilisée par l'auto-complétion de la console\n"
|
||||||
|
"pour récupérer les différents choix possibles pour une certaine commande."
|
||||||
|
|
||||||
|
#: completions.go:202
|
||||||
|
msgid "CompletionCommandShellShort"
|
||||||
|
msgstr ""
|
||||||
|
"Obtenir les différentes possibilités d'auto-complétion pour une certaine "
|
||||||
|
"commande"
|
||||||
|
|
||||||
|
#: completions.go:745 completions.go:784 completions.go:809 completions.go:835
|
||||||
|
msgid "CompletionSubcommandNoDescFlagDesc"
|
||||||
|
msgstr "désactiver les desriptions"
|
||||||
|
|
||||||
|
#: completions.go:52
|
||||||
|
msgid "CompletionSubcommandUnsupportedFlagError"
|
||||||
|
msgstr "la sous-commande '%s' ne comprend pas l'option '%s'"
|
||||||
|
|
||||||
|
#: command.go:770
|
||||||
|
msgid "DidYouMeanThis"
|
||||||
|
msgstr "Vouliez-vous dire ceci ?"
|
||||||
|
|
||||||
|
#: cobra.go:234
|
||||||
|
msgid "Error"
|
||||||
|
msgstr "Erreur"
|
||||||
|
|
||||||
|
#: args.go:98
|
||||||
|
msgid "ExactArgsValidationError"
|
||||||
|
msgid_plural "ExactArgsValidationErrorPlural"
|
||||||
|
msgstr[0] "accepte %d arg, mais en a reçu %d"
|
||||||
|
msgstr[1] "accepte %d args, mais en a reçu %d"
|
||||||
|
|
||||||
|
#: flag_groups.go:205
|
||||||
|
msgid "ExclusiveFlagsValidationError"
|
||||||
|
msgstr "les options [%v] sont exclusives, mais les options %v ont été fournies"
|
||||||
|
|
||||||
|
#: command.go:1176
|
||||||
|
msgid "FlagNotSetError"
|
||||||
|
msgid_plural "FlagNotSetErrorPlural"
|
||||||
|
msgstr[0] "l'option requise \"%s\" n'est pas présente"
|
||||||
|
msgstr[1] "les options requises \"%s\" ne sont pas présentes"
|
||||||
|
|
||||||
|
#: localizer.go:66
|
||||||
|
msgid "ForInfoAboutCommand"
|
||||||
|
msgstr "pour plus d'information au sujet d'une commande"
|
||||||
|
|
||||||
|
#: command.go:1200
|
||||||
|
msgid "HelpFor"
|
||||||
|
msgstr "aide pour"
|
||||||
|
|
||||||
|
#: args.go:37
|
||||||
|
msgid "LegacyArgsValidationError"
|
||||||
|
msgstr "commande %q inconnue pour %q%s"
|
||||||
|
|
||||||
|
#: args.go:88
|
||||||
|
msgid "MaximumNArgsValidationError"
|
||||||
|
msgid_plural "MaximumNArgsValidationErrorPlural"
|
||||||
|
msgstr[0] "accepte au plus %d arg, mais en a reçu %d"
|
||||||
|
msgstr[1] "accepte au plus %d args, mais en a reçu %d"
|
||||||
|
|
||||||
|
#: args.go:78
|
||||||
|
msgid "MinimumNArgsValidationError"
|
||||||
|
msgid_plural "MinimumNArgsValidationErrorPlural"
|
||||||
|
msgstr[0] "requiert au moins %d arg, mais en a reçu %d"
|
||||||
|
msgstr[1] "requiert au moins %d args, mais en a reçu %d"
|
||||||
|
|
||||||
|
#: args.go:45
|
||||||
|
msgid "NoArgsValidationError"
|
||||||
|
msgstr "commande %q inconnue pour %q"
|
||||||
|
|
||||||
|
#: args.go:62
|
||||||
|
msgid "OnlyValidArgsValidationError"
|
||||||
|
msgstr "argument %q invalide pour %q%s"
|
||||||
|
|
||||||
|
#: args.go:108
|
||||||
|
msgid "RangeArgsValidationError"
|
||||||
|
msgid_plural "RangeArgsValidationErrorPlural"
|
||||||
|
msgstr[0] "accepte entre %d et %d arg, mais en a reçu %d"
|
||||||
|
msgstr[1] "accepte entre %d et %d args, mais en a reçu %d"
|
||||||
|
|
||||||
|
#: command.go:1110
|
||||||
|
msgid "RunHelpTip"
|
||||||
|
msgstr "Essayez '%v --help' pour obtenir de l'aide."
|
||||||
|
|
||||||
|
#: localizer.go:61
|
||||||
|
msgid "SectionAdditionalCommands"
|
||||||
|
msgstr "Commandes Connexes"
|
||||||
|
|
||||||
|
#: localizer.go:64
|
||||||
|
msgid "SectionAdditionalHelpTopics"
|
||||||
|
msgstr "Autres Sujets"
|
||||||
|
|
||||||
|
#: localizer.go:58
|
||||||
|
msgid "SectionAliases"
|
||||||
|
msgstr "Alias"
|
||||||
|
|
||||||
|
#: localizer.go:60
|
||||||
|
msgid "SectionAvailableCommands"
|
||||||
|
msgstr "Commandes Disponibles"
|
||||||
|
|
||||||
|
#: localizer.go:59
|
||||||
|
msgid "SectionExamples"
|
||||||
|
msgstr "Exemples"
|
||||||
|
|
||||||
|
#: localizer.go:62
|
||||||
|
msgid "SectionFlags"
|
||||||
|
msgstr "Options"
|
||||||
|
|
||||||
|
#: localizer.go:63
|
||||||
|
msgid "SectionGlobalFlags"
|
||||||
|
msgstr "Options Globales"
|
||||||
|
|
||||||
|
#: localizer.go:57
|
||||||
|
msgid "SectionUsage"
|
||||||
|
msgstr "Usage"
|
||||||
|
|
||||||
|
#: command.go:1202 command.go:1224
|
||||||
|
msgid "ThisCommand"
|
||||||
|
msgstr "cette commande"
|
||||||
|
|
||||||
|
#: localizer.go:65
|
||||||
|
msgid "Use"
|
||||||
|
msgstr "Utiliser"
|
||||||
|
|
||||||
|
#: command.go:1222
|
||||||
|
msgid "VersionFor"
|
||||||
|
msgstr "version pour"
|
||||||
|
|
||||||
|
#: command.go:1247 command.go:1249
|
||||||
|
msgid "command"
|
||||||
|
msgstr "commande"
|
||||||
|
|
||||||
|
#~ msgid "PathToCommand"
|
||||||
|
#~ msgstr "command"
|
138
localizer.go
Normal file
138
localizer.go
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
package cobra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/leonelquinteros/gotext"
|
||||||
|
"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",
|
||||||
|
"LC_MESSAGES",
|
||||||
|
"LANG",
|
||||||
|
}
|
||||||
|
|
||||||
|
// availableLocalizationDomains holds all the domains used in localization.
|
||||||
|
// Each domain MUST have its own locales/<domain>.pot file and locales/<domain>/ dir.
|
||||||
|
// Therefore, please only use short, ^[a-z]+$ strings as domains.
|
||||||
|
var availableLocalizationDomains = []string{
|
||||||
|
"default",
|
||||||
|
}
|
||||||
|
|
||||||
|
// localeFS points to an embedded filesystem of binary gettext translation files.
|
||||||
|
// For performance and smaller builds, only the binary MO files are included.
|
||||||
|
// Their sibling PO files should still be considered their authoritative source.
|
||||||
|
//
|
||||||
|
//go:embed locales/*/*.mo
|
||||||
|
var localeFS embed.FS
|
||||||
|
|
||||||
|
// i18nCommandGlossary wraps the translated strings passed to the command usage template.
|
||||||
|
// This is used in CommandUsageTemplateData.
|
||||||
|
type i18nCommandGlossary struct {
|
||||||
|
SectionUsage string
|
||||||
|
SectionAliases string
|
||||||
|
SectionExamples string
|
||||||
|
SectionAvailableCommands string
|
||||||
|
SectionAdditionalCommands string
|
||||||
|
SectionFlags string
|
||||||
|
SectionGlobalFlags string
|
||||||
|
SectionAdditionalHelpTopics string
|
||||||
|
Use string
|
||||||
|
ForInfoAboutCommand string
|
||||||
|
}
|
||||||
|
|
||||||
|
var commonCommandGlossary *i18nCommandGlossary
|
||||||
|
|
||||||
|
func getCommandGlossary() *i18nCommandGlossary {
|
||||||
|
if commonCommandGlossary == nil {
|
||||||
|
commonCommandGlossary = &i18nCommandGlossary{
|
||||||
|
SectionUsage: gotext.Get("SectionUsage"),
|
||||||
|
SectionAliases: gotext.Get("SectionAliases"),
|
||||||
|
SectionExamples: gotext.Get("SectionExamples"),
|
||||||
|
SectionAvailableCommands: gotext.Get("SectionAvailableCommands"),
|
||||||
|
SectionAdditionalCommands: gotext.Get("SectionAdditionalCommands"),
|
||||||
|
SectionFlags: gotext.Get("SectionFlags"),
|
||||||
|
SectionGlobalFlags: gotext.Get("SectionGlobalFlags"),
|
||||||
|
SectionAdditionalHelpTopics: gotext.Get("SectionAdditionalHelpTopics"),
|
||||||
|
Use: gotext.Get("Use"),
|
||||||
|
ForInfoAboutCommand: gotext.Get("ForInfoAboutCommand"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return commonCommandGlossary
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupLocalization() {
|
||||||
|
for _, localeIdentifier := range detectLangs() {
|
||||||
|
locale := gotext.NewLocale("", localeIdentifier)
|
||||||
|
|
||||||
|
allDomainsFound := true
|
||||||
|
for _, domain := range availableLocalizationDomains {
|
||||||
|
|
||||||
|
//localeFilepath := fmt.Sprintf("locales/%s/%s.po", domain, localeIdentifier)
|
||||||
|
localeFilepath := fmt.Sprintf("locales/%s/%s.mo", domain, localeIdentifier)
|
||||||
|
localeFile, err := localeFS.ReadFile(localeFilepath)
|
||||||
|
if err != nil {
|
||||||
|
allDomainsFound = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
//translator := gotext.NewPo()
|
||||||
|
translator := gotext.NewMo()
|
||||||
|
translator.Parse(localeFile)
|
||||||
|
|
||||||
|
locale.AddTranslator(domain, translator)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !allDomainsFound {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
gotext.SetStorage(locale)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectLangs() []string {
|
||||||
|
var detectedLangs []string
|
||||||
|
|
||||||
|
// From environment
|
||||||
|
for _, envKey := range envVariablesHoldingLocale {
|
||||||
|
lang := os.Getenv(envKey)
|
||||||
|
if lang != "" {
|
||||||
|
detectedLang := language.Make(lang)
|
||||||
|
appendLang(&detectedLangs, detectedLang)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lastly, from defaults
|
||||||
|
appendLang(&detectedLangs, defaultLanguage)
|
||||||
|
|
||||||
|
return detectedLangs
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendLang(langs *[]string, lang language.Tag) {
|
||||||
|
if lang.IsRoot() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
langString := lang.String()
|
||||||
|
*langs = append(*langs, langString)
|
||||||
|
|
||||||
|
langBase, confidentInBase := lang.Base()
|
||||||
|
if confidentInBase != language.No {
|
||||||
|
*langs = append(*langs, langBase.ISO3())
|
||||||
|
*langs = append(*langs, langBase.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
setupLocalization()
|
||||||
|
}
|
161
localizer_test.go
Normal file
161
localizer_test.go
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
package cobra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/leonelquinteros/gotext"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// resetLocalization resets to the vendor defaults
|
||||||
|
// Ideally this would be done using gotext.SetStorage(nil)
|
||||||
|
func resetLocalization() {
|
||||||
|
locale := gotext.NewLocale("/usr/local/share/locale", "en_US")
|
||||||
|
locale.AddDomain("default")
|
||||||
|
locale.SetDomain("default")
|
||||||
|
gotext.SetStorage(locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLocalization(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
rule string
|
||||||
|
env map[string]string
|
||||||
|
expectedLanguage string
|
||||||
|
message string
|
||||||
|
expectedTranslation string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
rule: "default language is english",
|
||||||
|
expectedLanguage: "en",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rule: "section example (en)",
|
||||||
|
env: map[string]string{
|
||||||
|
"LANGUAGE": "en",
|
||||||
|
},
|
||||||
|
expectedLanguage: "en",
|
||||||
|
message: "SectionExamples",
|
||||||
|
expectedTranslation: "Examples",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rule: "section example (fr)",
|
||||||
|
env: map[string]string{
|
||||||
|
"LANGUAGE": "fr",
|
||||||
|
},
|
||||||
|
expectedLanguage: "fr",
|
||||||
|
message: "SectionExamples",
|
||||||
|
expectedTranslation: "Exemples",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rule: "untranslated string stays as-is",
|
||||||
|
message: "AtelophobiacCoder",
|
||||||
|
expectedTranslation: "AtelophobiacCoder",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rule: "fr_FR falls back to fr",
|
||||||
|
env: map[string]string{
|
||||||
|
"LANGUAGE": "fr_FR",
|
||||||
|
},
|
||||||
|
expectedLanguage: "fr",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rule: "fr-FR falls back to fr",
|
||||||
|
env: map[string]string{
|
||||||
|
"LANGUAGE": "fr-FR",
|
||||||
|
},
|
||||||
|
expectedLanguage: "fr",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rule: "fr_FR@UTF-8 falls back to fr",
|
||||||
|
env: map[string]string{
|
||||||
|
"LANGUAGE": "fr_FR@UTF-8",
|
||||||
|
},
|
||||||
|
expectedLanguage: "fr",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rule: "fr_FR.UTF-8 falls back to fr",
|
||||||
|
env: map[string]string{
|
||||||
|
"LANGUAGE": "fr_FR.UTF-8",
|
||||||
|
},
|
||||||
|
expectedLanguage: "fr",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rule: "LANGUAGE > LC_ALL",
|
||||||
|
env: map[string]string{
|
||||||
|
"LANGUAGE": "fr",
|
||||||
|
"LC_ALL": "en",
|
||||||
|
"LC_MESSAGES": "en",
|
||||||
|
"LANG": "en",
|
||||||
|
},
|
||||||
|
expectedLanguage: "fr",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rule: "LC_ALL > LC_MESSAGES",
|
||||||
|
env: map[string]string{
|
||||||
|
"LC_ALL": "fr",
|
||||||
|
"LC_MESSAGES": "en",
|
||||||
|
"LANG": "en",
|
||||||
|
},
|
||||||
|
expectedLanguage: "fr",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rule: "LC_MESSAGES > LANG",
|
||||||
|
env: map[string]string{
|
||||||
|
"LC_MESSAGES": "fr",
|
||||||
|
"LANG": "en",
|
||||||
|
},
|
||||||
|
expectedLanguage: "fr",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rule: "LANG is supported",
|
||||||
|
env: map[string]string{
|
||||||
|
"LANG": "fr",
|
||||||
|
},
|
||||||
|
expectedLanguage: "fr",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rule: "Fall back to another env if a language is not supported",
|
||||||
|
env: map[string]string{
|
||||||
|
"LANGUAGE": "xx",
|
||||||
|
"LC_ALL": "fr",
|
||||||
|
},
|
||||||
|
expectedLanguage: "fr",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.rule, func(t *testing.T) {
|
||||||
|
// I. Prepare the environment
|
||||||
|
os.Clearenv()
|
||||||
|
if tt.env != nil {
|
||||||
|
for envKey, envValue := range tt.env {
|
||||||
|
err := os.Setenv(envKey, envValue)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("os.Setenv() failed for %s=%s", envKey, envValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// II. Run the initialization of localization
|
||||||
|
resetLocalization()
|
||||||
|
setupLocalization()
|
||||||
|
|
||||||
|
// III. Assert that language was detected correctly
|
||||||
|
if tt.expectedLanguage != "" {
|
||||||
|
actualLanguage := gotext.GetLanguage()
|
||||||
|
if actualLanguage != tt.expectedLanguage {
|
||||||
|
t.Errorf("Expected language `%v' but got `%v'.", tt.expectedLanguage, actualLanguage)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IV. Assert that the message was translated adequately
|
||||||
|
if tt.message != "" {
|
||||||
|
actualTranslation := gotext.Get(tt.message)
|
||||||
|
if actualTranslation != tt.expectedTranslation {
|
||||||
|
t.Errorf("Expected translation `%v' but got `%v'.", tt.expectedTranslation, actualTranslation)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue