fix(cli): load locale sources during registration
All checks were successful
Security Scan / security (push) Successful in 24s
All checks were successful
Security Scan / security (push) Successful in 24s
This commit is contained in:
parent
fcadba08b1
commit
c6fae794b3
3 changed files with 92 additions and 1 deletions
|
|
@ -7,6 +7,7 @@ import (
|
|||
"sync"
|
||||
|
||||
"dappco.re/go/core"
|
||||
"forge.lthn.ai/core/go-i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
|
@ -19,6 +20,7 @@ import (
|
|||
// )
|
||||
func WithCommands(name string, register func(root *Command), localeFS ...fs.FS) CommandSetup {
|
||||
return func(c *core.Core) {
|
||||
loadLocaleSources(localeSourcesFromFS(localeFS...)...)
|
||||
if root, ok := c.App().Runtime.(*cobra.Command); ok {
|
||||
register(root)
|
||||
}
|
||||
|
|
@ -49,6 +51,7 @@ func RegisterCommands(fn CommandRegistration, localeFS ...fs.FS) {
|
|||
root := instance
|
||||
registeredCommandsMu.Unlock()
|
||||
|
||||
loadLocaleSources(localeSourcesFromFS(localeFS...)...)
|
||||
appendLocales(localeFS...)
|
||||
|
||||
// If commands already attached (CLI already running), attach immediately
|
||||
|
|
@ -73,6 +76,31 @@ func appendLocales(localeFS ...fs.FS) {
|
|||
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.
|
||||
func RegisteredLocales() []fs.FS {
|
||||
registeredCommandsMu.Lock()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ package cli
|
|||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
|
||||
"forge.lthn.ai/core/go-i18n"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
|
@ -16,6 +18,19 @@ func resetGlobals(t *testing.T) {
|
|||
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
|
||||
// with no concurrent RegisterCommands calls in flight (i.e. test setup/teardown).
|
||||
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.
|
||||
func TestWithAppName_Good(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)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -110,6 +110,8 @@ func Init(opts Options) error {
|
|||
return
|
||||
}
|
||||
|
||||
loadLocaleSources(opts.I18nSources...)
|
||||
|
||||
// Attach registered commands AFTER Core startup so i18n is available
|
||||
attachRegisteredCommands(rootCmd)
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue