diff --git a/handler.go b/handler.go index dd95c61..73cdfbb 100644 --- a/handler.go +++ b/handler.go @@ -1,6 +1,9 @@ package i18n import ( + "strings" + "unicode" + "dappco.re/go/core" ) @@ -42,11 +45,12 @@ func (h CountHandler) Match(key string) bool { func (h CountHandler) Handle(key string, args []any, next func() string) string { noun := core.TrimPrefix(key, "i18n.count.") + lang := currentLangForGrammar() if len(args) > 0 { count := toInt(args[0]) - return core.Sprintf("%s %s", FormatNumber(int64(count)), Pluralize(noun, count)) + return core.Sprintf("%s %s", FormatNumber(int64(count)), countWordForm(lang, noun, count)) } - return noun + return renderWord(lang, noun) } // DoneHandler handles i18n.done.{verb} -> "File deleted" patterns. @@ -128,6 +132,51 @@ func DefaultHandlers() []KeyHandler { } } +func countWordForm(lang, noun string, count int) string { + display := renderWord(lang, noun) + if display == "" { + return Pluralize(noun, count) + } + if count == 1 { + return display + } + if isUpperAcronymPlural(display) { + return display + } + return Pluralize(display, count) +} + +func isUpperAcronymPlural(s string) bool { + if len(s) < 2 || !strings.HasSuffix(s, "s") { + return false + } + hasLetter := false + for _, r := range s[:len(s)-1] { + if !unicode.IsLetter(r) { + continue + } + hasLetter = true + if !unicode.IsUpper(r) { + return false + } + } + return hasLetter +} + +func isAllUpper(s string) bool { + hasLetter := false + for _, r := range s { + if !unicode.IsLetter(r) { + continue + } + hasLetter = true + if !unicode.IsUpper(r) { + return false + } + } + return hasLetter +} + // RunHandlerChain executes a chain of handlers for a key. func RunHandlerChain(handlers []KeyHandler, key string, args []any, fallback func() string) string { for i, h := range handlers { diff --git a/handler_test.go b/handler_test.go index be2e65b..3869d2c 100644 --- a/handler_test.go +++ b/handler_test.go @@ -72,7 +72,11 @@ func TestCountHandler(t *testing.T) { {"i18n.count.file", []any{5}, "5 files"}, {"i18n.count.file", []any{0}, "0 files"}, {"i18n.count.child", []any{3}, "3 children"}, + {"i18n.count.url", []any{2}, "2 URLs"}, + {"i18n.count.api", []any{2}, "2 APIs"}, + {"i18n.count.cpus", []any{2}, "2 CPUs"}, {"i18n.count.file", nil, "file"}, + {"i18n.count.url", nil, "URL"}, } for _, tt := range tests {