Documentation
¶
Overview ¶
Package translation provides a type-safe, declarative system for translating model content stored per-record (distinct from i18n, which localizes static catalog/error strings).
Translatable fields are marked with struct tags, similar to json/db/validate. Each model implements the Translatable interface to expose its model name and instance key. The Translator reads and writes translations through a Store; the Postgres implementation lives in the postgres subpackage, and an in-memory MockStore is provided for tests.
Declaring a model ¶
type Story struct {
ID string `json:"id" translate:"primary"`
Title string `json:"title" translate:"title"`
Description string `json:"description" translate:"description"`
}
func (s *Story) GetTranslationKey() (modelName, keyID string) {
return "stories", s.ID
}
Only the primary key field is required; every other tagged field must be a string. Fields without a translate tag are ignored.
Setup ¶
store := postgres.NewStore(log, db)
tr, err := translation.New(translation.Config{
Store: store,
DefaultLanguage: translation.LanguageRu,
SupportedLangs: []translation.Language{translation.LanguageRu, translation.LanguageKk},
})
Automatic translation in REST ¶
The translationrest subpackage installs one middleware that resolves the request language and translates the response in place, so handlers only return their DTO:
r := router.New(errorMapper, translationrest.Middleware(log, tr))
A response is translated when its rest.ResponseEncoder is a Translatable (single model) or a TranslatableList (collection — translated in one batch query). Translating to the default language is a no-op.
Manual use ¶
// Single model: err := tr.Translate(ctx, story, translation.LanguageKk) // Batch, type-safe: err := translation.TranslateSlice(ctx, tr, stories, translation.LanguageKk) // CRUD on the stored translations: err := tr.Save(ctx, translation.LanguageKk, story) err := tr.Get(ctx, translation.LanguageKk, story) err := tr.Delete(ctx, translation.LanguageKk, story)
Testing ¶
tr, _ := translation.New(translation.Config{Store: translation.NewMockStore(), ...})
Index ¶
- Variables
- func SetLanguageInContext(ctx context.Context, lang Language) context.Context
- func TranslateSlice[T Translatable](ctx context.Context, t *Translator, models []T, lang Language) error
- type Config
- type Data
- type Language
- type MockStore
- func (m *MockStore) CheckTranslationsExist(ctx context.Context, modelName, keyID string, columns []string, ...) error
- func (m *MockStore) DeleteTranslations(ctx context.Context, modelName, keyID string, lang Language) error
- func (m *MockStore) GetTranslations(ctx context.Context, modelName, keyID string, lang Language) (map[string]string, error)
- func (m *MockStore) GetTranslationsBatch(ctx context.Context, modelName string, keyIDs []string, lang Language) (map[string]map[string]string, error)
- func (m *MockStore) SaveTranslation(ctx context.Context, data Data) error
- type Store
- type Translatable
- type TranslatableList
- type Translator
- func (t *Translator) CheckTranslationsExist(ctx context.Context, model Translatable) error
- func (t *Translator) DefaultLanguage() Language
- func (t *Translator) Delete(ctx context.Context, lang Language, model Translatable) error
- func (t *Translator) Get(ctx context.Context, lang Language, model Translatable) error
- func (t *Translator) HTTPMiddleware(next http.Handler) http.Handler
- func (t *Translator) IsDefaultLanguage(lang Language) bool
- func (t *Translator) LanguageFromRequest(r *http.Request) Language
- func (t *Translator) Languages() []Language
- func (t *Translator) Match(code string) Language
- func (t *Translator) Save(ctx context.Context, lang Language, model Translatable) error
- func (t *Translator) Translate(ctx context.Context, model Translatable, lang Language) error
- func (t *Translator) ValidateLanguage(lang Language) error
- func (t *Translator) ValidateTranslateTags(model Translatable) error
Constants ¶
This section is empty.
Variables ¶
var ( // ErrTranslationNotFound is returned when a translation is not found ErrTranslationNotFound = errors.New("translation not found") // ErrInvalidLanguage is returned when an invalid language code is provided ErrInvalidLanguage = errors.New("invalid language") // ErrMissingTranslations is returned when required translations are missing ErrMissingTranslations = errors.New("missing required translations") // ErrInvalidTag is returned when a translate tag is malformed ErrInvalidTag = errors.New("invalid translate tag") // ErrNoPrimaryKey is returned when no primary key field is found ErrNoPrimaryKey = errors.New("no primary key field found with translate:\"primary\" tag") // ErrMultiplePrimaryKeys is returned when multiple primary key fields are found ErrMultiplePrimaryKeys = errors.New("multiple primary key fields found") // ErrInvalidModel is returned when the model doesn't implement Translatable ErrInvalidModel = errors.New("model must implement Translatable interface") )
var ( LanguageRu = Language{Code: "ru", Name: "Russian"} LanguageKk = Language{Code: "kk", Name: "Kazakh"} LanguageEn = Language{Code: "en", Name: "English"} )
Predefined languages offered as a convenience; applications are free to declare their own. The SDK does not assume any particular language set.
Functions ¶
func SetLanguageInContext ¶
SetLanguageInContext returns a child context carrying lang.
func TranslateSlice ¶
func TranslateSlice[T Translatable](ctx context.Context, t *Translator, models []T, lang Language) error
TranslateSlice translates a slice of models with type safety using generics
Types ¶
type Config ¶
type Config struct {
// Store is the translation storage backend (e.g. the postgres subpackage).
Store Store
// DefaultLanguage is the language the canonical content is authored in.
// Translating a model to the default language is a no-op.
DefaultLanguage Language
// SupportedLangs is the set of languages the application serves. It must
// include DefaultLanguage.
SupportedLangs []Language
}
Config holds configuration for the Translator.
type Data ¶
type Data struct {
ModelName string
KeyID string
Language Language
Columns map[string]string // column_name -> translation_value
CreatedAt time.Time
UpdatedAt time.Time
}
Data is a unit of translation to persist for one model instance in one language: a set of column -> value pairs.
type Language ¶
type Language struct {
Code string `json:"code"` // BCP-47 / ISO 639-1 code, e.g. "ru", "kk", "en"
Name string `json:"name"` // human-readable name, e.g. "Russian"
}
Language identifies a language by its code (and an optional display name).
func LanguageFromContext ¶
LanguageFromContext returns the language stored by the middleware. The zero Language is returned when none is set; callers that need a concrete default should compare against Translator.DefaultLanguage.
type MockStore ¶
type MockStore struct {
// contains filtered or unexported fields
}
MockStore is a mock implementation of Store for testing
func (*MockStore) CheckTranslationsExist ¶
func (m *MockStore) CheckTranslationsExist(ctx context.Context, modelName, keyID string, columns []string, langs []Language) error
CheckTranslationsExist checks if translations exist for all columns and languages
func (*MockStore) DeleteTranslations ¶
func (m *MockStore) DeleteTranslations(ctx context.Context, modelName, keyID string, lang Language) error
DeleteTranslations deletes translations from memory
func (*MockStore) GetTranslations ¶
func (m *MockStore) GetTranslations(ctx context.Context, modelName, keyID string, lang Language) (map[string]string, error)
GetTranslations retrieves translations from memory
type Store ¶
type Store interface {
// SaveTranslation saves translation data (should be executed in a transaction)
SaveTranslation(ctx context.Context, data Data) error
// GetTranslations retrieves all translations for a specific model instance and language
// Returns a map of column_name -> translation_value
GetTranslations(ctx context.Context, modelName, keyID string, lang Language) (map[string]string, error)
// DeleteTranslations deletes all translations for a specific model instance and language
DeleteTranslations(ctx context.Context, modelName, keyID string, lang Language) error
// CheckTranslationsExist checks if translations exist for all specified columns and languages
// Returns nil if all translations exist, ErrMissingTranslations otherwise
CheckTranslationsExist(ctx context.Context, modelName, keyID string, columns []string, langs []Language) error
// GetTranslationsBatch retrieves translations for multiple model instances
// Returns a map of keyID -> (column_name -> translation_value)
GetTranslationsBatch(ctx context.Context, modelName string, keyIDs []string, lang Language) (map[string]map[string]string, error)
}
Store is the interface for translation storage
type Translatable ¶
type Translatable interface {
GetTranslationKey() (modelName, keyID string)
}
Translatable is implemented by models that carry translatable fields. The returned modelName groups instances of the same type; keyID identifies the instance.
type TranslatableList ¶
type TranslatableList interface {
Translatables() []Translatable
}
TranslatableList is implemented by response payloads that wrap a collection of Translatable models, so the REST middleware can translate them in one batch without per-handler code.
type Translator ¶
type Translator struct {
// contains filtered or unexported fields
}
Translator active object holds all deps
func NewMockTranslator ¶
func NewMockTranslator() *Translator
NewMockTranslator creates a translator with mock store for testing
func (*Translator) CheckTranslationsExist ¶
func (t *Translator) CheckTranslationsExist(ctx context.Context, model Translatable) error
CheckTranslationsExist checks if translations exist for all supported languages
func (*Translator) DefaultLanguage ¶
func (t *Translator) DefaultLanguage() Language
DefaultLanguage returns the default language
func (*Translator) Delete ¶
func (t *Translator) Delete(ctx context.Context, lang Language, model Translatable) error
Delete deletes translations for a model
func (*Translator) Get ¶
func (t *Translator) Get(ctx context.Context, lang Language, model Translatable) error
Get retrieves translations for a model
func (*Translator) HTTPMiddleware ¶
func (t *Translator) HTTPMiddleware(next http.Handler) http.Handler
HTTPMiddleware is net/http middleware that resolves the request language and stores it in the context for downstream handlers (and the REST translation middleware) to read via LanguageFromContext.
func (*Translator) IsDefaultLanguage ¶
func (t *Translator) IsDefaultLanguage(lang Language) bool
IsDefaultLanguage checks if the given language is the default language
func (*Translator) LanguageFromRequest ¶
func (t *Translator) LanguageFromRequest(r *http.Request) Language
LanguageFromRequest resolves the request language from the X-Language header, falling back to Accept-Language and then the translator's default. Unsupported codes fall back to the default.
func (*Translator) Languages ¶
func (t *Translator) Languages() []Language
Languages returns all supported languages by the app
func (*Translator) Match ¶
func (t *Translator) Match(code string) Language
Match maps a language code (e.g. one already resolved by an upstream i18n middleware) to a supported Language, falling back to the default when the code is empty or unsupported.
func (*Translator) Save ¶
func (t *Translator) Save(ctx context.Context, lang Language, model Translatable) error
Save saves translations for a model
func (*Translator) Translate ¶
func (t *Translator) Translate(ctx context.Context, model Translatable, lang Language) error
Translate translates a single model to the specified language Also translates nested Translatable objects in slice fields
func (*Translator) ValidateLanguage ¶
func (t *Translator) ValidateLanguage(lang Language) error
ValidateLanguage checks if the language is supported
func (*Translator) ValidateTranslateTags ¶
func (t *Translator) ValidateTranslateTags(model Translatable) error
ValidateTranslateTags validates that the model has correct translate tags
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package postgres is the Postgres implementation of translation.Store.
|
Package postgres is the Postgres implementation of translation.Store. |
|
Package translationrest wires the translation package into a skit REST router as a single rest.MidFunc that translates responses automatically.
|
Package translationrest wires the translation package into a skit REST router as a single rest.MidFunc that translates responses automatically. |