i18n

package
v0.1.18 Latest Latest
Warning

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

Go to latest
Published: May 1, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package i18n provides internationalization support for the gogpu/ui toolkit.

The i18n package enables string localization, locale management, plural rules, and text direction (LTR/RTL) detection. It integrates with the reactive state system via state.Signal for dynamic locale switching.

Core Types

  • Locale — represents a language/region combination (e.g., "en-US", "ru-RU")
  • Bundle — holds message translations for a single locale
  • Translator — resolves messages with locale fallback chains
  • Direction — text direction (LTR or RTL)
  • PluralForms — plural category forms (zero, one, two, few, many, other)

Basic Usage

// Create translator with English fallback
t := i18n.NewTranslator(i18n.NewLocale("en", "US"))

// Add English bundle
en := i18n.NewBundle(i18n.NewLocale("en", "US"))
en.Set("greeting", "Hello!")
en.SetPlural("items", i18n.PluralForms{
    One:   "%d item",
    Other: "%d items",
})
t.AddBundle(en)

// Add Russian bundle
ru := i18n.NewBundle(i18n.NewLocale("ru", "RU"))
ru.Set("greeting", "Привет!")
t.AddBundle(ru)

// Resolve messages
t.T("greeting")          // "Hello!"
t.SetLocale(i18n.NewLocale("ru", "RU"))
t.T("greeting")          // "Привет!"

Plural Support

Plural rules follow CLDR categories (zero, one, two, few, many, other). Built-in rules are provided for English, Russian, and Arabic:

t.Tp("items", 1)   // "1 item"    (one)
t.Tp("items", 5)   // "5 items"   (other)
t.Tpf("items", 3, 3) // "3 items" (other, formatted)

Reactive Locale Switching

The Translator exposes a state.ReadonlySignal for the current locale, enabling reactive UI updates when the locale changes:

localeSig := t.LocaleSignal()
state.Effect(func() {
    fmt.Println("Locale changed to:", localeSig.Get())
})

Text Direction

RTL detection is built in for Arabic, Hebrew, Persian, and Urdu:

locale := i18n.NewLocale("ar", "SA")
locale.Direction() // i18n.RTL

Thread Safety

Translator is safe for concurrent access from multiple goroutines. All methods use a sync.RWMutex internally. Bundle is NOT thread-safe; it should be fully populated before being added to a Translator.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Bundle

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

Bundle is a message catalog for a single locale.

A Bundle holds key-value message pairs and optional plural forms for a specific locale. Bundles are NOT thread-safe; they should be fully populated before being added to a Translator.

Example:

en := i18n.NewBundle(i18n.NewLocale("en", "US"))
en.Set("welcome", "Welcome!")
en.SetPlural("files", i18n.PluralForms{
    One:   "%d file",
    Other: "%d files",
})

func NewBundle

func NewBundle(locale Locale) *Bundle

NewBundle creates a new empty Bundle for the given locale.

func (*Bundle) Get

func (b *Bundle) Get(key string) (string, bool)

Get retrieves a simple message by key.

Returns the message and true if found, or empty string and false if not.

func (*Bundle) GetPlural

func (b *Bundle) GetPlural(key string) (PluralForms, bool)

GetPlural retrieves plural forms by key.

Returns the forms and true if found, or zero PluralForms and false if not.

func (*Bundle) Keys

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

Keys returns all message keys (both simple and plural) in no particular order.

func (*Bundle) Len

func (b *Bundle) Len() int

Len returns the total number of messages (simple + plural keys).

func (*Bundle) Locale

func (b *Bundle) Locale() Locale

Locale returns the locale this bundle is for.

func (*Bundle) Set

func (b *Bundle) Set(key, value string)

Set adds or replaces a simple message for the given key.

func (*Bundle) SetAll

func (b *Bundle) SetAll(other *Bundle)

SetAll copies all messages from another bundle into this one.

Existing keys are overwritten. This is useful for merging partial translations with a base bundle.

func (*Bundle) SetPlural

func (b *Bundle) SetPlural(key string, forms PluralForms)

SetPlural adds or replaces plural forms for the given key.

type Direction

type Direction uint8

Direction represents the text direction for a locale.

const (
	// LTR is left-to-right text direction (e.g., English, Russian).
	LTR Direction = iota

	// RTL is right-to-left text direction (e.g., Arabic, Hebrew).
	RTL
)

func DirectionForLanguage

func DirectionForLanguage(language string) Direction

DirectionForLanguage returns the text direction for the given ISO 639-1 language code.

Returns RTL for Arabic, Hebrew, Persian, Urdu, and other RTL languages. Returns LTR for all other languages.

func (Direction) IsRTL

func (d Direction) IsRTL() bool

IsRTL returns true if the direction is right-to-left.

func (Direction) String

func (d Direction) String() string

