refactor(i18n): rename core.* namespace to i18n.*
More self-documenting: when you see T("i18n.label.status") it's
immediately clear the i18n package is composing it, not looking up a key.
- T("i18n.label.status") → "Status:"
- T("i18n.progress.build") → "Building..."
- T("i18n.count.file", 5) → "5 files"
- T("i18n.done.delete", "file") → "File deleted"
- T("i18n.fail.delete", "file") → "Failed to delete file"
Intent keys (core.delete, core.install, etc.) remain unchanged -
they're semantic intent names, not i18n namespace patterns.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
39de3c2836
commit
5e55f66de9
3 changed files with 52 additions and 62 deletions
|
|
@ -513,7 +513,7 @@ func L(word string) string {
|
|||
// Use this for direct key lookups without auto-composition.
|
||||
//
|
||||
// i18n._("cli.success") // Raw lookup
|
||||
// i18n.T("core.label.status") // Smart: returns "Status:"
|
||||
// i18n.T("i18n.label.status") // Smart: returns "Status:"
|
||||
func _(messageID string, args ...any) string {
|
||||
if svc := Default(); svc != nil {
|
||||
return svc.Raw(messageID, args...)
|
||||
|
|
@ -626,25 +626,25 @@ func (s *Service) PluralCategory(n int) PluralCategory {
|
|||
//
|
||||
// The core.* namespace provides auto-composed grammar shortcuts:
|
||||
//
|
||||
// T("core.label.status") // → "Status:"
|
||||
// T("core.progress.build") // → "Building..."
|
||||
// T("core.progress.check", "config") // → "Checking config..."
|
||||
// T("core.count.file", 5) // → "5 files"
|
||||
// T("core.done.delete", "file") // → "File deleted"
|
||||
// T("core.fail.delete", "file") // → "Failed to delete file"
|
||||
// T("i18n.label.status") // → "Status:"
|
||||
// T("i18n.progress.build") // → "Building..."
|
||||
// T("i18n.progress.check", "config") // → "Checking config..."
|
||||
// T("i18n.count.file", 5) // → "5 files"
|
||||
// T("i18n.done.delete", "file") // → "File deleted"
|
||||
// T("i18n.fail.delete", "file") // → "Failed to delete file"
|
||||
//
|
||||
// For semantic intents, pass a Subject:
|
||||
//
|
||||
// T("core.delete", S("file", "config.yaml")) // → "Delete config.yaml?"
|
||||
//
|
||||
// Use _() for raw key lookup without core.* magic.
|
||||
// Use _() for raw key lookup without i18n.* magic.
|
||||
func (s *Service) T(messageID string, args ...any) string {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
// Handle core.* namespace magic
|
||||
if strings.HasPrefix(messageID, "core.") {
|
||||
if result := s.handleCoreNamespace(messageID, args); result != "" {
|
||||
// Handle i18n.* namespace magic
|
||||
if strings.HasPrefix(messageID, "i18n.") {
|
||||
if result := s.handleI18nNamespace(messageID, args); result != "" {
|
||||
if s.debug {
|
||||
return debugFormat(messageID, result)
|
||||
}
|
||||
|
|
@ -672,19 +672,19 @@ func (s *Service) T(messageID string, args ...any) string {
|
|||
return text
|
||||
}
|
||||
|
||||
// handleCoreNamespace processes core.* namespace patterns.
|
||||
// handleI18nNamespace processes i18n.* namespace patterns.
|
||||
// Returns empty string if pattern not recognized.
|
||||
// Must be called with s.mu.RLock held.
|
||||
func (s *Service) handleCoreNamespace(key string, args []any) string {
|
||||
// core.label.{word} → Label(word)
|
||||
if strings.HasPrefix(key, "core.label.") {
|
||||
word := strings.TrimPrefix(key, "core.label.")
|
||||
func (s *Service) handleI18nNamespace(key string, args []any) string {
|
||||
// i18n.label.{word} → Label(word)
|
||||
if strings.HasPrefix(key, "i18n.label.") {
|
||||
word := strings.TrimPrefix(key, "i18n.label.")
|
||||
return Label(word)
|
||||
}
|
||||
|
||||
// core.progress.{verb} → Progress(verb) or ProgressSubject(verb, subj)
|
||||
if strings.HasPrefix(key, "core.progress.") {
|
||||
verb := strings.TrimPrefix(key, "core.progress.")
|
||||
// i18n.progress.{verb} → Progress(verb) or ProgressSubject(verb, subj)
|
||||
if strings.HasPrefix(key, "i18n.progress.") {
|
||||
verb := strings.TrimPrefix(key, "i18n.progress.")
|
||||
if len(args) > 0 {
|
||||
if subj, ok := args[0].(string); ok {
|
||||
return ProgressSubject(verb, subj)
|
||||
|
|
@ -693,9 +693,9 @@ func (s *Service) handleCoreNamespace(key string, args []any) string {
|
|||
return Progress(verb)
|
||||
}
|
||||
|
||||
// core.count.{noun} → "N noun(s)"
|
||||
if strings.HasPrefix(key, "core.count.") {
|
||||
noun := strings.TrimPrefix(key, "core.count.")
|
||||
// i18n.count.{noun} → "N noun(s)"
|
||||
if strings.HasPrefix(key, "i18n.count.") {
|
||||
noun := strings.TrimPrefix(key, "i18n.count.")
|
||||
if len(args) > 0 {
|
||||
count := toInt(args[0])
|
||||
return fmt.Sprintf("%d %s", count, Pluralize(noun, count))
|
||||
|
|
@ -703,9 +703,9 @@ func (s *Service) handleCoreNamespace(key string, args []any) string {
|
|||
return noun
|
||||
}
|
||||
|
||||
// core.done.{verb} → ActionResult(verb, subj)
|
||||
if strings.HasPrefix(key, "core.done.") {
|
||||
verb := strings.TrimPrefix(key, "core.done.")
|
||||
// i18n.done.{verb} → ActionResult(verb, subj)
|
||||
if strings.HasPrefix(key, "i18n.done.") {
|
||||
verb := strings.TrimPrefix(key, "i18n.done.")
|
||||
if len(args) > 0 {
|
||||
if subj, ok := args[0].(string); ok {
|
||||
return ActionResult(verb, subj)
|
||||
|
|
@ -714,9 +714,9 @@ func (s *Service) handleCoreNamespace(key string, args []any) string {
|
|||
return Title(PastTense(verb))
|
||||
}
|
||||
|
||||
// core.fail.{verb} → ActionFailed(verb, subj)
|
||||
if strings.HasPrefix(key, "core.fail.") {
|
||||
verb := strings.TrimPrefix(key, "core.fail.")
|
||||
// i18n.fail.{verb} → ActionFailed(verb, subj)
|
||||
if strings.HasPrefix(key, "i18n.fail.") {
|
||||
verb := strings.TrimPrefix(key, "i18n.fail.")
|
||||
if len(args) > 0 {
|
||||
if subj, ok := args[0].(string); ok {
|
||||
return ActionFailed(verb, subj)
|
||||
|
|
@ -725,16 +725,6 @@ func (s *Service) handleCoreNamespace(key string, args []any) string {
|
|||
return ActionFailed(verb, "")
|
||||
}
|
||||
|
||||
// core.{intent} with Subject → C(intent, subject).Question
|
||||
if len(args) > 0 {
|
||||
if subject, ok := args[0].(*Subject); ok {
|
||||
s.mu.RUnlock()
|
||||
result := s.C(key, subject)
|
||||
s.mu.RLock()
|
||||
return result.Question
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -140,16 +140,16 @@ func TestPluralization(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
SetDefault(svc)
|
||||
|
||||
// Singular - uses core.count.* magic
|
||||
result := svc.T("core.count.item", 1)
|
||||
// Singular - uses i18n.count.* magic
|
||||
result := svc.T("i18n.count.item", 1)
|
||||
assert.Equal(t, "1 item", result)
|
||||
|
||||
// Plural
|
||||
result = svc.T("core.count.item", 5)
|
||||
result = svc.T("i18n.count.item", 5)
|
||||
assert.Equal(t, "5 items", result)
|
||||
|
||||
// Zero uses plural
|
||||
result = svc.T("core.count.item", 0)
|
||||
result = svc.T("i18n.count.item", 0)
|
||||
assert.Equal(t, "0 items", result)
|
||||
}
|
||||
|
||||
|
|
@ -320,7 +320,7 @@ func TestDebugMode(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestCoreNamespaceMagic(t *testing.T) {
|
||||
func TestI18nNamespaceMagic(t *testing.T) {
|
||||
svc, err := New()
|
||||
require.NoError(t, err)
|
||||
SetDefault(svc)
|
||||
|
|
@ -331,16 +331,16 @@ func TestCoreNamespaceMagic(t *testing.T) {
|
|||
args []any
|
||||
expected string
|
||||
}{
|
||||
{"label", "core.label.status", nil, "Status:"},
|
||||
{"label version", "core.label.version", nil, "Version:"},
|
||||
{"progress", "core.progress.build", nil, "Building..."},
|
||||
{"progress check", "core.progress.check", nil, "Checking..."},
|
||||
{"progress with subject", "core.progress.check", []any{"config"}, "Checking config..."},
|
||||
{"count singular", "core.count.file", []any{1}, "1 file"},
|
||||
{"count plural", "core.count.file", []any{5}, "5 files"},
|
||||
{"done", "core.done.delete", []any{"file"}, "File deleted"},
|
||||
{"done build", "core.done.build", []any{"project"}, "Project built"},
|
||||
{"fail", "core.fail.delete", []any{"file"}, "Failed to delete file"},
|
||||
{"label", "i18n.label.status", nil, "Status:"},
|
||||
{"label version", "i18n.label.version", nil, "Version:"},
|
||||
{"progress", "i18n.progress.build", nil, "Building..."},
|
||||
{"progress check", "i18n.progress.check", nil, "Checking..."},
|
||||
{"progress with subject", "i18n.progress.check", []any{"config"}, "Checking config..."},
|
||||
{"count singular", "i18n.count.file", []any{1}, "1 file"},
|
||||
{"count plural", "i18n.count.file", []any{5}, "5 files"},
|
||||
{"done", "i18n.done.delete", []any{"file"}, "File deleted"},
|
||||
{"done build", "i18n.done.build", []any{"project"}, "Project built"},
|
||||
{"fail", "i18n.fail.delete", []any{"file"}, "Failed to delete file"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
@ -351,15 +351,15 @@ func TestCoreNamespaceMagic(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRawBypassesCoreNamespace(t *testing.T) {
|
||||
func TestRawBypassesI18nNamespace(t *testing.T) {
|
||||
svc, err := New()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Raw() should return key as-is since core.label.status isn't in JSON
|
||||
result := svc.Raw("core.label.status")
|
||||
assert.Equal(t, "core.label.status", result)
|
||||
// Raw() should return key as-is since i18n.label.status isn't in JSON
|
||||
result := svc.Raw("i18n.label.status")
|
||||
assert.Equal(t, "i18n.label.status", result)
|
||||
|
||||
// T() should compose it
|
||||
result = svc.T("core.label.status")
|
||||
result = svc.T("i18n.label.status")
|
||||
assert.Equal(t, "Status:", result)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -265,12 +265,12 @@ func TestIntentT_Integration(t *testing.T) {
|
|||
svc, err := New()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Using T with core.* prefix and Subject should return Question form
|
||||
result := svc.T("core.delete", S("file", "config.yaml"))
|
||||
assert.Equal(t, "Delete config.yaml?", result)
|
||||
// Using C with intent key and Subject
|
||||
composed := svc.C("core.delete", S("file", "config.yaml"))
|
||||
assert.Equal(t, "Delete config.yaml?", composed.Question)
|
||||
|
||||
// Using T with regular key should work normally
|
||||
result = svc.T("cmd.dev.short")
|
||||
result := svc.T("cmd.dev.short")
|
||||
assert.Equal(t, "Multi-repo development workflow", result)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue