cli/docs/plans/2026-01-30-i18n-v2-design.md

135 lines
3.7 KiB
Markdown
Raw Normal View History

# 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