String returns the human-readable name of the direction.

type Locale

type Locale struct {
	// Language is the ISO 639-1 language code (e.g., "en", "ru", "ar").
	Language string

	// Region is the optional ISO 3166-1 alpha-2 region code (e.g., "US", "RU").
	// Empty string means no specific region.
	Region string
}

Locale represents a language and optional region combination.

Language codes follow BCP 47 / ISO 639-1 (e.g., "en", "ru", "ar"). Region codes follow ISO 3166-1 alpha-2 (e.g., "US", "RU", "SA").

A Locale with an empty Region matches any region for that language during fallback resolution.

func NewLocale

func NewLocale(language, region string) Locale

NewLocale creates a new Locale with the given language and region.

Language is normalized to lowercase, region to uppercase, following BCP 47 conventions. If region is empty, the locale matches any region for that language.

Example:

en := i18n.NewLocale("en", "US")  // English (United States)
ru := i18n.NewLocale("ru", "")    // Russian (any region)

func ParseLocale

func ParseLocale(s string) Locale

ParseLocale parses a locale string in the format "language-region" or "language".

Accepted formats:

  • "en" -> Locale{Language: "en"}
  • "en-US" -> Locale{Language: "en", Region: "US"}
  • "en_US" -> Locale{Language: "en", Region: "US"}

Returns a zero-value Locale if the input is empty.

func (Locale) Direction

func (l Locale) Direction() Direction

Direction returns the text direction for this locale.

Returns RTL for Arabic ("ar"), Hebrew ("he"), Persian ("fa"), and Urdu ("ur"). Returns LTR for all other languages.

func (Locale) IsZero

func (l Locale) IsZero() bool

IsZero returns true if the locale has no language set.

func (Locale) Matches

func (l Locale) Matches(other Locale) bool

Matches reports whether this locale matches the other locale.

A locale with an empty region matches any locale with the same language. Both language comparisons are case-insensitive.

func (Locale) String

func (l Locale) String() string

String returns the locale in "language-REGION" format (e.g., "en-US").

If Region is empty, returns just the language (e.g., "en").

type PluralCategory

type PluralCategory uint8

PluralCategory represents a CLDR plural category.

const (
	// PluralOther is the default/fallback category.
	PluralOther PluralCategory = iota

	// PluralZero is used for zero quantity in languages that distinguish it (e.g., Arabic).
	PluralZero

	// PluralOne is used for singular quantity.
	PluralOne

	// PluralTwo is used for dual quantity in languages that distinguish it (e.g., Arabic).
	PluralTwo

	// PluralFew is used for a small quantity in languages with this distinction (e.g., Russian 2-4).
	PluralFew

	// PluralMany is used for a large quantity in languages with this distinction (e.g., Russian 5-20).
	PluralMany
)

func PluralRuleArabic

func PluralRuleArabic(count int) PluralCategory

PluralRuleArabic implements CLDR plural rules for Arabic.

Categories:

  • zero: n=0
  • one: n=1
  • two: n=2
  • few: n%100 in 3..10
  • many: n%100 in 11..99
  • other: everything else (100, 200, ...)

func PluralRuleEnglish

func PluralRuleEnglish(count int) PluralCategory

PluralRuleEnglish implements CLDR plural rules for English.

Categories: one (n=1), other (everything else).

func PluralRuleFrench

func PluralRuleFrench(count int) PluralCategory

PluralRuleFrench implements CLDR plural rules for French and other Romance languages that treat 0 as singular.

Categories: one (n=0 or n=1), other (everything else).

func PluralRuleJapanese

func PluralRuleJapanese(count int) PluralCategory

PluralRuleJapanese implements CLDR plural rules for Japanese, Chinese, Korean, Thai, and other languages with no grammatical plural.

Categories: other (always).

func PluralRulePolish

func PluralRulePolish(count int) PluralCategory

PluralRulePolish implements CLDR plural rules for Polish.

Categories:

  • one: n=1
  • few: n%10 in 2..4 and n%100 not in 12..14
  • many: everything else

func PluralRuleRussian

func PluralRuleRussian(count int) PluralCategory

PluralRuleRussian implements CLDR plural rules for Russian and other Slavic languages (Ukrainian, Belarusian, Serbian, Croatian).

Categories:

  • one: n%10=1 and n%100!=11 (1, 21, 31, 41...)
  • few: n%10 in 2..4 and n%100 not in 12..14 (2-4, 22-24, 32-34...)
  • many: n%10=0 or n%10 in 5..9 or n%100 in 11..14 (0, 5-20, 25-30...)
  • other: fallback (non-integer, but we handle int only)

type PluralForms

type PluralForms struct {
	// Zero form (used in Arabic for 0).
	Zero string

	// One form (singular).
	One string

	// Two form (dual, used in Arabic for 2).
	Two string

	// Few form (e.g., Russian 2-4).
	Few string

	// Many form (e.g., Russian 5-20).
	Many string

	// Other form (default fallback).
	Other string
}

