i18n

package
v0.35.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 26, 2026 License: MIT Imports: 14 Imported by: 0

README

i18n - Custom Internationalization Library

Custom-built internationalization system for matcha email client. Zero external i18n dependencies - everything implemented from scratch.

Table of Contents

Architecture Overview

┌─────────────────────────────────────────────────────────────┐
│                      Application Layer                      │
│              (TUI components call t(), tn(), tpl())         │
└──────────────────────────┬──────────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────────┐
│                    Global Manager                           │
│  • Singleton instance                                       │
│  • Current language state                                   │
│  • Localizer instances                                      │
└──────────────────────────┬──────────────────────────────────┘
                           │
         ┌─────────────────┼─────────────────┐
         │                 │                 │
┌────────▼────────┐ ┌─────▼──────┐ ┌───────▼────────┐
│     Bundle      │ │  Localizer │ │     Cache      │
│  All messages   │ │  Per-lang  │ │  Translations  │
│  All locales    │ │  instance  │ │  Rendered text │
└────────┬────────┘ └─────┬──────┘ └────────────────┘
         │                │
         │         ┌──────▼──────────┐
         │         │   Pluralizer    │
         │         │  Plural rules   │
         │         └─────────────────┘
         │
┌────────▼────────────────────────────────────────────────────┐
│                   Translation Files                         │
│            locales/en.json, locales/uk.json, ...            │
└─────────────────────────────────────────────────────────────┘

Directory Structure

i18n/
├── manager.go           # Global singleton manager
├── bundle.go            # Translation bundle storage
├── locale.go            # Locale representation
├── message.go           # Message structure (with plural forms)
├── localizer.go         # Per-language translation engine
├── loader.go            # Load & embed translation files
├── parser.go            # JSON parser for translation files
├── plural_rules.go      # Plural rule functions for all languages
├── pluralizer.go        # Plural form selection logic
├── template.go          # Simple {var} template engine
├── interpolator.go      # Variable interpolation in strings
├── cache.go             # In-memory translation cache
├── fallback.go          # Fallback chain (uk → en)
├── detector.go          # Auto-detect user language
├── registry.go          # Available languages registry
├── formatter.go         # Number formatting per locale
├── date_formatter.go    # Date/time formatting per locale
├── context.go           # Context keys for locale passing
├── errors.go            # Error types
├── init.go              # Package initialization
├── embed.go             # Embed translation files in binary
├── languages/           # Language-specific implementations
│   ├── base.go          # Base language interface
│   ├── en.go            # English locale registration
│   ├── uk.go            # Ukrainian locale registration
│   ├── es.go            # Spanish locale registration
│   └── ...              # Other languages
└── locales/             # Translation JSON files
    ├── en.json          # English translations (base)
    ├── uk.json          # Ukrainian translations
    └── ...              # Other language files

Core Components

Translation Manager

File: manager.go

Global singleton that coordinates all i18n operations. Provides three main translation functions:

  • T(key) - Simple translation lookup
  • Tn(key, count, data) - Translation with pluralization
  • Tpl(key, data) - Translation with template variables
// Usage in TUI components
import "github.com/floatpane/matcha/i18n"

func render() string {
    title := i18n.GetManager().T("composer.title")
    count := i18n.GetManager().Tn("inbox.unread", 5, map[string]interface{}{
        "count": 5,
    })
    return title + " - " + count
}

Maintains:

  • Current active language
  • Bundle with all loaded translations
  • Localizer instances per language
  • Translation cache
Bundle System

File: bundle.go

Central storage for all translation messages across all languages. Thread-safe with RWMutex.

type Bundle struct {
    defaultLang string
    messages    map[string]MessageMap  // lang → message ID → Message
    locales     map[string]*Locale     // lang → Locale info
    mu          sync.RWMutex
}

Responsibilities:

  • Store translations for all languages
  • Retrieve messages by language + key
  • Register locale definitions
  • List available languages
Localizer

File: localizer.go

Per-language translation engine. Each active language gets one localizer instance.

type Localizer struct {
    lang    string
    bundle  *Bundle
    locale  *Locale
    cache   *Cache
}

Core methods:

  • Localize(messageID) - Basic lookup
  • LocalizePlural(messageID, count, data) - With plural rules
  • LocalizeTemplate(messageID, data) - With variable substitution

Handles:

  • Message lookup with fallback
  • Plural form selection
  • Template interpolation
  • Result caching
