From 0c1b74c637ea29fa0446110c1a92a23f4e778be5 Mon Sep 17 00:00:00 2001 From: Snider Date: Tue, 17 Mar 2026 05:22:28 +0000 Subject: [PATCH] feat: auto-derive i18n keys from command names (Conclave pattern) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commandService.applyI18n() walks registered commands and sets Short/Long from cmd.{name}.short/long keys automatically. Downstream packages no longer need to call i18n.T() for command descriptions — the CLI Conclave handles it via service name derivation. This is the Conclave pattern: services inside a sealed core.New() auto-discover each other's capabilities via the lifecycle hooks. Co-Authored-By: Virgil --- pkg/cli/commands.go | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/pkg/cli/commands.go b/pkg/cli/commands.go index 2a60b7e..d06d19f 100644 --- a/pkg/cli/commands.go +++ b/pkg/cli/commands.go @@ -25,7 +25,7 @@ import ( // cli.WithCommands("dev", dev.AddDevCommands, locales.FS) func WithCommands(name string, register func(root *Command), localeFS ...fs.FS) core.Option { return core.WithName("cmd."+name, func(c *core.Core) (any, error) { - svc := &commandService{core: c, register: register} + svc := &commandService{core: c, name: name, register: register} if len(localeFS) > 0 { svc.localeFS = localeFS[0] } @@ -35,6 +35,7 @@ func WithCommands(name string, register func(root *Command), localeFS ...fs.FS) type commandService struct { core *core.Core + name string register func(root *Command) localeFS fs.FS } @@ -42,10 +43,33 @@ type commandService struct { func (s *commandService) OnStartup(_ context.Context) error { if root, ok := s.core.App.(*cobra.Command); ok { s.register(root) + // Auto-set Short/Long from i18n keys derived from command name. + // The Conclave's i18n service has already loaded all translations + // from sibling services' LocaleProvider before commands attach. + s.applyI18n(root) } return nil } +// applyI18n walks commands added by this service and sets Short/Long +// from derived i18n keys if they're empty or still raw keys. +func (s *commandService) applyI18n(root *cobra.Command) { + for _, cmd := range root.Commands() { + key := "cmd." + cmd.Name() + // Only set if Short is empty or looks like a raw key (contains dots) + if cmd.Short == "" || cmd.Short == key+".short" { + if translated := T(key + ".short"); translated != key+".short" { + cmd.Short = translated + } + } + if cmd.Long == "" || cmd.Long == key+".long" { + if translated := T(key + ".long"); translated != key+".long" { + cmd.Long = translated + } + } + } +} + // Locales implements core.LocaleProvider. func (s *commandService) Locales() fs.FS { return s.localeFS