135 lines
3.7 KiB
Markdown
135 lines
3.7 KiB
Markdown
|
|
# 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
|