PluralForms holds message strings for each CLDR plural category.

Not all categories are used by every language. For example, English only uses One and Other, while Arabic uses all six categories. When a category is not set (empty string), the translator falls back to Other.

Example (English):

PluralForms{One: "%d item", Other: "%d items"}

Example (Russian):

PluralForms{
    One:   "%d файл",    // 1, 21, 31...
    Few:   "%d файла",   // 2-4, 22-24...
    Many:  "%d файлов",  // 5-20, 25-30...
    Other: "%d файлов",  // fallback
}

func (PluralForms) Get

func (p PluralForms) Get(cat PluralCategory) string

Get returns the message string for the given plural category.

If the requested category has an empty string, falls back to Other.

type PluralRule

type PluralRule func(count int) PluralCategory

PluralRule is a function that returns the plural category for a given count.

Languages have different rules for determining which plural form to use. Each supported language registers a PluralRule function.

type Translator

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

Translator resolves localized messages with fallback chain support.

The resolution order for a message key is:

  1. Current locale (exact match: language + region)
  2. Current locale (language-only match)
  3. Fallback locale (exact match)
  4. Fallback locale (language-only match)
  5. The key itself (as a last resort)

Translator is safe for concurrent access from multiple goroutines.

Example:

t := i18n.NewTranslator(i18n.NewLocale("en", "US"))
t.AddBundle(enBundle)
t.AddBundle(ruBundle)

t.T("greeting")  // resolves from en-US bundle
t.SetLocale(i18n.NewLocale("ru", "RU"))
t.T("greeting")  // resolves from ru-RU bundle

func NewTranslator

func NewTranslator(fallback Locale) *Translator

NewTranslator creates a new Translator with the given fallback locale.

The fallback locale is used when a message is not found in the current locale. The current locale is initially set to the fallback.

func (*Translator) AddBundle

func (t *Translator) AddBundle(bundle *Bundle)

AddBundle registers a message bundle for its locale.

If a bundle for the same locale string already exists, it is replaced. Bundles should be fully populated before being added.

func (*Translator) BundleCount

func (t *Translator) BundleCount() int

BundleCount returns the number of registered bundles.

func (*Translator) Direction

func (t *Translator) Direction() Direction

Direction returns the text direction for the current locale.

func (*Translator) Fallback

func (t *Translator) Fallback() Locale

Fallback returns the fallback locale.

func (*Translator) Has

func (t *Translator) Has(key string) bool

Has reports whether a message with the given key exists in the current or fallback locale.

func (*Translator) Locale

func (t *Translator) Locale() Locale

Locale returns the current locale.

func (*Translator) LocaleSignal

func (t *Translator) LocaleSignal() state.ReadonlySignal[Locale]

LocaleSignal returns a read-only signal of the current locale.

Subscribe to this signal to react to locale changes:

sig := t.LocaleSignal()
state.Subscribe(sig, ctx, func(locale i18n.Locale) {
    // Update UI for new locale
})

func (*Translator) SetFallback

func (t *Translator) SetFallback(locale Locale)

SetFallback changes the fallback locale.

func (*Translator) SetLocale

func (t *Translator) SetLocale(locale Locale)

SetLocale changes the current locale.

This triggers the locale signal, notifying all subscribers.

func (*Translator) SetPluralRule

func (t *Translator) SetPluralRule(language string, rule PluralRule)

SetPluralRule registers a custom plural rule for a language.

This overrides the built-in CLDR rule for the given language code.

func (*Translator) T

func (t *Translator) T(key string) string

T resolves a simple message by key.

Resolution order: current locale -> fallback locale -> key itself.

func (*Translator) Tf

func (t *Translator) Tf(key string, args ...any) string

Tf resolves a message by key and formats it with fmt.Sprintf.

Resolution order: current locale -> fallback locale -> key itself.

Example:

t.Tf("greeting.name", "World")  // "Hello, World!"

func (*Translator) Tp

func (t *Translator) Tp(key string, count int) string

Tp resolves a plural message by key and count.

The plural category is determined by the locale's plural rules, then the appropriate plural form is selected and formatted with the count value.

Example:

t.Tp("items", 1)   // "1 item"
t.Tp("items", 5)   // "5 items"

func (*Translator) Tpf

func (t *Translator) Tpf(key string, count int, args ...any) string

Tpf resolves a plural message by key and count, then formats with additional args.

The count is used only for plural category selection. The args are passed to fmt.Sprintf for formatting the resolved string.

Example:

// Bundle: PluralForms{One: "%d item in %s", Other: "%d items in %s"}
t.Tpf("items.location", 3, 3, "cart")  // "3 items in cart"

Jump to

Keyboard shortcuts

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