Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/go/cli/RFC.md fully. Find ONE feature ...' (#31) from agent/read---spec-code-core-go-cli-rfc-md-full into dev
All checks were successful
Security Scan / security (push) Successful in 21s
All checks were successful
Security Scan / security (push) Successful in 21s
This commit is contained in:
commit
04ab0fb947
3 changed files with 92 additions and 1 deletions
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"dappco.re/go/core"
|
"dappco.re/go/core"
|
||||||
|
"forge.lthn.ai/core/go-i18n"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -19,6 +20,7 @@ import (
|
||||||
// )
|
// )
|
||||||
func WithCommands(name string, register func(root *Command), localeFS ...fs.FS) CommandSetup {
|
func WithCommands(name string, register func(root *Command), localeFS ...fs.FS) CommandSetup {
|
||||||
return func(c *core.Core) {
|
return func(c *core.Core) {
|
||||||
|
loadLocaleSources(localeSourcesFromFS(localeFS...)...)
|
||||||
if root, ok := c.App().Runtime.(*cobra.Command); ok {
|
if root, ok := c.App().Runtime.(*cobra.Command); ok {
|
||||||
register(root)
|
register(root)
|
||||||
}
|
}
|
||||||
|
|
@ -49,6 +51,7 @@ func RegisterCommands(fn CommandRegistration, localeFS ...fs.FS) {
|
||||||
root := instance
|
root := instance
|
||||||
registeredCommandsMu.Unlock()
|
registeredCommandsMu.Unlock()
|
||||||
|
|
||||||
|
loadLocaleSources(localeSourcesFromFS(localeFS...)...)
|
||||||
appendLocales(localeFS...)
|
appendLocales(localeFS...)
|
||||||
|
|
||||||
// If commands already attached (CLI already running), attach immediately
|
// If commands already attached (CLI already running), attach immediately
|
||||||
|
|
@ -73,6 +76,31 @@ func appendLocales(localeFS ...fs.FS) {
|
||||||
registeredCommandsMu.Unlock()
|
registeredCommandsMu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func localeSourcesFromFS(localeFS ...fs.FS) []LocaleSource {
|
||||||
|
sources := make([]LocaleSource, 0, len(localeFS))
|
||||||
|
for _, lfs := range localeFS {
|
||||||
|
if lfs != nil {
|
||||||
|
sources = append(sources, LocaleSource{FS: lfs, Dir: "."})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sources
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadLocaleSources(sources ...LocaleSource) {
|
||||||
|
svc := i18n.Default()
|
||||||
|
if svc == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, src := range sources {
|
||||||
|
if src.FS == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := svc.AddLoader(i18n.NewFSLoader(src.FS, src.Dir)); err != nil {
|
||||||
|
LogDebug("failed to load locale source", "dir", src.Dir, "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// RegisteredLocales returns all locale filesystems registered by command packages.
|
// RegisteredLocales returns all locale filesystems registered by command packages.
|
||||||
func RegisteredLocales() []fs.FS {
|
func RegisteredLocales() []fs.FS {
|
||||||
registeredCommandsMu.Lock()
|
registeredCommandsMu.Lock()
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@ package cli
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"testing/fstest"
|
||||||
|
|
||||||
|
"forge.lthn.ai/core/go-i18n"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
@ -16,6 +18,19 @@ func resetGlobals(t *testing.T) {
|
||||||
t.Cleanup(doReset)
|
t.Cleanup(doReset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resetI18nDefault(t *testing.T) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
prev := i18n.Default()
|
||||||
|
svc, err := i18n.New()
|
||||||
|
require.NoError(t, err)
|
||||||
|
i18n.SetDefault(svc)
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
i18n.SetDefault(prev)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// doReset clears all package-level state. Only safe from a single goroutine
|
// doReset clears all package-level state. Only safe from a single goroutine
|
||||||
// with no concurrent RegisterCommands calls in flight (i.e. test setup/teardown).
|
// with no concurrent RegisterCommands calls in flight (i.e. test setup/teardown).
|
||||||
func doReset() {
|
func doReset() {
|
||||||
|
|
@ -135,6 +150,53 @@ func TestRegisterCommands_Bad(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestLocaleLoading_Good verifies locale files become available to the active i18n service.
|
||||||
|
func TestLocaleLoading_Good(t *testing.T) {
|
||||||
|
t.Run("Init loads I18nSources", func(t *testing.T) {
|
||||||
|
resetGlobals(t)
|
||||||
|
resetI18nDefault(t)
|
||||||
|
|
||||||
|
localeFS := fstest.MapFS{
|
||||||
|
"en.json": {
|
||||||
|
Data: []byte(`{"custom":{"hello":"Hello from locale"}}`),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := Init(Options{
|
||||||
|
AppName: "test",
|
||||||
|
I18nSources: []LocaleSource{WithLocales(localeFS, ".")},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, "Hello from locale", i18n.T("custom.hello"))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("WithCommands loads localeFS before registration", func(t *testing.T) {
|
||||||
|
resetGlobals(t)
|
||||||
|
resetI18nDefault(t)
|
||||||
|
|
||||||
|
err := Init(Options{AppName: "test"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
localeFS := fstest.MapFS{
|
||||||
|
"en.json": {
|
||||||
|
Data: []byte(`{"custom":{"immediate":"Loaded eagerly"}}`),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var observed string
|
||||||
|
setup := WithCommands("test", func(root *cobra.Command) {
|
||||||
|
_ = root
|
||||||
|
observed = i18n.T("custom.immediate")
|
||||||
|
}, localeFS)
|
||||||
|
|
||||||
|
setup(Core())
|
||||||
|
|
||||||
|
assert.Equal(t, "Loaded eagerly", observed)
|
||||||
|
assert.Equal(t, "Loaded eagerly", i18n.T("custom.immediate"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// TestWithAppName_Good tests the app name override.
|
// TestWithAppName_Good tests the app name override.
|
||||||
func TestWithAppName_Good(t *testing.T) {
|
func TestWithAppName_Good(t *testing.T) {
|
||||||
t.Run("overrides root command use", func(t *testing.T) {
|
t.Run("overrides root command use", func(t *testing.T) {
|
||||||
|
|
@ -158,4 +220,3 @@ func TestWithAppName_Good(t *testing.T) {
|
||||||
assert.Equal(t, "core", RootCmd().Use)
|
assert.Equal(t, "core", RootCmd().Use)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,8 @@ func Init(opts Options) error {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadLocaleSources(opts.I18nSources...)
|
||||||
|
|
||||||
// Attach registered commands AFTER Core startup so i18n is available
|
// Attach registered commands AFTER Core startup so i18n is available
|
||||||
attachRegisteredCommands(rootCmd)
|
attachRegisteredCommands(rootCmd)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue