feat: translation support

See toolshed/abra#483
This commit is contained in:
2025-08-19 11:22:52 +02:00
parent 5cf6048ecb
commit 4e205cf13e
108 changed files with 11217 additions and 1645 deletions

View File

@ -12,6 +12,7 @@ import (
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/formatter"
"coopcloud.tech/abra/pkg/i18n"
"coopcloud.tech/abra/pkg/log"
"coopcloud.tech/abra/pkg/secret"
"github.com/docker/docker/api/types"
@ -20,9 +21,9 @@ import (
)
var AppSecretGenerateCommand = &cobra.Command{
Use: "generate <domain> [[secret] [version] | --all] [flags]",
Aliases: []string{"g"},
Short: "Generate secrets",
Use: i18n.G("generate <domain> [[secret] [version] | --all] [flags]"),
Aliases: []string{i18n.G("g")},
Short: i18n.G("Generate secrets"),
Args: cobra.RangeArgs(1, 3),
ValidArgsFunction: func(
cmd *cobra.Command,
@ -34,8 +35,7 @@ var AppSecretGenerateCommand = &cobra.Command{
case 1:
app, err := appPkg.Get(args[0])
if err != nil {
errMsg := fmt.Sprintf("autocomplete failed: %s", err)
return []string{errMsg}, cobra.ShellCompDirectiveError
return []string{i18n.G("autocomplete failed: %s", err)}, cobra.ShellCompDirectiveError
}
return autocomplete.SecretComplete(app.Recipe.Name)
default:
@ -50,11 +50,11 @@ var AppSecretGenerateCommand = &cobra.Command{
}
if len(args) <= 2 && !generateAllSecrets {
log.Fatal("missing arguments [secret]/[version] or '--all'")
log.Fatal(i18n.G("missing arguments [secret]/[version] or '--all'"))
}
if len(args) > 2 && generateAllSecrets {
log.Fatal("cannot use '[secret] [version]' and '--all' together")
log.Fatal(i18n.G("cannot use '[secret] [version]' and '--all' together"))
}
composeFiles, err := app.Recipe.GetComposeFiles(app.Env)
@ -72,7 +72,7 @@ var AppSecretGenerateCommand = &cobra.Command{
secretVersion := args[2]
s, ok := secrets[secretName]
if !ok {
log.Fatalf("%s doesn't exist in the env config?", secretName)
log.Fatal(i18n.G("%s doesn't exist in the env config?", secretName))
}
s.Version = secretVersion
secrets = map[string]secret.Secret{
@ -99,11 +99,11 @@ var AppSecretGenerateCommand = &cobra.Command{
}
if len(secretVals) == 0 {
log.Warn("no secrets generated")
log.Warn(i18n.G("no secrets generated"))
os.Exit(1)
}
headers := []string{"NAME", "VALUE"}
headers := []string{i18n.G("NAME"), i18n.G("VALUE")}
table, err := formatter.CreateTable()
if err != nil {
log.Fatal(err)
@ -121,7 +121,7 @@ var AppSecretGenerateCommand = &cobra.Command{
if internal.MachineReadable {
out, err := formatter.ToJSON(headers, rows)
if err != nil {
log.Fatal("unable to render to JSON: %s", err)
log.Fatal(i18n.G("unable to render to JSON: %s", err))
}
fmt.Println(out)
return
@ -131,31 +131,31 @@ var AppSecretGenerateCommand = &cobra.Command{
log.Fatal(err)
}
log.Warnf(
log.Warn(i18n.G(
"generated secrets %s shown again, please take note of them %s",
formatter.BoldStyle.Render("NOT"),
formatter.BoldStyle.Render("NOW"),
)
formatter.BoldStyle.Render(i18n.G("NOT")),
formatter.BoldStyle.Render(i18n.G("NOW")),
))
},
}
var AppSecretInsertCommand = &cobra.Command{
Use: "insert <domain> <secret> <version> <data> [flags]",
Aliases: []string{"i"},
Short: "Insert secret",
Long: `This command inserts a secret into an app environment.
Use: i18n.G("insert <domain> <secret> <version> <data> [flags]"),
Aliases: []string{i18n.G("i")},
Short: i18n.G("Insert secret"),
Long: i18n.G(`This command inserts a secret into an app environment.
Arbitrary secret insertion is not supported. Secrets that are inserted must
match those configured in the recipe beforehand.
This can be useful when you want to manually generate secrets for an app
environment. Typically, you can let Abra generate them for you on app creation
(see "abra app new --secrets/-S" for more).`,
Example: ` # insert regular secret
(see "abra app new --secrets/-S" for more).`),
Example: i18n.G(` # insert regular secret
abra app secret insert 1312.net my_secret v1 mySuperSecret
# insert secret as file
abra app secret insert 1312.net my_secret v1 secret.txt -f`,
abra app secret insert 1312.net my_secret v1 secret.txt -f`),
Args: cobra.MinimumNArgs(4),
ValidArgsFunction: func(
cmd *cobra.Command,
@ -167,8 +167,7 @@ environment. Typically, you can let Abra generate them for you on app creation
case 1:
app, err := appPkg.Get(args[0])
if err != nil {
errMsg := fmt.Sprintf("autocomplete failed: %s", err)
return []string{errMsg}, cobra.ShellCompDirectiveError
return []string{i18n.G("autocomplete failed: %s", err)}, cobra.ShellCompDirectiveError
}
return autocomplete.SecretComplete(app.Recipe.Name)
default:
@ -208,13 +207,13 @@ environment. Typically, you can let Abra generate them for you on app creation
}
}
if !isRecipeSecret {
log.Fatalf("no secret %s available for recipe %s?", name, app.Recipe.Name)
log.Fatal(i18n.G("no secret %s available for recipe %s?", name, app.Recipe.Name))
}
if insertFromFile {
raw, err := os.ReadFile(data)
if err != nil {
log.Fatalf("reading secret from file: %s", err)
log.Fatal(i18n.G("reading secret from file: %s", err))
}
data = string(raw)
}
@ -228,7 +227,7 @@ environment. Typically, you can let Abra generate them for you on app creation
log.Fatal(err)
}
log.Infof("%s successfully stored on server", secretName)
log.Info(i18n.G("%s successfully stored on server", secretName))
if storeInPass {
if err := secret.PassInsertSecret(data, name, app.Name, app.Server); err != nil {
@ -244,28 +243,28 @@ func secretRm(cl *dockerClient.Client, app appPkg.App, secretName, parsed string
return err
}
log.Infof("deleted %s successfully from server", secretName)
log.Info(i18n.G("deleted %s successfully from server", secretName))
if removeFromPass {
if err := secret.PassRmSecret(parsed, app.StackName(), app.Server); err != nil {
return err
}
log.Infof("deleted %s successfully from local pass store", secretName)
log.Info(i18n.G("deleted %s successfully from local pass store", secretName))
}
return nil
}
var AppSecretRmCommand = &cobra.Command{
Use: "remove <domain> [[secret] | --all] [flags]",
Aliases: []string{"rm"},
Short: "Remove a secret",
Long: `This command removes a secret from an app environment.
Use: i18n.G("remove <domain> [[secret] | --all] [flags]"),
Aliases: []string{i18n.G("rm")},
Short: i18n.G("Remove a secret"),
Long: i18n.G(`This command removes a secret from an app environment.
Arbitrary secret removal is not supported. Secrets that are removed must
match those configured in the recipe beforehand.`,
Example: " abra app secret rm 1312.net oauth_key",
match those configured in the recipe beforehand.`),
Example: i18n.G(" abra app secret rm 1312.net oauth_key"),
Args: cobra.RangeArgs(1, 2),
ValidArgsFunction: func(
cmd *cobra.Command,
@ -278,8 +277,7 @@ match those configured in the recipe beforehand.`,
if !rmAllSecrets {
app, err := appPkg.Get(args[0])
if err != nil {
errMsg := fmt.Sprintf("autocomplete failed: %s", err)
return []string{errMsg}, cobra.ShellCompDirectiveError
return []string{i18n.G("autocomplete failed: %s", err)}, cobra.ShellCompDirectiveError
}
return autocomplete.SecretComplete(app.Recipe.Name)
}
@ -306,11 +304,11 @@ match those configured in the recipe beforehand.`,
}
if len(args) == 2 && rmAllSecrets {
log.Fatal("cannot use [secret] and --all/-a together")
log.Fatal(i18n.G("cannot use [secret] and --all/-a together"))
}
if len(args) != 2 && !rmAllSecrets {
log.Fatal("no secret(s) specified?")
log.Fatal(i18n.G("no secret(s) specified?"))
}
cl, err := client.New(app.Server)
@ -361,19 +359,19 @@ match those configured in the recipe beforehand.`,
}
if !match && secretToRm != "" {
log.Fatalf("%s doesn't exist on server?", secretToRm)
log.Fatal(i18n.G("%s doesn't exist on server?", secretToRm))
}
if !match {
log.Fatal("no secrets to remove?")
log.Fatal(i18n.G("no secrets to remove?"))
}
},
}
var AppSecretLsCommand = &cobra.Command{
Use: "list <domain>",
Aliases: []string{"ls"},
Short: "List all secrets",
Use: i18n.G("list <domain>"),
Aliases: []string{i18n.G("ls")},
Short: i18n.G("List all secrets"),
Args: cobra.MinimumNArgs(1),
ValidArgsFunction: func(
cmd *cobra.Command,
@ -393,7 +391,7 @@ var AppSecretLsCommand = &cobra.Command{
log.Fatal(err)
}
headers := []string{"NAME", "VERSION", "GENERATED NAME", "CREATED ON SERVER"}
headers := []string{i18n.G("NAME"), i18n.G("VERSION"), i18n.G("GENERATED NAME"), i18n.G("CREATED ON SERVER")}
table, err := formatter.CreateTable()
if err != nil {
log.Fatal(err)
@ -423,7 +421,7 @@ var AppSecretLsCommand = &cobra.Command{
if internal.MachineReadable {
out, err := formatter.ToJSON(headers, rows)
if err != nil {
log.Fatal("unable to render to JSON: %s", err)
log.Fatal(i18n.G("unable to render to JSON: %s", err))
}
fmt.Println(out)
return
@ -436,14 +434,14 @@ var AppSecretLsCommand = &cobra.Command{
return
}
log.Warnf("no secrets stored for %s", app.Name)
log.Warn(i18n.G("no secrets stored for %s", app.Name))
},
}
var AppSecretCommand = &cobra.Command{
Use: "secret [cmd] [args] [flags]",
Aliases: []string{"s"},
Short: "Manage app secrets",
Use: i18n.G("secret [cmd] [args] [flags]"),
Aliases: []string{i18n.G("s")},
Short: i18n.G("Manage app secrets"),
}
var (
@ -458,105 +456,105 @@ var (
func init() {
AppSecretGenerateCommand.Flags().BoolVarP(
&internal.MachineReadable,
"machine",
"m",
i18n.G("machine"),
i18n.G("m"),
false,
"print machine-readable output",
i18n.G("print machine-readable output"),
)
AppSecretGenerateCommand.Flags().BoolVarP(
&storeInPass,
"pass",
"p",
i18n.G("pass"),
i18n.G("p"),
false,
"store generated secrets in a local pass store",
i18n.G("store generated secrets in a local pass store"),
)
AppSecretGenerateCommand.Flags().BoolVarP(
&internal.Chaos,
"chaos",
"C",
i18n.G("chaos"),
i18n.G("C"),
false,
"ignore uncommitted recipes changes",
i18n.G("ignore uncommitted recipes changes"),
)
AppSecretGenerateCommand.Flags().BoolVarP(
&generateAllSecrets,
"all",
"a",
i18n.G("all"),
i18n.G("a"),
false,
"generate all secrets",
i18n.G("generate all secrets"),
)
AppSecretInsertCommand.Flags().BoolVarP(
&storeInPass,
"pass",
"p",
i18n.G("pass"),
i18n.G("p"),
false,
"store generated secrets in a local pass store",
i18n.G("store generated secrets in a local pass store"),
)
AppSecretInsertCommand.Flags().BoolVarP(
&insertFromFile,
"file",
"f",
i18n.G("file"),
i18n.G("f"),
false,
"treat input as a file",
i18n.G("treat input as a file"),
)
AppSecretInsertCommand.Flags().BoolVarP(
&trimInput,
"trim",
"t",
i18n.G("trim"),
i18n.G("t"),
false,
"trim input",
i18n.G("trim input"),
)
AppSecretInsertCommand.Flags().BoolVarP(
&internal.Chaos,
"chaos",
"C",
i18n.G("chaos"),
i18n.G("C"),
false,
"ignore uncommitted recipes changes",
i18n.G("ignore uncommitted recipes changes"),
)
AppSecretRmCommand.Flags().BoolVarP(
&rmAllSecrets,
"all",
"a",
i18n.G("all"),
i18n.G("a"),
false,
"remove all secrets",
i18n.G("remove all secrets"),
)
AppSecretRmCommand.Flags().BoolVarP(
&removeFromPass,
"pass",
"p",
i18n.G("pass"),
i18n.G("p"),
false,
"remove generated secrets from a local pass store",
i18n.G("remove generated secrets from a local pass store"),
)
AppSecretRmCommand.Flags().BoolVarP(
&internal.Chaos,
"chaos",
"C",
i18n.G("chaos"),
i18n.G("C"),
false,
"ignore uncommitted recipes changes",
i18n.G("ignore uncommitted recipes changes"),
)
AppSecretLsCommand.Flags().BoolVarP(
&internal.Chaos,
"chaos",
"C",
i18n.G("chaos"),
i18n.G("C"),
false,
"ignore uncommitted recipes changes",
i18n.G("ignore uncommitted recipes changes"),
)
AppSecretLsCommand.Flags().BoolVarP(
&internal.MachineReadable,
"machine",
"m",
i18n.G("machine"),
i18n.G("m"),
false,
"print machine-readable output",
i18n.G("print machine-readable output"),
)
}