# i18n Package Refactor Design ## Goal Refactor pkg/i18n to be extensible without breaking changes in future. Based on Gemini review recommendations. ## File Structure ### Renamed/Merged | Current | New | Reason | |---------|-----|--------| | `interfaces.go` | `types.go` | Contains types, not interfaces | | `mutate.go` | `loader.go` | Loads/flattens JSON | | `actions.go` | `hooks.go` | Missing key callbacks | | `checks.go` | (merge into loader.go) | Loading helpers | | `mode.go` | (merge into types.go) | Just one type | ### New Files | File | Purpose | |------|---------| | `handler.go` | KeyHandler interface + built-in handlers | | `context.go` | TranslationContext + C() helper | ### Unchanged `grammar.go`, `language.go`, `localise.go`, `debug.go`, `numbers.go`, `time.go`, `i18n.go`, `intents.go`, `compose.go`, `transform.go` ## Interfaces ### KeyHandler ```go // KeyHandler processes translation keys before standard lookup. type KeyHandler interface { Match(key string) bool Handle(key string, args []any, next func() string) string } ``` Built-in handlers: - `LabelHandler` - `i18n.label.*` → "Status:" - `ProgressHandler` - `i18n.progress.*` → "Building..." - `CountHandler` - `i18n.count.*` → "5 files" - `NumericHandler` - `i18n.numeric.*` → formatted numbers - `DoneHandler` - `i18n.done.*` → "File deleted" - `FailHandler` - `i18n.fail.*` → "Failed to delete file" ### Loader ```go // Loader provides translation data to the Service. type Loader interface { Load(lang string) (map[string]Message, *GrammarData, error) Languages() []string } ``` Built-in: `FSLoader` for embedded/filesystem JSON. ### TranslationContext ```go type TranslationContext struct { Context string Gender string Formality Formality Extra map[string]any } func C(context string) *TranslationContext ``` ## Service Changes ```go type Service struct { loader Loader messages map[string]map[string]Message grammar map[string]*GrammarData currentLang string fallbackLang string formality Formality mode Mode debug bool handlers []KeyHandler mu sync.RWMutex } ``` ### Constructors ```go func New() (*Service, error) func NewWithLoader(loader Loader, opts ...Option) (*Service, error) type Option func(*Service) func WithDefaultHandlers() Option func WithFallback(lang string) Option func WithFormality(f Formality) Option ``` ### T() Flow 1. Parse args → extract Context, Subject, data 2. Run handler chain (each can handle or call next) 3. Standard lookup with context suffix fallback ## Public API ### Keep - `T(key, args...)`, `Raw(key, args...)` - `S(noun, value)` - Subject builder - `SetLanguage()`, `CurrentLanguage()`, `SetMode()`, `CurrentMode()` - `SetFormality()`, `SetDebug()`, `Direction()`, `IsRTL()` - Grammar: `PastTense()`, `Gerund()`, `Pluralize()`, `Article()`, `Title()`, `Label()`, `Progress()` ### Add - `C(context)` - Context builder - `NewWithLoader()` - Custom loader support - `AddHandler()`, `PrependHandler()` - Custom handlers ### Remove (No Aliases) - `NewSubject()` - use `S()` - `N()` - use `T("i18n.numeric.*")` ## Breaking Changes - Constructor signature changes - Internal file reorganisation - No backwards compatibility layer ## Implementation Order 1. Create new files (types.go, handler.go, loader.go, context.go, hooks.go) 2. Move types from interfaces.go → types.go 3. Implement Loader interface + FSLoader 4. Implement KeyHandler interface + built-in handlers 5. Implement TranslationContext 6. Update Service struct + constructors 7. Update T() to use handler chain 8. Update package-level functions in i18n.go 9. Delete old files 10. Update tests