[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/go/i18n/RFC.md fully. Find ONE feature... #104

Merged
Virgil merged 1 commit from agent/read---spec-code-core-go-i18n-rfc-md-ful into dev 2026-04-02 03:49:38 +00:00
5 changed files with 173 additions and 33 deletions

82
context_map.go Normal file
View file

@ -0,0 +1,82 @@
package i18n
import "dappco.re/go/core"
func mapValueString(values any, key string) (string, bool) {
switch m := values.(type) {
case map[string]any:
raw, ok := m[key]
if !ok {
return "", false
}
text := core.Trim(core.Sprintf("%v", raw))
if text == "" {
return "", false
}
return text, true
case map[string]string:
text, ok := m[key]
if !ok {
return "", false
}
text = core.Trim(text)
if text == "" {
return "", false
}
return text, true
default:
return "", false
}
}
func contextMapValues(values any) map[string]any {
switch m := values.(type) {
case map[string]any:
return contextMapValuesAny(m)
case map[string]string:
return contextMapValuesString(m)
default:
return nil
}
}
func contextMapValuesAny(values map[string]any) map[string]any {
if len(values) == 0 {
return nil
}
extra := make(map[string]any, len(values))
for key, value := range values {
switch key {
case "Context", "Gender", "Location", "Formality":
continue
case "Extra", "extra", "Extras", "extras":
mergeContextExtra(extra, value)
continue
default:
extra[key] = value
}
}
if len(extra) == 0 {
return nil
}
return extra
}
func contextMapValuesString(values map[string]string) map[string]any {
if len(values) == 0 {
return nil
}
extra := make(map[string]any, len(values))
for key, value := range values {
switch key {
case "Context", "Gender", "Location", "Formality", "Extra", "extra", "Extras", "extras":
continue
default:
extra[key] = value
}
}
if len(extra) == 0 {
return nil
}
return extra
}

View file

@ -238,6 +238,8 @@ func subjectArgText(arg any) string {
return ""
case map[string]any:
return contextArgText(v)
case map[string]string:
return contextArgText(v)
case fmt.Stringer:
return v.String()
default:
@ -245,15 +247,10 @@ func subjectArgText(arg any) string {
}
}
func contextArgText(values map[string]any) string {
if len(values) == 0 {
return ""
}
func contextArgText(values any) string {
for _, key := range []string{"Subject", "subject", "Value", "value", "Text", "text", "Context", "context", "Noun", "noun"} {
if raw, ok := values[key]; ok {
if text := core.Trim(core.Sprintf("%v", raw)); text != "" {
return text
}
if text, ok := mapValueString(values, key); ok {
return text
}
}
return ""

View file

@ -63,6 +63,11 @@ func TestProgressHandler(t *testing.T) {
if got != "Building project..." {
t.Errorf("ProgressHandler.Handle(build, map[Subject:project]) = %q, want %q", got, "Building project...")
}
got = h.Handle("i18n.progress.build", []any{map[string]string{"Subject": "project"}}, nil)
if got != "Building project..." {
t.Errorf("ProgressHandler.Handle(build, map[string]string[Subject:project]) = %q, want %q", got, "Building project...")
}
}
func TestCountHandler(t *testing.T) {
@ -148,6 +153,11 @@ func TestDoneHandler(t *testing.T) {
t.Errorf("DoneHandler.Handle(delete, map[Subject:config.yaml]) = %q, want %q", got, "Config.yaml deleted")
}
got = h.Handle("i18n.done.delete", []any{map[string]string{"Subject": "config.yaml"}}, nil)
if got != "Config.yaml deleted" {
t.Errorf("DoneHandler.Handle(delete, map[string]string[Subject:config.yaml]) = %q, want %q", got, "Config.yaml deleted")
}
// Without subject — just past tense
got = h.Handle("i18n.done.delete", nil, nil)
if got != "Deleted" {
@ -182,6 +192,11 @@ func TestFailHandler(t *testing.T) {
t.Errorf("FailHandler.Handle(push, map[Subject:commits]) = %q, want %q", got, "Failed to push commits")
}
got = h.Handle("i18n.fail.push", []any{map[string]string{"Subject": "commits"}}, nil)
if got != "Failed to push commits" {
t.Errorf("FailHandler.Handle(push, map[string]string[Subject:commits]) = %q, want %q", got, "Failed to push commits")
}
got = h.Handle("i18n.fail.push", nil, nil)
if got != "Failed to push" {
t.Errorf("FailHandler.Handle(push) = %q, want %q", got, "Failed to push")

View file

@ -453,14 +453,14 @@ func (s *Service) getEffectiveContextGenderLocationAndFormality(data any) (strin
var gender string
location := s.location
formality := s.formality
if v, ok := m["Context"].(string); ok {
context = core.Trim(v)
if v, ok := mapValueString(m, "Context"); ok {
context = v
}
if v, ok := m["Gender"].(string); ok {
gender = core.Trim(v)
if v, ok := mapValueString(m, "Gender"); ok {
gender = v
}
if v, ok := m["Location"].(string); ok {
location = core.Trim(v)
if v, ok := mapValueString(m, "Location"); ok {
location = v
}
if v, ok := m["Formality"]; ok {
switch f := v.(type) {
@ -479,6 +479,30 @@ func (s *Service) getEffectiveContextGenderLocationAndFormality(data any) (strin
}
return context, gender, location, formality
}
if m, ok := data.(map[string]string); ok {
var context string
var gender string
location := s.location
formality := s.formality
if v, ok := mapValueString(m, "Context"); ok {
context = v
}
if v, ok := mapValueString(m, "Gender"); ok {
gender = v
}
if v, ok := mapValueString(m, "Location"); ok {
location = v
}
if v, ok := mapValueString(m, "Formality"); ok {
switch core.Lower(v) {
case "formal":
formality = FormalityFormal
case "informal":
formality = FormalityInformal
}
}
return context, gender, location, formality
}
return "", "", s.location, s.getEffectiveFormality(data)
}
@ -490,25 +514,9 @@ func (s *Service) getEffectiveContextExtra(data any) map[string]any {
}
return v.Extra
case map[string]any:
if len(v) == 0 {
return nil
}
extra := make(map[string]any, len(v))
for key, value := range v {
switch key {
case "Context", "Gender", "Location", "Formality":
continue
case "Extra", "extra", "Extras", "extras":
mergeContextExtra(extra, value)
continue
default:
extra[key] = value
}
}
if len(extra) == 0 {
return nil
}
return extra
return contextMapValues(v)
case map[string]string:
return contextMapValues(v)
default:
return nil
}
@ -563,6 +571,16 @@ func (s *Service) getEffectiveFormality(data any) Formality {
}
}
}
if m, ok := data.(map[string]string); ok {
if f, ok := mapValueString(m, "Formality"); ok {
switch core.Lower(f) {
case "formal":
return FormalityFormal
case "informal":
return FormalityInformal
}
}
}
return s.formality
}
@ -690,6 +708,8 @@ func missingKeyArgs(args []any) map[string]any {
switch v := args[0].(type) {
case map[string]any:
return v
case map[string]string:
return contextMapValues(v)
case *TranslationContext:
return missingKeyContextArgs(v)
case *Subject:

View file

@ -128,6 +128,32 @@ func TestServiceTMapContextNestedExtra(t *testing.T) {
}
}
func TestServiceTMapContextStringExtras(t *testing.T) {
svc, err := New()
if err != nil {
t.Fatalf("New() failed: %v", err)
}
SetDefault(svc)
svc.AddMessages("en", map[string]string{
"welcome._greeting": "hello",
"welcome._greeting._region._europe": "bonjour",
"welcome._greeting._region._americas": "howdy",
})
if got := svc.T("welcome", map[string]string{"Context": "greeting"}); got != "hello" {
t.Fatalf("T(welcome, map[string]string{Context:greeting}) = %q, want %q", got, "hello")
}
if got := svc.T("welcome", map[string]string{"Context": "greeting", "region": "europe"}); got != "bonjour" {
t.Fatalf("T(welcome, map[string]string{Context:greeting region:europe}) = %q, want %q", got, "bonjour")
}
if got := svc.T("welcome", map[string]string{"Context": "greeting", "region": "americas"}); got != "howdy" {
t.Fatalf("T(welcome, map[string]string{Context:greeting region:americas}) = %q, want %q", got, "howdy")
}
}
func TestServiceRaw(t *testing.T) {
svc, err := New()
if err != nil {