feat: auto-derive i18n keys from command names (Conclave pattern)
Some checks failed
Deploy / build (push) Failing after 3s
Security Scan / security (push) Successful in 13s

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 <virgil@lethean.io>
This commit is contained in:
Snider 2026-03-17 05:22:28 +00:00
parent 91de96994a
commit 0c1b74c637

View file

@ -25,7 +25,7 @@ import (
// cli.WithCommands("dev", dev.AddDevCommands, locales.FS) // cli.WithCommands("dev", dev.AddDevCommands, locales.FS)
func WithCommands(name string, register func(root *Command), localeFS ...fs.FS) core.Option { func WithCommands(name string, register func(root *Command), localeFS ...fs.FS) core.Option {
return core.WithName("cmd."+name, func(c *core.Core) (any, error) { 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 { if len(localeFS) > 0 {
svc.localeFS = localeFS[0] svc.localeFS = localeFS[0]
} }
@ -35,6 +35,7 @@ func WithCommands(name string, register func(root *Command), localeFS ...fs.FS)
type commandService struct { type commandService struct {
core *core.Core core *core.Core
name string
register func(root *Command) register func(root *Command)
localeFS fs.FS localeFS fs.FS
} }
@ -42,10 +43,33 @@ type commandService struct {
func (s *commandService) OnStartup(_ context.Context) error { func (s *commandService) OnStartup(_ context.Context) error {
if root, ok := s.core.App.(*cobra.Command); ok { if root, ok := s.core.App.(*cobra.Command); ok {
s.register(root) 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 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. // Locales implements core.LocaleProvider.
func (s *commandService) Locales() fs.FS { func (s *commandService) Locales() fs.FS {
return s.localeFS return s.localeFS