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 ¶
- type Bundle
- func (b *Bundle) Get(key string) (string, bool)
- func (b *Bundle) GetPlural(key string) (PluralForms, bool)
- func (b *Bundle) Keys() []string
- func (b *Bundle) Len() int
- func (b *Bundle) Locale() Locale
- func (b *Bundle) Set(key, value string)
- func (b *Bundle) SetAll(other *Bundle)
- func (b *Bundle) SetPlural(key string, forms PluralForms)
- type Direction
- type Locale
- type PluralCategory
- type PluralForms
- type PluralRule
- type Translator
- func (t *Translator) AddBundle(bundle *Bundle)
- func (t *Translator) BundleCount() int
- func (t *Translator) Direction() Direction
- func (t *Translator) Fallback() Locale
- func (t *Translator) Has(key string) bool
- func (t *Translator) Locale() Locale
- func (t *Translator) LocaleSignal() state.ReadonlySignal[Locale]
- func (t *Translator) SetFallback(locale Locale)
- func (t *Translator) SetLocale(locale Locale)
- func (t *Translator) SetPluralRule(language string, rule PluralRule)
- func (t *Translator) T(key string) string
- func (t *Translator) Tf(key string, args ...any) string
- func (t *Translator) Tp(key string, count int) string
- func (t *Translator) Tpf(key string, count int, args ...any) string
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 (*Bundle) Get ¶
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 ¶
Keys returns all message keys (both simple and plural) in no particular order.
func (*Bundle) SetAll ¶
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.
func DirectionForLanguage ¶
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.
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 ¶
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 ¶
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 ¶
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.
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:
- Current locale (exact match: language + region)
- Current locale (language-only match)
- Fallback locale (exact match)
- Fallback locale (language-only match)
- 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) 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"