feat(i18n): add debug mode and NewSubject alias
- Add SetDebug()/Debug() methods for showing key prefixes in output - Debug mode shows: "[cli.success] Success" instead of "Success" - Add NewSubject() as alias for S() for readability - Both T() and C() respect debug mode Debug mode is useful for: - Identifying which translation keys are used where - Verifying correct key usage during development - QA testing of translation coverage Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
fc74d4df9c
commit
fa6d62e385
4 changed files with 134 additions and 1 deletions
|
|
@ -32,6 +32,13 @@ func S(noun string, value any) *Subject {
|
|||
}
|
||||
}
|
||||
|
||||
// NewSubject is an alias for S() for readability in longer expressions.
|
||||
//
|
||||
// NewSubject("file", path).Count(3).In("workspace")
|
||||
func NewSubject(noun string, value any) *Subject {
|
||||
return S(noun, value)
|
||||
}
|
||||
|
||||
// Count sets the count for pluralization.
|
||||
// Used to determine singular/plural forms in templates.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -25,6 +25,12 @@ func TestSubject_Good(t *testing.T) {
|
|||
assert.Equal(t, "", s.location)
|
||||
})
|
||||
|
||||
t.Run("NewSubject alias", func(t *testing.T) {
|
||||
s := NewSubject("repo", "core-php")
|
||||
assert.Equal(t, "repo", s.Noun)
|
||||
assert.Equal(t, "core-php", s.Value)
|
||||
})
|
||||
|
||||
t.Run("with count", func(t *testing.T) {
|
||||
s := S("file", "*.go").Count(5)
|
||||
assert.Equal(t, 5, s.GetCount())
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ type Service struct {
|
|||
fallbackLang string
|
||||
availableLangs []language.Tag
|
||||
mode Mode // Translation mode (Normal, Strict, Collect)
|
||||
debug bool // Debug mode shows key prefixes
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
|
|
@ -241,6 +242,17 @@ func SetDefault(s *Service) {
|
|||
defaultService = s
|
||||
}
|
||||
|
||||
// SetDebug enables or disables debug mode on the default service.
|
||||
// In debug mode, translations show their keys: [key] translation
|
||||
//
|
||||
// SetDebug(true)
|
||||
// T("cli.success") // "[cli.success] Success"
|
||||
func SetDebug(enabled bool) {
|
||||
if svc := Default(); svc != nil {
|
||||
svc.SetDebug(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
// T translates a message using the default service.
|
||||
// For semantic intents (core.* namespace), pass a Subject as the first argument.
|
||||
//
|
||||
|
|
@ -368,6 +380,24 @@ func (s *Service) Mode() Mode {
|
|||
return s.mode
|
||||
}
|
||||
|
||||
// SetDebug enables or disables debug mode.
|
||||
// In debug mode, translations are prefixed with their key:
|
||||
//
|
||||
// [cli.success] Success
|
||||
// [core.delete] Delete config.yaml?
|
||||
func (s *Service) SetDebug(enabled bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.debug = enabled
|
||||
}
|
||||
|
||||
// Debug returns whether debug mode is enabled.
|
||||
func (s *Service) Debug() bool {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.debug
|
||||
}
|
||||
|
||||
// T translates a message by its ID.
|
||||
// Optional template data can be passed for interpolation.
|
||||
//
|
||||
|
|
@ -431,6 +461,11 @@ func (s *Service) T(messageID string, args ...any) string {
|
|||
text = applyTemplate(text, data)
|
||||
}
|
||||
|
||||
// Debug mode: prefix with key
|
||||
if s.debug {
|
||||
return "[" + messageID + "] " + text
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
|
|
@ -494,13 +529,27 @@ func (s *Service) C(intent string, subject *Subject) *Composed {
|
|||
// Create template data from subject
|
||||
data := newTemplateData(subject)
|
||||
|
||||
return &Composed{
|
||||
result := &Composed{
|
||||
Question: executeIntentTemplate(intentDef.Question, data),
|
||||
Confirm: executeIntentTemplate(intentDef.Confirm, data),
|
||||
Success: executeIntentTemplate(intentDef.Success, data),
|
||||
Failure: executeIntentTemplate(intentDef.Failure, data),
|
||||
Meta: intentDef.Meta,
|
||||
}
|
||||
|
||||
// Debug mode: prefix each form with the intent key
|
||||
s.mu.RLock()
|
||||
debug := s.debug
|
||||
s.mu.RUnlock()
|
||||
if debug {
|
||||
prefix := "[" + intent + "] "
|
||||
result.Question = prefix + result.Question
|
||||
result.Confirm = prefix + result.Confirm
|
||||
result.Success = prefix + result.Success
|
||||
result.Failure = prefix + result.Failure
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// executeIntentTemplate executes an intent template with the given data.
|
||||
|
|
|
|||
|
|
@ -164,3 +164,74 @@ func TestNestedKeys(t *testing.T) {
|
|||
result = svc.T("cmd.dev.work.flag.status")
|
||||
assert.Equal(t, "Show status only, don't push", result)
|
||||
}
|
||||
|
||||
func TestDebugMode(t *testing.T) {
|
||||
t.Run("default is disabled", func(t *testing.T) {
|
||||
svc, err := New()
|
||||
require.NoError(t, err)
|
||||
assert.False(t, svc.Debug())
|
||||
})
|
||||
|
||||
t.Run("T with debug mode", func(t *testing.T) {
|
||||
svc, err := New()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Without debug
|
||||
result := svc.T("cli.success")
|
||||
assert.Equal(t, "Success", result)
|
||||
|
||||
// Enable debug
|
||||
svc.SetDebug(true)
|
||||
assert.True(t, svc.Debug())
|
||||
|
||||
// With debug - shows key prefix
|
||||
result = svc.T("cli.success")
|
||||
assert.Equal(t, "[cli.success] Success", result)
|
||||
|
||||
// Disable debug
|
||||
svc.SetDebug(false)
|
||||
result = svc.T("cli.success")
|
||||
assert.Equal(t, "Success", result)
|
||||
})
|
||||
|
||||
t.Run("C with debug mode", func(t *testing.T) {
|
||||
svc, err := New()
|
||||
require.NoError(t, err)
|
||||
|
||||
subject := S("file", "config.yaml")
|
||||
|
||||
// Without debug
|
||||
result := svc.C("core.delete", subject)
|
||||
assert.NotContains(t, result.Question, "[core.delete]")
|
||||
|
||||
// Enable debug
|
||||
svc.SetDebug(true)
|
||||
|
||||
// With debug - shows key prefix on all forms
|
||||
result = svc.C("core.delete", subject)
|
||||
assert.Contains(t, result.Question, "[core.delete]")
|
||||
assert.Contains(t, result.Success, "[core.delete]")
|
||||
assert.Contains(t, result.Failure, "[core.delete]")
|
||||
})
|
||||
|
||||
t.Run("package-level SetDebug", func(t *testing.T) {
|
||||
// Reset default
|
||||
defaultService = nil
|
||||
defaultOnce = sync.Once{}
|
||||
defaultErr = nil
|
||||
|
||||
err := Init()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Enable debug via package function
|
||||
SetDebug(true)
|
||||
assert.True(t, Default().Debug())
|
||||
|
||||
// Translate
|
||||
result := T("cli.success")
|
||||
assert.Equal(t, "[cli.success] Success", result)
|
||||
|
||||
// Cleanup
|
||||
SetDebug(false)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue