go-i18n/i18n.go
Virgil 97cffe8dbd
All checks were successful
Security Scan / security (push) Successful in 10s
Test / test (push) Successful in 1m11s
feat(i18n): add prompt shorthand
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-01 22:38:31 +00:00

182 lines
4.2 KiB
Go

package i18n
import (
"bytes"
"text/template"
"dappco.re/go/core"
)
// 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
}
// ErrServiceNotInitialised is returned when the service is not initialised.
var ErrServiceNotInitialised = core.NewError("i18n: service not initialised")
// ErrServiceNotInitialized is deprecated: use ErrServiceNotInitialised.
var ErrServiceNotInitialized = ErrServiceNotInitialised
// SetLanguage sets the language for the default service.
func SetLanguage(lang string) error {
svc := Default()
if svc == nil {
return ErrServiceNotInitialised
}
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)
}
// Prompt translates a prompt key from the prompt namespace.
//
// Prompt("yes") // "y"
// Prompt("confirm") // "Are you sure?"
func Prompt(key string) string {
if key == "" {
return ""
}
return T("prompt." + key)
}
// AddHandler appends one or more handlers to the default service's handler chain.
func AddHandler(handlers ...KeyHandler) {
if svc := Default(); svc != nil {
svc.AddHandler(handlers...)
}
}
// PrependHandler inserts one or more handlers at the start of the default service's handler chain.
func PrependHandler(handlers ...KeyHandler) {
if svc := Default(); svc != nil {
svc.PrependHandler(handlers...)
}
}
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 !core.Contains(text, "{{") {
return text
}
data = templateDataForRendering(data)
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("").Funcs(TemplateFuncs()).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()
}
func templateDataForRendering(data any) any {
switch v := data.(type) {
case *TranslationContext:
if v == nil {
return nil
}
rendered := map[string]any{
"Context": v.Context,
"Gender": v.Gender,
"Formality": v.Formality,
"Extra": v.Extra,
}
for key, value := range v.Extra {
if _, exists := rendered[key]; !exists {
rendered[key] = value
}
}
return rendered
case *Subject:
if v == nil {
return nil
}
return map[string]any{
"Subject": v.String(),
"Noun": v.Noun,
"Count": v.count,
"Gender": v.gender,
"Location": v.location,
"Formality": v.formality,
"IsFormal": v.formality == FormalityFormal,
"IsPlural": v.count != 1,
"Value": v.Value,
}
default:
return data
}
}