Documentation
¶
Index ¶
- Constants
- func SanitizeOutput(s string) string
- func ValidateFormatString(format string, argCount int) error
- func ValidateKey(key string) error
- func ValidateLocale(locale string) error
- type Cacher
- type Detector
- type EnvProvider
- type ErrInvalidFormat
- type ErrInvalidKey
- type ErrInvalidLocale
- type ErrKeyNotFound
- type ErrPathTraversal
- type ErrUnknownFormat
- type FallbackChainer
- type FormattedTranslator
- type GenderCategory
- type GenderTranslator
- type KeyChecker
- type KeyResolver
- type LeveledLogger
- type LocaleDetector
- type LocaleGetter
- type LocaleSetter
- type LogLevel
- type Logger
- type Normalizer
- type PluralCategory
- type PluralResolver
- type PluralTranslator
- type TranslationLoader
- type TranslationLookup
- type TranslationParser
- type TranslatorProvider
Constants ¶
const MaxFormatPrecision = 1024
MaxFormatPrecision is the maximum allowed precision value in a printf-style format specifier (e.g., %.1000s). Larger values would let a malicious or corrupt translation string balloon fmt.Sprintf's output into a huge allocation — e.g., "%.99999999s" would cause fmt to pad the argument with spaces up to ~100M characters. 1024 is chosen as a generous cap: CJK width alignment, long log lines, and common formatting use well under 100; 1024 accommodates a paragraph of padded text without inviting abuse.
const MaxJSONDepth = 50
MaxJSONDepth is the maximum allowed nesting depth for JSON documents.
const MaxJSONSize = 10 * 1024 * 1024
MaxJSONSize is the maximum allowed size for JSON input in bytes (10 MB).
const MaxKeyCount = 10000
MaxKeyCount is the maximum number of total keys (including nested) allowed in a single parsed translation file. This prevents denial-of-service from translation files with an excessive number of entries.
const MaxKeyDepth = 10
MaxKeyDepth is the maximum nesting depth for translation keys.
const MaxKeyLength = 256
MaxKeyLength is the maximum allowed length for translation keys.
const MaxLocaleLength = 10
MaxLocaleLength is the maximum allowed length for locale strings.
const MaxOutputLength = 10240
MaxOutputLength is the maximum allowed length for sanitized output (10 KB).
const MaxPrefixLength = 64
MaxPrefixLength is the maximum allowed length for namespace prefixes.
Variables ¶
This section is empty.
Functions ¶
func SanitizeOutput ¶
SanitizeOutput removes dangerous characters and sequences from output strings. It strips control characters (except newline and tab), ANSI escape sequences, and BiDi override characters (U+202A through U+202E). The result is truncated to MaxOutputLength bytes if it exceeds that limit.
func ValidateFormatString ¶
ValidateFormatString validates that format is safe and that its format specifiers match argCount. The dangerous %n specifier is rejected, and precision values greater than MaxFormatPrecision are rejected to prevent allocation-amplification attacks (e.g., a malicious "%.99999999s" translation string coercing fmt.Sprintf into a ~100MB allocation). Returns an ErrInvalidFormat on any violation.
func ValidateKey ¶
ValidateKey validates a translation key for security and format compliance.
func ValidateLocale ¶
ValidateLocale validates a locale string according to BCP 47 format and performs security checks to prevent path traversal and injection attacks.
Types ¶
type Cacher ¶
type Cacher interface {
// Get retrieves a cached translation by its cache key.
// Returns the cached value and true on a hit, or an empty string and false on a miss.
Get(key string) (string, bool)
// Set stores a resolved translation under the given cache key.
Set(key string, value string)
// Invalidate discards all cached entries.
Invalidate()
}
Cacher abstracts resolved-translation caching for the Translator. Implementations must be safe for concurrent use by multiple goroutines. This interface is independent of TranslatorProvider and all other interfaces.
type Detector ¶
type Detector interface {
// Detect retrieves the system locale from environment variables.
// Returns a locale string (which may need normalization).
Detect() string
}
Detector retrieves the system locale from environment variables.
type EnvProvider ¶
type EnvProvider interface {
// Getenv returns the value of the environment variable named by the key.
Getenv(key string) string
}
EnvProvider abstracts environment variable access for testing.
type ErrInvalidFormat ¶
ErrInvalidFormat indicates that a format string is invalid.
func NewErrInvalidFormat ¶
func NewErrInvalidFormat(format string, cause error) *ErrInvalidFormat
NewErrInvalidFormat creates a new ErrInvalidFormat error for the given format identifier and underlying cause.
func (ErrInvalidFormat) Error ¶
func (e ErrInvalidFormat) Error() string
Error returns a formatted message including the format identifier and its cause.
func (ErrInvalidFormat) Is ¶
func (e ErrInvalidFormat) Is(target error) bool
Is implements error comparison for ErrInvalidFormat.
func (ErrInvalidFormat) Unwrap ¶
func (e ErrInvalidFormat) Unwrap() error
Unwrap returns the underlying cause of the error.
type ErrInvalidKey ¶
ErrInvalidKey indicates that a translation key is invalid.
func NewErrInvalidKey ¶
func NewErrInvalidKey(key string, cause error) *ErrInvalidKey
NewErrInvalidKey creates a new ErrInvalidKey error for the given key and underlying cause.
func (ErrInvalidKey) Error ¶
func (e ErrInvalidKey) Error() string
Error returns a formatted message including the invalid key and its cause.
func (ErrInvalidKey) Is ¶
func (e ErrInvalidKey) Is(target error) bool
Is implements error comparison for ErrInvalidKey.
func (ErrInvalidKey) Unwrap ¶
func (e ErrInvalidKey) Unwrap() error
Unwrap returns the underlying cause of the error.
type ErrInvalidLocale ¶
ErrInvalidLocale indicates that a locale string is invalid or malformed.
func NewErrInvalidLocale ¶
func NewErrInvalidLocale(locale string, cause error) *ErrInvalidLocale
NewErrInvalidLocale creates a new ErrInvalidLocale error for the given locale string and underlying cause.
func (ErrInvalidLocale) Error ¶
func (e ErrInvalidLocale) Error() string
Error returns a formatted message including the invalid locale and its cause.
func (ErrInvalidLocale) Is ¶
func (e ErrInvalidLocale) Is(target error) bool
Is implements error comparison for ErrInvalidLocale. Called by errors.Is() which handles unwrapping; this method only matches the immediate target.
func (ErrInvalidLocale) Unwrap ¶
func (e ErrInvalidLocale) Unwrap() error
Unwrap returns the underlying cause of the error.
type ErrKeyNotFound ¶
type ErrKeyNotFound struct {
Key string
}
ErrKeyNotFound indicates that a translation key was not found.
func NewErrKeyNotFound ¶
func NewErrKeyNotFound(key string) *ErrKeyNotFound
NewErrKeyNotFound creates a new ErrKeyNotFound error for the given key.
func (ErrKeyNotFound) Error ¶
func (e ErrKeyNotFound) Error() string
Error returns a formatted message including the missing key.
func (ErrKeyNotFound) Is ¶
func (e ErrKeyNotFound) Is(target error) bool
Is implements error comparison for ErrKeyNotFound.
type ErrPathTraversal ¶
type ErrPathTraversal struct {
Path string
}
ErrPathTraversal indicates that a path traversal attempt was detected.
func NewErrPathTraversal ¶
func NewErrPathTraversal(path string) *ErrPathTraversal
NewErrPathTraversal creates a new ErrPathTraversal error for the given path.
func (ErrPathTraversal) Error ¶
func (e ErrPathTraversal) Error() string
Error returns a formatted message including the offending path.
func (ErrPathTraversal) Is ¶
func (e ErrPathTraversal) Is(target error) bool
Is implements error comparison for ErrPathTraversal.
type ErrUnknownFormat ¶
type ErrUnknownFormat struct {
Extension string
}
ErrUnknownFormat indicates that no parser is registered for a given file extension.
func NewErrUnknownFormat ¶
func NewErrUnknownFormat(ext string) *ErrUnknownFormat
NewErrUnknownFormat creates a new ErrUnknownFormat error for the given extension string.
func (ErrUnknownFormat) Error ¶
func (e ErrUnknownFormat) Error() string
Error returns a formatted message including the unregistered extension.
func (ErrUnknownFormat) Is ¶
func (e ErrUnknownFormat) Is(target error) bool
Is implements error comparison for ErrUnknownFormat.
type FallbackChainer ¶
type FallbackChainer interface {
// GetChain returns the fallback chain for a given locale.
// For example, "es-MX" might return ["es-MX", "es-ES", "en-US"].
// The chain should always include a default fallback locale.
GetChain(locale string) []string
}
FallbackChainer generates fallback locale chains for graceful degradation.
type FormattedTranslator ¶
type FormattedTranslator interface {
// TranslateWithArgs looks up a translation key and formats it with arguments.
// Uses fmt.Sprintf formatting. If the key is not found, returns the key itself.
TranslateWithArgs(key string, args ...interface{}) string
}
FormattedTranslator provides formatted translation lookup with arguments.
type GenderCategory ¶
type GenderCategory string
GenderCategory represents a grammatical gender for message selection. The caller specifies the gender value; no linguistic rule engine is needed.
const ( Masculine GenderCategory = "masculine" Feminine GenderCategory = "feminine" Neuter GenderCategory = "neuter" GenderOther GenderCategory = "other" )
Grammatical gender categories for gender-aware message selection.
type GenderTranslator ¶
type GenderTranslator interface {
// TranslateGender looks up key.<gender>, falling back to key.other.
TranslateGender(key string, gender GenderCategory) string
}
GenderTranslator provides gender-aware translation lookup.
type KeyChecker ¶
type KeyChecker interface {
// HasKey checks if a translation key exists in the current locale or fallback chain.
HasKey(key string) bool
}
KeyChecker checks whether a translation key exists.
type KeyResolver ¶
type KeyResolver interface {
// Resolve looks up a translation key in the provided translations map.
// Keys use dot notation for nested values (e.g., "error.validation.required").
// Returns the translated string or an error if the key is invalid or not found.
Resolve(translations map[string]interface{}, key string) (string, error)
}
KeyResolver handles nested key resolution using dot notation. Implementations must enforce maximum key depth and length limits.
type LeveledLogger ¶
type LeveledLogger interface {
// Debug logs a debug-level message with optional format arguments.
Debug(msg string, args ...any)
// Info logs an info-level message with optional format arguments.
Info(msg string, args ...any)
// Warn logs a warn-level message with optional format arguments.
Warn(msg string, args ...any)
// Error logs an error-level message with optional format arguments.
Error(msg string, args ...any)
// Fatal logs a fatal-level message with optional format arguments.
Fatal(msg string, args ...any)
}
LeveledLogger defines minimal leveled logging without fields or context. Use this interface when you only need basic logging capabilities.
type LocaleDetector ¶
type LocaleDetector interface {
Detector
Normalizer
}
LocaleDetector detects and normalizes system locale information. It composes the Detector and Normalizer interfaces.
type LocaleGetter ¶
type LocaleGetter interface {
// GetLocale returns the current locale being used for translations.
GetLocale() string
}
LocaleGetter retrieves the active locale.
type LocaleSetter ¶
type LocaleSetter interface {
// SetLocale changes the current locale for translation lookups.
// The locale will be normalized before being set.
SetLocale(locale string)
}
LocaleSetter changes the active locale for translation lookups.
type Logger ¶
type Logger interface {
// LeveledLogger provides the core logging methods.
LeveledLogger
// WithFields returns a new Logger with additional structured fields.
// Fields are provided as key-value pairs: ("user_id", 123, "action", "login").
WithFields(fields ...any) Logger
// WithContext returns a new Logger with the given context.
WithContext(ctx context.Context) Logger
// WithLevel returns a new Logger that only logs at or above the given level.
WithLevel(level LogLevel) Logger
// Enabled returns true if logging at the given level would produce output.
Enabled(level LogLevel) bool
}
Logger defines the logging interface accepted by the i18n package. This interface is compatible with github.com/0verkilll/logger.Logger and allows any implementation that satisfies these methods.
The interface follows SOLID principles:
- Single Responsibility: focused on logging operations
- Open/Closed: extensible through implementation
- Liskov Substitution: any implementation is substitutable
- Interface Segregation: LeveledLogger provides minimal subset
- Dependency Inversion: depend on this abstraction, not concrete loggers
type Normalizer ¶
type Normalizer interface {
// Normalize converts locale strings to BCP 47 format.
// Handles various input formats (en_US, en_US.UTF-8, etc.) and
// returns a standardized BCP 47 code (e.g., "en-US").
Normalize(locale string) string
}
Normalizer converts locale strings to BCP 47 format.
type PluralCategory ¶
type PluralCategory string
PluralCategory represents a CLDR plural category. Values are lowercase strings that double as key suffixes in translation files.
const ( Zero PluralCategory = "zero" One PluralCategory = "one" Two PluralCategory = "two" Few PluralCategory = "few" Many PluralCategory = "many" Other PluralCategory = "other" )
CLDR plural categories used by plural rules to select the correct translation form.
type PluralResolver ¶
type PluralResolver interface {
// Resolve returns the plural category for the given locale and count.
// The count parameter accepts int, int64, float64, and string (numeric string).
// Returns Other for unrecognized types or unknown locales.
Resolve(locale string, count interface{}) PluralCategory
}
PluralResolver determines the plural category for a given locale and count.
type PluralTranslator ¶
type PluralTranslator interface {
// TranslatePlural resolves the plural category for the current locale and count,
// looks up key.<category>, and falls back to key.other.
TranslatePlural(key string, count interface{}) string
}
PluralTranslator provides count-dependent translation lookup.
type TranslationLoader ¶
type TranslationLoader interface {
// Load retrieves translation data for the specified locale.
// The locale parameter must be a valid BCP 47 code.
// Returns the raw translation data or an error if loading fails.
Load(locale string) ([]byte, error)
}
TranslationLoader abstracts loading translation data from various sources. Implementations must ensure secure file access and prevent path traversal attacks.
type TranslationLookup ¶
type TranslationLookup interface {
// Translate looks up a translation key in the current locale.
// If the key is not found, it tries the fallback chain.
// Returns the key itself if not found in any locale.
Translate(key string) string
}
TranslationLookup provides single-key translation lookup.
type TranslationParser ¶
type TranslationParser interface {
// Parse converts raw translation data into a structured format.
// The data should be validated for security issues (nesting depth, size, etc.).
// Returns a map of translation keys to values (which may be nested) or an error.
Parse(data []byte) (map[string]interface{}, error)
}
TranslationParser abstracts parsing translation data formats. Implementations must validate input and prevent malicious data from causing issues.
type TranslatorProvider ¶
type TranslatorProvider interface {
TranslationLookup
FormattedTranslator
PluralTranslator
GenderTranslator
KeyChecker
LocaleSetter
LocaleGetter
}
TranslatorProvider defines the interface for translation services. This interface allows other packages to accept translation capabilities without creating a hard dependency on the full i18n package.
TranslatorProvider composes seven single-responsibility sub-interfaces: TranslationLookup, FormattedTranslator, KeyChecker, LocaleSetter, LocaleGetter, PluralTranslator, and GenderTranslator. Consumers that need only a subset of these capabilities should accept the narrower sub-interface instead.
Integration Pattern ¶
Other Go packages can support optional i18n by defining a local interface matching this signature. Application developers then pass their i18n.Translator instance to the package's SetTranslator() function.
Example usage in a package:
// In mypackage/i18n.go
type TranslatorProvider interface {
Translate(key string) string
TranslateWithArgs(key string, args ...interface{}) string
TranslatePlural(key string, count interface{}) string
TranslateGender(key string, gender i18n.GenderCategory) string
HasKey(key string) bool
SetLocale(locale string)
GetLocale() string
}
var globalTranslator TranslatorProvider
func SetTranslator(t TranslatorProvider) {
globalTranslator = t
}
Application developers can then use it:
translator, _ := i18n.New(
i18n.WithFileSystemLoader("locales"),
i18n.WithDefaultLocale("en-US"),
)
mypackage.SetTranslator(translator)
Benefits ¶
- No forced dependencies: Packages work with or without i18n
- Shared translator: One translator instance serves all packages
- Centralized control: Application manages all translations
- Language switching: Changes affect all integrated packages
The Translator type in this package implements TranslatorProvider automatically.