Plural Rules Engine

Files: plural_rules.go, pluralizer.go

Implements CLDR plural rules for all supported languages.

type PluralForm int

const (
    Zero PluralForm = iota  // 0 items
    One                     // 1 item
    Two                     // 2 items (Arabic, Welsh)
    Few                     // 2-4 items (Slavic languages)
    Many                    // 5+ items (Slavic languages)
    Other                   // Default/fallback
)

Each language has specific rules:

English/Spanish: Simple (one/other)

func EnglishPlural(n int) PluralForm {
    if n == 1 {
        return One
    }
    return Other
}

Ukrainian/Russian: Complex (one/few/many)

func UkrainianPlural(n int) PluralForm {
    mod10 := n % 10
    mod100 := n % 100
    
    if mod10 == 1 && mod100 != 11 {
        return One     // 1, 21, 31, 41...
    }
    if mod10 >= 2 && mod10 <= 4 && (mod100 < 12 || mod100 > 14) {
        return Few     // 2-4, 22-24, 32-34...
    }
    return Many        // 0, 5-20, 25-30...
}

Arabic: Very complex (zero/one/two/few/many/other)

Template System

Files: template.go, interpolator.go

Simple variable substitution using {variable} syntax.

template := "Update available: {latest} (current: {current})"
data := map[string]interface{}{
    "latest": "1.5.0",
    "current": "1.4.0",
}
result := Interpolate(template, data)
// → "Update available: 1.5.0 (current: 1.4.0)"

Design philosophy: Keep simple - no complex logic, just variable replacement. Complex formatting done in Go code before passing to templates.

Language Registry

File: registry.go

Global registry of all available languages. Languages self-register on init.

// In languages/uk.go
func init() {
    i18n.RegisterLanguage(&i18n.Locale{
        Code:       "uk",
        Name:       "Ukrainian",
        NativeName: "Українська",
        Direction:  "ltr",
        PluralFunc: i18n.UkrainianPlural,
    })
}

Registry provides:

  • GetLanguage(code) - Lookup locale by code
  • AvailableLanguages() - List all registered locales
  • LanguageCodes() - List all language codes

Data Flow

Translation Lookup Flow
1. TUI calls t("composer.title")
              ↓
2. Manager.T() → GetLocalizer(currentLang)
              ↓
3. Localizer checks cache
   • Cache hit? → return cached value
   • Cache miss? → continue
              ↓
4. Bundle.GetMessage(lang, "composer.title")
   • Found? → return Message
   • Not found? → try fallback chain (uk → en)
              ↓
5. Return message.One (singular form)
              ↓
6. Cache result for future lookups
              ↓
7. Return translated string to TUI
Pluralization Flow
1. TUI calls tn("inbox.unread", 5, {"count": 5})
              ↓
2. Manager.Tn() → GetLocalizer(currentLang)
              ↓
3. Get Message from bundle
              ↓
4. Call locale.PluralFunc(5)
   • English: 5 → Other
   • Ukrainian: 5 → Many
   • Arabic: 5 → Few
              ↓
5. Select appropriate form from Message:
   • message.One (n=1)
   • message.Few (n=2-4 in Ukrainian)
   • message.Many (n=5+ in Ukrainian)
   • message.Other (fallback)
              ↓
6. Interpolate {count} → "5"
              ↓
7. Return "5 листів" (Ukrainian) or "5 emails" (English)

Translation File Format

