diff --git a/docs/forward-api.md b/docs/forward-api.md index 3bc1f11..c5db94b 100644 --- a/docs/forward-api.md +++ b/docs/forward-api.md @@ -194,7 +194,7 @@ T("i18n.count.person", 3) // "3 people" Produces past-tense completion messages. ```go -T("i18n.done.delete", "config.yaml") // "Config.Yaml deleted" +T("i18n.done.delete", "config.yaml") // "Config.yaml deleted" T("i18n.done.push", "commits") // "Commits pushed" T("i18n.done.delete") // "Deleted" ``` diff --git a/grammar.go b/grammar.go index adb0224..4fcf4cf 100644 --- a/grammar.go +++ b/grammar.go @@ -621,18 +621,26 @@ func isVowel(r rune) bool { return false } -// Title capitalises the first letter of each word. +// Title capitalises the first letter of each word-like segment. +// +// Hyphens and whitespace start a new segment; punctuation inside identifiers +// such as dots and underscores is preserved so filenames stay readable. func Title(s string) string { b := core.NewBuilder() b.Grow(len(s)) - prev := ' ' + capNext := true for _, r := range s { - if !unicode.IsLetter(prev) && unicode.IsLetter(r) { + if unicode.IsLetter(r) && capNext { b.WriteRune(unicode.ToUpper(r)) } else { b.WriteRune(r) } - prev = r + switch r { + case ' ', '\t', '\n', '\r', '-': + capNext = true + default: + capNext = false + } } return b.String() } diff --git a/grammar_test.go b/grammar_test.go index a8ecde3..f11063c 100644 --- a/grammar_test.go +++ b/grammar_test.go @@ -454,6 +454,7 @@ func TestTitle(t *testing.T) { {"", ""}, {"HELLO", "HELLO"}, {"hello-world", "Hello-World"}, + {"config.yaml", "Config.yaml"}, } for _, tt := range tests { @@ -663,7 +664,7 @@ func TestActionResult(t *testing.T) { verb, subject string want string }{ - {"delete", "config.yaml", "Config.Yaml deleted"}, + {"delete", "config.yaml", "Config.yaml deleted"}, {"build", "project", "Project built"}, {"", "file", ""}, {"delete", "", ""}, diff --git a/handler_test.go b/handler_test.go index 3869d2c..fdff311 100644 --- a/handler_test.go +++ b/handler_test.go @@ -104,8 +104,8 @@ func TestDoneHandler(t *testing.T) { // With subject got := h.Handle("i18n.done.delete", []any{"config.yaml"}, nil) - if got != "Config.Yaml deleted" { - t.Errorf("DoneHandler.Handle(delete, config.yaml) = %q, want %q", got, "Config.Yaml deleted") + if got != "Config.yaml deleted" { + t.Errorf("DoneHandler.Handle(delete, config.yaml) = %q, want %q", got, "Config.yaml deleted") } // Without subject — just past tense diff --git a/service_test.go b/service_test.go index 31a28f6..227126c 100644 --- a/service_test.go +++ b/service_test.go @@ -54,8 +54,8 @@ func TestServiceT(t *testing.T) { // Done handler got = svc.T("i18n.done.delete", "config.yaml") - if got != "Config.Yaml deleted" { - t.Errorf("T(i18n.done.delete, config.yaml) = %q, want 'Config.Yaml deleted'", got) + if got != "Config.yaml deleted" { + t.Errorf("T(i18n.done.delete, config.yaml) = %q, want 'Config.yaml deleted'", got) } // Fail handler diff --git a/types.go b/types.go index 7e59ab8..dffcbec 100644 --- a/types.go +++ b/types.go @@ -8,7 +8,7 @@ // T("i18n.label.status") // "Status:" // T("i18n.progress.build") // "Building..." // T("i18n.count.file", 5) // "5 files" -// T("i18n.done.delete", "config.yaml") // "Config.Yaml deleted" +// T("i18n.done.delete", "config.yaml") // "Config.yaml deleted" // T("i18n.fail.push", "commits") // "Failed to push commits" package i18n