go-i18n/i18n.go
Claude e8a87b0f50
feat: grammar-aware i18n module extracted from core
Standalone grammar-aware translation engine with:
- 3-tier verb/noun fallback (JSON locale → irregular maps → regular rules)
- 6 built-in i18n.* namespace handlers (label, progress, count, done, fail, numeric)
- Nested en.json with gram/prompt/time/lang sections (no flat command keys)
- CLDR plural rules for 10 languages
- Subject fluent API, number/time formatting, RTL detection
- 55 tests passing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 19:51:27 +00:00

129 lines
2.9 KiB
Go

package i18n
import (
"bytes"
"errors"
"strings"
"text/template"
)
// T translates a message using the default service.
func T(messageID string, args ...any) string {
if svc := Default(); svc != nil {
return svc.T(messageID, args...)
}
return messageID
}
// Raw translates without i18n.* namespace magic.
func Raw(messageID string, args ...any) string {
if svc := Default(); svc != nil {
return svc.Raw(messageID, args...)
}
return messageID
}
// ErrServiceNotInitialized is returned when the service is not initialised.
var ErrServiceNotInitialized = errors.New("i18n: service not initialized")
// SetLanguage sets the language for the default service.
func SetLanguage(lang string) error {
svc := Default()
if svc == nil {
return ErrServiceNotInitialized
}
return svc.SetLanguage(lang)
}
// CurrentLanguage returns the current language code.
func CurrentLanguage() string {
if svc := Default(); svc != nil {
return svc.Language()
}
return "en"
}
// SetMode sets the translation mode for the default service.
func SetMode(m Mode) {
if svc := Default(); svc != nil {
svc.SetMode(m)
}
}
// CurrentMode returns the current translation mode.
func CurrentMode() Mode {
if svc := Default(); svc != nil {
return svc.Mode()
}
return ModeNormal
}
// N formats a number using the i18n.numeric.* namespace.
//
// N("number", 1234567) // "1,234,567"
// N("percent", 0.85) // "85%"
// N("bytes", 1536000) // "1.5 MB"
// N("ordinal", 1) // "1st"
func N(format string, value any) string {
return T("i18n.numeric."+format, value)
}
// AddHandler appends a handler to the default service's handler chain.
func AddHandler(h KeyHandler) {
if svc := Default(); svc != nil {
svc.AddHandler(h)
}
}
// PrependHandler inserts a handler at the start of the default service's handler chain.
func PrependHandler(h KeyHandler) {
if svc := Default(); svc != nil {
svc.PrependHandler(h)
}
}
func executeIntentTemplate(tmplStr string, data templateData) string {
if tmplStr == "" {
return ""
}
if cached, ok := templateCache.Load(tmplStr); ok {
var buf bytes.Buffer
if err := cached.(*template.Template).Execute(&buf, data); err != nil {
return tmplStr
}
return buf.String()
}
tmpl, err := template.New("").Funcs(TemplateFuncs()).Parse(tmplStr)
if err != nil {
return tmplStr
}
templateCache.Store(tmplStr, tmpl)
var buf bytes.Buffer
if err := tmpl.Execute(&buf, data); err != nil {
return tmplStr
}
return buf.String()
}
func applyTemplate(text string, data any) string {
if !strings.Contains(text, "{{") {
return text
}
if cached, ok := templateCache.Load(text); ok {
var buf bytes.Buffer
if err := cached.(*template.Template).Execute(&buf, data); err != nil {
return text
}
return buf.String()
}
tmpl, err := template.New("").Parse(text)
if err != nil {
return text
}
templateCache.Store(text, tmpl)
var buf bytes.Buffer
if err := tmpl.Execute(&buf, data); err != nil {
return text
}
return buf.String()
}