Location: locales/*.json

Structure:

{
  "language": "uk",
  "messages": {
    "common": {
      "yes": "Так",
      "no": "Ні",
      "save": "Зберегти"
    },
    "composer": {
      "title": "Написати новий лист",
      "send": "Надіслати",
      "from": "Від"
    },
    "inbox": {
      "unread": {
        "one": "{count} непрочитаний лист",
        "few": "{count} непрочитані листи",
        "other": "{count} непрочитаних листів"
      }
    }
  }
}

Nested structure: Dot notation for keys (composer.title, inbox.unread)

Plural objects: When value is object with one/few/many/other, it's treated as plural form.

Plural Forms

Languages use different plural categories:

Language Forms Used Example (emails)
English one, other 1 email, 2 emails
Spanish one, other 1 correo, 2 correos
German one, other 1 E-Mail, 2 E-Mails
French one, other 0 courriel, 1 courriel, 2 courriels
Ukrainian one, few, other 1 лист, 2 листи, 5 листів
Russian one, few, other 1 письмо, 2 письма, 5 писем
Polish one, few, many, other 1 e-mail, 2 e-maile, 5 e-maili
Arabic zero, one, two, few, many, other Complex rules
Japanese other All numbers use same form
Chinese other All numbers use same form

Caching Strategy

File: cache.go

Simple in-memory string cache with RWMutex for thread safety.

Cache key format: {lang}:{messageID}:{count}:{hash(data)}

Why cache?

  • Translation lookup involves: map lookups, plural calculation, interpolation
  • Typical UI has ~100 translated strings
  • Redrawing UI on every keypress = wasted CPU
  • Cache hit = instant string return

Cache invalidation: Cache cleared when language changes via SetLanguage().

No size limit: Translation cache is small (~KB for entire UI) and only grows to unique message combinations actually used.

Language Detection

File: detector.go

Detection priority:

  1. Config file: language = "uk" in ~/.config/matcha/config.toml
  2. Environment: LANG, LC_ALL, LC_MESSAGES
  3. System locale: OS-specific detection
  4. Default: Falls back to "en"
func DetectLanguage(cfg *config.Config) string {
    // 1. Check config
    if cfg.Language != "" && isValidLanguage(cfg.Language) {
        return cfg.Language
    }
    
    // 2. Check environment
    if lang := detectFromEnv(); lang != "" {
        return lang
    }
    
    // 3. Check system
    if lang := detectFromSystem(); lang != "" {
        return lang
    }
    
    // 4. Default
    return "en"
}

Normalizes language codes: en_US.UTF-8en, uk_UAuk

Adding/Editing Languages

Adding a New Language

1. Create translation file:

cd i18n/locales
cp en.json xx.json  # Replace 'xx' with language code (ISO 639-1)

2. Update language code:

{
  "language": "xx",
  "messages": { ... }
}

3. Translate all strings:

  • Keep JSON structure identical to en.json
  • Translate all message values
  • Preserve placeholders: {count}, {latest}, {current}
  • Don't translate technical terms: S/MIME, PGP, IMAP, SMTP
  • Don't translate commands: matcha update

4. Handle plural forms:

Check plural rules for your language at CLDR Plural Rules.

Add plural forms as needed:

"hours_ago": {
  "one": "{count} hour ago",
  "other": "{count} hours ago"
}

For complex plurals (Ukrainian, Arabic, Polish):

"address_count": {
  "one": "{count} адреса",
  "few": "{count} адреси",
  "other": "{count} адрес"
}

5. Register language (if not already in languages/):

Create languages/xx.go:

package languages

import "github.com/floatpane/matcha/i18n"

func init() {
    i18n.RegisterLanguage(&i18n.Locale{
        Code:       "xx",
        Name:       "YourLanguage",
        NativeName: "YourLanguageInNativeScript",
        Direction:  "ltr",  // or "rtl" for Arabic, Hebrew, etc.
        PluralFunc: i18n.YourLanguagePlural,
    })
}

If plural function doesn't exist in plural_rules.go, add it:

// In plural_rules.go
func YourLanguagePlural(n int) PluralForm {
    // Implement CLDR rules for your language
    if n == 1 {
        return One
    }
    return Other
}

6. Test:

go build
# Edit config: language = "xx"
./matcha

Verify:

  • All UI elements translated
  • Plural forms work (test with 0, 1, 2, 5, 21 items)
  • Variables interpolate correctly
  • No English leaks through
Editing Existing Translations

1. Open translation file:

vi i18n/locales/uk.json

2. Find key using dot notation:

Translation keys follow UI structure:

  • composer.* - Email composer
  • inbox.* - Inbox view
  • settings_general.* - General settings
  • common.* - Shared elements

3. Update translation:

{
  "composer": {
    "title": "Old translation"  // Change this
  }
}

4. Rebuild and test:

go build
./matcha

Language changes apply instantly (no restart needed).

Translation Guidelines

Do translate:

  • All visible UI text
  • Button labels
  • Menu items
  • Help text
  • Tips
  • Error messages shown to user
  • Status messages

Don't translate:

  • Backend error logs
  • Debug messages
  • Protocol names (IMAP, SMTP, S/MIME, PGP)
  • File paths (~/.config/matcha/)
  • Environment variables ($EDITOR)
  • Commands (matcha update)
  • Technical identifiers

Variable placeholders:

Always keep variables unchanged:

// ✅ Correct
"update_available": "Доступне оновлення: {latest} (встановлено: {current})"

// ❌ Wrong - renamed variable
"update_available": "Доступне оновлення: {останній} (встановлено: {поточний})"
Testing Checklist
  • All screens display in target language
  • Test plural forms with different counts (0, 1, 2, 5, 21)
  • Variables interpolate correctly
  • No untranslated English text (except technical terms)
  • Text fits in UI (not truncated)
  • Special characters render properly
  • RTL languages display correctly (Arabic, Hebrew)
  • Date/time formats work
  • Help text makes sense in context
Contributing Translations
  1. Fork repository
  2. Add/edit translation file in i18n/locales/
  3. Test thoroughly with checklist above
  4. Submit pull request with:
    • Translation file
    • Screenshots of translated UI
    • Note about plural form testing
    • List any technical challenges

Translation quality > completeness. Better to have 80% high-quality translation than 100% machine-translated text.


For more details on using translations in code, see: Documentation.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrLanguageNotFound is returned when a requested language is not available.
	ErrLanguageNotFound = errors.New("language not found")

	// ErrMessageNotFound is returned when a translation key does not exist.
	ErrMessageNotFound = errors.New("message not found")

	// ErrInvalidLocale is returned when a locale code is malformed.
	ErrInvalidLocale = errors.New("invalid locale code")

	// ErrLoadFailed is returned when translation files fail to load.
	ErrLoadFailed = errors.New("failed to load translations")

	// ErrParseFailed is returned when a translation file cannot be parsed.
	ErrParseFailed = errors.New("failed to parse translation file")

	// ErrNoDefaultLanguage is returned when no default language is set.
	ErrNoDefaultLanguage = errors.New("no default language set")
)

Functions

func DetectLanguage

func DetectLanguage(cfg *config.Config) string

DetectLanguage determines the language to use based on config and environment.

func HasLanguage

func HasLanguage(code string) bool

HasLanguage checks if a language code is registered.

func Init

func Init(defaultLang string) error

Init initializes the global translation manager with a default language.

func Interpolate

func Interpolate(template string, data map[string]interface{}) string

Interpolate replaces placeholders in a template string with values from data. Supports {key} syntax for variable interpolation.

func LanguageCodes

func LanguageCodes() []string

LanguageCodes returns all registered language codes.

func LoadFromDirectory

func LoadFromDirectory(bundle *Bundle, dir string) error

LoadFromDirectory loads translation files from a directory on disk. This allows overriding embedded translations with external files.

func LoadTranslations

func LoadTranslations(bundle *Bundle) error

LoadTranslations loads all translation files into a bundle. First attempts to load from embedded files, then checks for external files.

func LocaleFromContext

func LocaleFromContext(ctx context.Context) string

LocaleFromContext extracts the locale from context. Returns empty string if no locale is set.

func RegisterLanguage

func RegisterLanguage(locale *Locale)

RegisterLanguage registers a locale in the global registry. This is typically called from init() functions in language files.

func WithLocale

func WithLocale(ctx context.Context, locale string) context.Context

WithLocale returns a new context with the given locale.

Types

type Bundle

type Bundle struct {
	// contains filtered or unexported fields
}

Bundle holds all translation messages for all languages.

func NewBundle

func NewBundle(defaultLang string) *Bundle

NewBundle creates a new Bundle with a default language.

func (*Bundle) AddMessages

func (b *Bundle) AddMessages(lang string, messages MessageMap) error

AddMessages adds translation messages for a language.

func (*Bundle) AvailableLanguages

func (b *Bundle) AvailableLanguages() []string

AvailableLanguages returns a list of all languages with loaded messages.

func (*Bundle) DefaultLanguage

func (b *Bundle) DefaultLanguage() string

DefaultLanguage returns the default language code.

func (*Bundle) GetLocale

func (b *Bundle) GetLocale(lang string) (*Locale, bool)

GetLocale retrieves a registered locale.

func (*Bundle) GetMessage

func (b *Bundle) GetMessage(lang, id string) (*Message, error)

GetMessage retrieves a message for a specific language and ID.

func (*Bundle) HasLanguage

func (b *Bundle) HasLanguage(lang string) bool

HasLanguage checks if a language has been loaded.

func (*Bundle) MessageCount

func (b *Bundle) MessageCount(lang string) int

MessageCount returns the number of messages for a language.

func (*Bundle) RegisterLocale

func (b *Bundle) RegisterLocale(locale *Locale)

RegisterLocale registers a locale configuration.

type Cache

type Cache struct {
	// contains filtered or unexported fields
}

Cache provides thread-safe caching for translated strings.

func NewCache

func NewCache() *Cache

NewCache creates a new Cache.

func (*Cache) Clear

func (c *Cache) Clear()

Clear removes all cached values.

func (*Cache) Delete

func (c *Cache) Delete(key string)

Delete removes a specific key from the cache.

func (*Cache) Get

func (c *Cache) Get(key string) (string, bool)

Get retrieves a cached value.

func (*Cache) Has

func (c *Cache) Has(key string) bool

Has checks if a key exists in the cache.

func (*Cache) Set

func (c *Cache) Set(key, value string)

Set stores a value in the cache.

func (*Cache) Size

func (c *Cache) Size() int

Size returns the number of cached items.

type DateFormatter

type DateFormatter struct {
	// contains filtered or unexported fields
}

DateFormatter formats dates and times according to locale rules.

func NewDateFormatter

func NewDateFormatter(locale *Locale) *DateFormatter

NewDateFormatter creates a date formatter for a locale.

func (*DateFormatter) FormatDate

func (f *DateFormatter) FormatDate(t time.Time, layout string) string

FormatDate formats a time according to the given layout.

func (*DateFormatter) FormatDateTime

func (f *DateFormatter) FormatDateTime(t time.Time) string

FormatDateTime formats both date and time.

func (*DateFormatter) FormatRelative

func (f *DateFormatter) FormatRelative(t time.Time) string

FormatRelative formats a time relative to now (e.g., "5 minutes ago"). This should use translated strings from the message catalog.

func (*DateFormatter) FormatTime

func (f *DateFormatter) FormatTime(t time.Time) string

FormatTime formats just the time portion.

type FallbackChain

type FallbackChain struct {
	// contains filtered or unexported fields
}

FallbackChain defines a sequence of languages to try when looking up translations.

func NewFallbackChain

func NewFallbackChain(preferred string, defaults ...string) *FallbackChain

NewFallbackChain creates a new fallback chain with a preferred language and defaults. Example: NewFallbackChain("pt-BR", "pt", "en") creates chain: pt-BR → pt → en

func (*FallbackChain) Languages

func (f *FallbackChain) Languages() []string

Languages returns the ordered list of languages in the fallback chain.

func (*FallbackChain) Resolve

func (f *FallbackChain) Resolve(bundle *Bundle, key string) (*Message, string, error)

Resolve attempts to find a message in the fallback chain. Returns the message, the language it was found in, and any error.

type Locale

type Locale struct {
	// Tag is the BCP 47 language tag
	Tag language.Tag

	// Code is the short language code (e.g., "en", "es", "de")
	Code string

	// Name is the English name of the language
	Name string

	// NativeName is the language's name in its own language
	NativeName string

	// Direction is the text direction ("ltr" or "rtl")
	Direction string

	// PluralFunc is the plural rule function for this language
	PluralFunc PluralFunc
}

Locale represents a language/region configuration.

func AvailableLanguages

func AvailableLanguages() []*Locale

AvailableLanguages returns all registered locales.

func GetLanguage

func GetLanguage(code string) (*Locale, bool)

GetLanguage retrieves a registered locale by code.

func ParseLocale

func ParseLocale(code string) (*Locale, error)

ParseLocale parses a language code and returns a Locale. Supports formats like "en", "en-US", "en_US".

func (*Locale) IsRTL

func (l *Locale) IsRTL() bool

IsRTL returns true if the locale uses right-to-left text direction.

func (*Locale) String

func (l *Locale) String() string

String returns the string representation of the locale.

type Localizer

type Localizer struct {
	// contains filtered or unexported fields
}

Localizer handles translation lookups for a specific language.

func NewLocalizer

func NewLocalizer(lang string, bundle *Bundle) *Localizer

NewLocalizer creates a new Localizer for a language.

func (*Localizer) ClearCache

func (l *Localizer) ClearCache()

ClearCache clears the localizer's cache.

func (*Localizer) Language

func (l *Localizer) Language() string

Language returns the localizer's language code.

func (*Localizer) Locale

func (l *Localizer) Locale() *Locale

Locale returns the localizer's locale.

func (*Localizer) Localize

func (l *Localizer) Localize(messageID string) string

Localize translates a message ID to text.

func (*Localizer) LocalizePlural

func (l *Localizer) LocalizePlural(messageID string, count int, data map[string]interface{}) string

LocalizePlural translates a message with plural support.

func (*Localizer) LocalizeTemplate

func (l *Localizer) LocalizeTemplate(messageID string, data map[string]interface{}) string

LocalizeTemplate translates a message and applies template data.

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager is the global translation manager.

func GetManager

func GetManager() *Manager

GetManager returns the global manager instance.

func (*Manager) AvailableLanguages

func (m *Manager) AvailableLanguages() []string

AvailableLanguages returns all loaded languages.

func (*Manager) ClearCache

func (m *Manager) ClearCache()

ClearCache clears all translation caches.

func (*Manager) GetLanguage

func (m *Manager) GetLanguage() string

GetLanguage returns the current language code.

func (*Manager) GetLocale

func (m *Manager) GetLocale() *Locale

GetLocale returns the current locale.

func (*Manager) SetLanguage

func (m *Manager) SetLanguage(lang string) error

SetLanguage changes the current language.

func (*Manager) T

func (m *Manager) T(key string) string

T translates a message key using the current language.

func (*Manager) Tn

func (m *Manager) Tn(key string, count int, data map[string]interface{}) string

Tn translates a message with plural support.

func (*Manager) Tpl

func (m *Manager) Tpl(key string, data map[string]interface{}) string

Tpl translates a message and applies template variables.

type Message

type Message struct {
	// ID is the unique identifier for this message (e.g., "composer.title")
	ID string `json:"id"`

	// Description provides context for translators
	Description string `json:"description,omitempty"`

	// Hash is an optional content hash for tracking changes
	Hash string `json:"hash,omitempty"`

	// Zero form is used when count is exactly 0 (optional)
	Zero string `json:"zero,omitempty"`

	// One form is used for singular (count == 1)
	One string `json:"one,omitempty"`

	// Two form is used for dual (count == 2) in some languages
	Two string `json:"two,omitempty"`

	// Few form is used for small counts in some languages (e.g., Polish)
	Few string `json:"few,omitempty"`

	// Many form is used for larger counts in some languages (e.g., Russian)
	Many string `json:"many,omitempty"`

	// Other is the default form used when no specific plural form matches
	Other string `json:"other,omitempty"`
}

Message represents a translatable message with support for plural forms.

func (*Message) GetDefault

func (m *Message) GetDefault() string

GetDefault returns the most appropriate default text (tries Other, then One).

func (*Message) GetText

func (m *Message) GetText(form PluralForm) string

GetText returns the appropriate text for the given plural form.

func (*Message) Pluralize

func (m *Message) Pluralize(count int, pluralFunc PluralFunc) string

Pluralize returns the appropriate text from a message based on count and plural rules.

type MessageMap

type MessageMap map[string]*Message

MessageMap maps message IDs to Message structs.

func ParseJSON

func ParseJSON(data []byte) (MessageMap, error)

ParseJSON parses a JSON translation file and returns a MessageMap.

type NumberFormatter

type NumberFormatter struct {
	// contains filtered or unexported fields
}

NumberFormatter formats numbers according to locale rules.

func NewNumberFormatter

func NewNumberFormatter(locale *Locale) *NumberFormatter

NewNumberFormatter creates a formatter for a locale.

func (*NumberFormatter) FormatFileSize

func (f *NumberFormatter) FormatFileSize(bytes int64) string

FormatFileSize formats a byte count as a human-readable size.

func (*NumberFormatter) FormatFloat

func (f *NumberFormatter) FormatFloat(n float64, precision int) string

FormatFloat formats a float64 with the specified precision.

func (*NumberFormatter) FormatInt

func (f *NumberFormatter) FormatInt(n int) string

FormatInt formats an integer according to locale rules.

func (*NumberFormatter) FormatInt64

func (f *NumberFormatter) FormatInt64(n int64) string

FormatInt64 formats an int64 according to locale rules.

func (*NumberFormatter) FormatPercent

func (f *NumberFormatter) FormatPercent(n float64) string

FormatPercent formats a number as a percentage (0.5 -> "50%").

type PluralForm

type PluralForm int

PluralForm represents the different plural categories.

const (
	// Zero is used when count is exactly 0
	Zero PluralForm = iota
	// One is used for singular (typically count == 1)
	One
	// Two is used for dual (count == 2) in some languages
	Two
	// Few is used for small counts in some languages
	Few
	// Many is used for larger counts in some languages
	Many
	// Other is the default/fallback form
	Other
)

func ArabicPlural

func ArabicPlural(n int) PluralForm

ArabicPlural implements plural rules for Arabic. Rule: zero (n == 0)

one (n == 1)
two (n == 2)
few (n mod 100 in 3..10)
many (n mod 100 in 11..99)
other (everything else)

func ChinesePlural

func ChinesePlural(n int) PluralForm

ChinesePlural implements plural rules for Chinese. Rule: other (always - no plural distinction)

func CzechPlural

func CzechPlural(n int) PluralForm

CzechPlural implements plural rules for Czech. Rule: one (n == 1)

few (n in 2..4)
many (everything else)

func DefaultPlural

func DefaultPlural(n int) PluralForm

DefaultPlural is a simple plural function (English-like: 1 = one, else = other).

func EnglishPlural

func EnglishPlural(n int) PluralForm

EnglishPlural implements plural rules for English. Rule: one (n == 1), other (everything else)

func FrenchPlural

func FrenchPlural(n int) PluralForm

FrenchPlural implements plural rules for French. Rule: one (n == 0 or n == 1), other (everything else)

func GermanPlural

func GermanPlural(n int) PluralForm

GermanPlural implements plural rules for German. Rule: one (n == 1), other (everything else)

func ItalianPlural

func ItalianPlural(n int) PluralForm

ItalianPlural implements plural rules for Italian. Rule: one (n == 1), other (everything else)

func JapanesePlural

func JapanesePlural(n int) PluralForm

JapanesePlural implements plural rules for Japanese. Rule: other (always - no plural distinction)

func PolishPlural

func PolishPlural(n int) PluralForm

PolishPlural implements plural rules for Polish. Rule: one (n == 1)

few (n mod 10 in 2..4 and n mod 100 not in 12..14)
many (everything else)

func PortuguesePlural

func PortuguesePlural(n int) PluralForm

PortuguesePlural implements plural rules for Portuguese. Rule: one (n == 0 or n == 1), other (everything else)

func RussianPlural

func RussianPlural(n int) PluralForm

RussianPlural implements plural rules for Russian. Rule: one (n mod 10 == 1 and n mod 100 != 11)

few (n mod 10 in 2..4 and n mod 100 not in 12..14)
many (everything else)

func SpanishPlural

func SpanishPlural(n int) PluralForm

SpanishPlural implements plural rules for Spanish. Rule: one (n == 1), other (everything else)

func UkrainianPlural

func UkrainianPlural(n int) PluralForm

UkrainianPlural implements plural rules for Ukrainian. Rule: one (n mod 10 == 1 and n mod 100 != 11)

few (n mod 10 in 2..4 and n mod 100 not in 12..14)
many (everything else)

Same as Russian

func (PluralForm) String

func (p PluralForm) String() string

String returns the string representation of the plural form.

type PluralFunc

type PluralFunc func(n int) PluralForm

PluralFunc is a function that returns the appropriate plural form for a count.

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry holds all registered language locales.

type Template

type Template struct {
	// contains filtered or unexported fields
}

Template represents a parsed template string with placeholders.

func NewTemplate

func NewTemplate(s string) *Template

NewTemplate parses a template string and returns a Template.

func (*Template) Execute

func (t *Template) Execute(data map[string]interface{}) string

Execute applies data to the template and returns the result.

func (*Template) String

func (t *Template) String() string

String returns the raw template string.

type TranslationFile

type TranslationFile struct {
	Language string                 `json:"language"`
	Messages map[string]interface{} `json:"messages"`
}

TranslationFile represents the structure of a JSON translation file.

type ValidationError

type ValidationError struct {
	Language string
	Key      string
	Message  string
}

ValidationError represents a validation issue.

type ValidationResult

type ValidationResult struct {
	Valid   bool
	Errors  []ValidationError
	Missing map[string][]string // lang -> missing keys
	Extra   map[string][]string // lang -> extra keys
}

ValidationResult contains the results of validating translation files.

func ValidateTranslations

func ValidateTranslations(bundle *Bundle, baseLang string) *ValidationResult

ValidateTranslations validates all translations against a base language. Checks for missing keys, extra keys, and consistency.

func (*ValidationResult) String

func (v *ValidationResult) String() string

String returns a human-readable validation report.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL