From 4d8b288a9cc31509e7a9bf95c0d018c8d81bfa81 Mon Sep 17 00:00:00 2001 From: Virgil Date: Thu, 2 Apr 2026 09:52:50 +0000 Subject: [PATCH] refactor(i18n): centralize default-service wrappers Co-Authored-By: Virgil --- debug.go | 4 +- default_service.go | 17 +++++ i18n.go | 152 ++++++++++++++++++++++----------------------- localise.go | 44 ++++++------- 4 files changed, 114 insertions(+), 103 deletions(-) create mode 100644 default_service.go diff --git a/debug.go b/debug.go index e97aa98..de6739c 100644 --- a/debug.go +++ b/debug.go @@ -2,9 +2,7 @@ package i18n // SetDebug enables or disables debug mode on the default service. func SetDebug(enabled bool) { - if svc := Default(); svc != nil { - svc.SetDebug(enabled) - } + withDefaultService(func(svc *Service) { svc.SetDebug(enabled) }) } func (s *Service) SetDebug(enabled bool) { diff --git a/default_service.go b/default_service.go new file mode 100644 index 0000000..a813317 --- /dev/null +++ b/default_service.go @@ -0,0 +1,17 @@ +package i18n + +// withDefaultService runs fn when the default service is available. +func withDefaultService(fn func(*Service)) { + if svc := Default(); svc != nil { + fn(svc) + } +} + +// defaultServiceValue returns the value produced by fn when the default +// service exists, or fallback otherwise. +func defaultServiceValue[T any](fallback T, fn func(*Service) T) T { + if svc := Default(); svc != nil { + return fn(svc) + } + return fallback +} diff --git a/i18n.go b/i18n.go index d2db405..5f3b78f 100644 --- a/i18n.go +++ b/i18n.go @@ -12,34 +12,34 @@ import ( // T translates a message using the default service. // // Example: -// i18n.T("greeting") +// +// i18n.T("greeting") func T(messageID string, args ...any) string { - if svc := Default(); svc != nil { + return defaultServiceValue(messageID, func(svc *Service) string { return svc.T(messageID, args...) - } - return messageID + }) } // Translate translates a message using the default service and returns a Core result. // // Example: -// result := i18n.Translate("greeting") +// +// result := i18n.Translate("greeting") func Translate(messageID string, args ...any) core.Result { - if svc := Default(); svc != nil { + return defaultServiceValue(core.Result{Value: messageID, OK: false}, func(svc *Service) core.Result { return svc.Translate(messageID, args...) - } - return core.Result{Value: messageID, OK: false} + }) } // Raw translates without i18n.* namespace magic. // // Example: -// i18n.Raw("prompt.yes") +// +// i18n.Raw("prompt.yes") func Raw(messageID string, args ...any) string { - if svc := Default(); svc != nil { + return defaultServiceValue(messageID, func(svc *Service) string { return svc.Raw(messageID, args...) - } - return messageID + }) } // ErrServiceNotInitialised is returned when the service is not initialised. @@ -51,103 +51,100 @@ var ErrServiceNotInitialized = ErrServiceNotInitialised // SetLanguage sets the language for the default service. // // Example: -// _ = i18n.SetLanguage("fr") +// +// _ = i18n.SetLanguage("fr") func SetLanguage(lang string) error { - svc := Default() - if svc == nil { - return ErrServiceNotInitialised - } - return svc.SetLanguage(lang) + return defaultServiceValue(ErrServiceNotInitialised, func(svc *Service) error { + return svc.SetLanguage(lang) + }) } // CurrentLanguage returns the current language code. // // Example: -// lang := i18n.CurrentLanguage() +// +// lang := i18n.CurrentLanguage() func CurrentLanguage() string { - if svc := Default(); svc != nil { + return defaultServiceValue("en", func(svc *Service) string { return svc.Language() - } - return "en" + }) } // AvailableLanguages returns the loaded language tags on the default service. // // Example: -// langs := i18n.AvailableLanguages() +// +// langs := i18n.AvailableLanguages() func AvailableLanguages() []string { - if svc := Default(); svc != nil { + return defaultServiceValue([]string(nil), func(svc *Service) []string { langs := svc.AvailableLanguages() if len(langs) == 0 { return nil } return append([]string(nil), langs...) - } - return nil + }) } // SetMode sets the translation mode for the default service. // // Example: -// i18n.SetMode(i18n.ModeCollect) +// +// i18n.SetMode(i18n.ModeCollect) func SetMode(m Mode) { - if svc := Default(); svc != nil { - svc.SetMode(m) - } + withDefaultService(func(svc *Service) { svc.SetMode(m) }) } // SetFallback sets the fallback language for the default service. // // Example: -// i18n.SetFallback("en") +// +// i18n.SetFallback("en") func SetFallback(lang string) { - if svc := Default(); svc != nil { - svc.SetFallback(lang) - } + withDefaultService(func(svc *Service) { svc.SetFallback(lang) }) } // CurrentMode returns the current translation mode. // // Example: -// mode := i18n.CurrentMode() +// +// mode := i18n.CurrentMode() func CurrentMode() Mode { - if svc := Default(); svc != nil { + return defaultServiceValue(ModeNormal, func(svc *Service) Mode { return svc.Mode() - } - return ModeNormal + }) } // CurrentFallback returns the current fallback language. // // Example: -// fallback := i18n.CurrentFallback() +// +// fallback := i18n.CurrentFallback() func CurrentFallback() string { - if svc := Default(); svc != nil { + return defaultServiceValue("en", func(svc *Service) string { return svc.Fallback() - } - return "en" + }) } // CurrentFormality returns the current default formality. // // Example: -// formality := i18n.CurrentFormality() +// +// formality := i18n.CurrentFormality() func CurrentFormality() Formality { - if svc := Default(); svc != nil { + return defaultServiceValue(FormalityNeutral, func(svc *Service) Formality { return svc.Formality() - } - return FormalityNeutral + }) } // CurrentDebug reports whether debug mode is enabled on the default service. // // Example: -// debug := i18n.CurrentDebug() +// +// debug := i18n.CurrentDebug() func CurrentDebug() bool { - if svc := Default(); svc != nil { + return defaultServiceValue(false, func(svc *Service) bool { return svc.Debug() - } - return false + }) } // N formats a value using the i18n.numeric.* namespace. @@ -167,10 +164,11 @@ func N(format string, value any, args ...any) string { // Prompt translates a prompt key from the prompt namespace. // // Example: -// i18n.Prompt("confirm") // -// Prompt("yes") // "y" -// Prompt("confirm") // "Are you sure?" +// i18n.Prompt("confirm") +// +// Prompt("yes") // "y" +// Prompt("confirm") // "Are you sure?" func Prompt(key string) string { key = normalizeLookupKey(key) if key == "" { @@ -182,9 +180,10 @@ func Prompt(key string) string { // Lang translates a language label from the lang namespace. // // Example: -// i18n.Lang("de") // -// Lang("de") // "German" +// i18n.Lang("de") +// +// Lang("de") // "German" func Lang(key string) string { key = normalizeLookupKey(key) if key == "" { @@ -210,27 +209,26 @@ func normalizeLookupKey(key string) string { // AddHandler appends one or more handlers to the default service's handler chain. // // Example: -// i18n.AddHandler(MyHandler{}) +// +// i18n.AddHandler(MyHandler{}) func AddHandler(handlers ...KeyHandler) { - if svc := Default(); svc != nil { - svc.AddHandler(handlers...) - } + withDefaultService(func(svc *Service) { svc.AddHandler(handlers...) }) } // SetHandlers replaces the default service's handler chain. // // Example: -// i18n.SetHandlers(i18n.LabelHandler{}, i18n.ProgressHandler{}) +// +// i18n.SetHandlers(i18n.LabelHandler{}, i18n.ProgressHandler{}) func SetHandlers(handlers ...KeyHandler) { - if svc := Default(); svc != nil { - svc.SetHandlers(handlers...) - } + withDefaultService(func(svc *Service) { svc.SetHandlers(handlers...) }) } // LoadFS loads additional translations from an fs.FS into the default service. // // Example: -// i18n.LoadFS(os.DirFS("."), "locales") +// +// i18n.LoadFS(os.DirFS("."), "locales") // // Call this from init() in packages that ship their own locale files: // @@ -239,42 +237,40 @@ func SetHandlers(handlers ...KeyHandler) { // // func init() { i18n.LoadFS(localeFS, "locales") } func LoadFS(fsys fs.FS, dir string) { - if svc := Default(); svc != nil { + withDefaultService(func(svc *Service) { if err := svc.LoadFS(fsys, dir); err != nil { log.Error("i18n: LoadFS failed", "dir", dir, "err", err) } - } + }) } // PrependHandler inserts one or more handlers at the start of the default service's handler chain. // // Example: -// i18n.PrependHandler(MyHandler{}) +// +// i18n.PrependHandler(MyHandler{}) func PrependHandler(handlers ...KeyHandler) { - if svc := Default(); svc != nil { - svc.PrependHandler(handlers...) - } + withDefaultService(func(svc *Service) { svc.PrependHandler(handlers...) }) } // CurrentHandlers returns a copy of the default service's handler chain. // // Example: -// handlers := i18n.CurrentHandlers() +// +// handlers := i18n.CurrentHandlers() func CurrentHandlers() []KeyHandler { - if svc := Default(); svc != nil { + return defaultServiceValue([]KeyHandler(nil), func(svc *Service) []KeyHandler { return svc.Handlers() - } - return nil + }) } // ClearHandlers removes all handlers from the default service. // // Example: -// i18n.ClearHandlers() +// +// i18n.ClearHandlers() func ClearHandlers() { - if svc := Default(); svc != nil { - svc.ClearHandlers() - } + withDefaultService(func(svc *Service) { svc.ClearHandlers() }) } func executeIntentTemplate(tmplStr string, data templateData) string { diff --git a/localise.go b/localise.go index 102bfe9..61b273d 100644 --- a/localise.go +++ b/localise.go @@ -70,49 +70,48 @@ func IsRTLLanguage(lang string) bool { // SetFormality sets the default formality level on the default service. // // Example: -// i18n.SetFormality(i18n.FormalityFormal) +// +// i18n.SetFormality(i18n.FormalityFormal) func SetFormality(f Formality) { - if svc := Default(); svc != nil { - svc.SetFormality(f) - } + withDefaultService(func(svc *Service) { svc.SetFormality(f) }) } // SetLocation sets the default location context on the default service. // // Example: -// i18n.SetLocation("workspace") +// +// i18n.SetLocation("workspace") func SetLocation(location string) { - if svc := Default(); svc != nil { - svc.SetLocation(location) - } + withDefaultService(func(svc *Service) { svc.SetLocation(location) }) } // CurrentLocation returns the current default location context. // // Example: -// location := i18n.CurrentLocation() +// +// location := i18n.CurrentLocation() func CurrentLocation() string { - if svc := Default(); svc != nil { + return defaultServiceValue("", func(svc *Service) string { return svc.Location() - } - return "" + }) } // Direction returns the text direction for the current language. // // Example: -// dir := i18n.Direction() +// +// dir := i18n.Direction() func Direction() TextDirection { - if svc := Default(); svc != nil { + return defaultServiceValue(DirLTR, func(svc *Service) TextDirection { return svc.Direction() - } - return DirLTR + }) } // CurrentDirection returns the current default text direction. // // Example: -// dir := i18n.CurrentDirection() +// +// dir := i18n.CurrentDirection() func CurrentDirection() TextDirection { return Direction() } @@ -120,18 +119,19 @@ func CurrentDirection() TextDirection { // IsRTL returns true if the current language uses right-to-left text. // // Example: -// rtl := i18n.IsRTL() +// +// rtl := i18n.IsRTL() func IsRTL() bool { return Direction() == DirRTL } // CurrentPluralCategory returns the plural category for the current default language. // // Example: -// cat := i18n.CurrentPluralCategory(2) +// +// cat := i18n.CurrentPluralCategory(2) func CurrentPluralCategory(n int) PluralCategory { - if svc := Default(); svc != nil { + return defaultServiceValue(GetPluralCategory("en", n), func(svc *Service) PluralCategory { return svc.PluralCategory(n) - } - return GetPluralCategory("en", n) + }) } func detectLanguage(supported []language.Tag) string { -- 2.45.3