A Go implementation of ICU MessageFormat for internationalization (i18n).
- ICU MessageFormat - Full support for ICU message syntax
- Number Formatting - Decimal, currency, percent, and scientific notation
- Date/Time Formatting - Various date and time styles with timezone support
- Plural Rules - Language-specific plural forms
- Select Cases - Conditional message selection
- Ordinal Numbers - 1st, 2nd, 3rd formatting
- Duration Formatting - Human-readable time durations
- Multi-locale Support - Default support for 10+ languages, extensible via custom formatters
go get go-slim.dev/icupackage main
import (
"context"
"fmt"
"golang.org/x/text/language"
"go-slim.dev/icu"
)
func main() {
// Parse ICU message
msg, _ := icu.Parse("Hello, {name}!")
// Create renderer with locale
renderer := icu.New(language.English)
// Render with variables
result, _ := renderer.Render(context.Background(), msg, map[string]any{
"name": "World",
})
fmt.Println(result) // Output: Hello, World!
}msg, _ := icu.Parse("Hello, {name}!")
renderer.Render(ctx, msg, map[string]any{"name": "Alice"})
// Output: Hello, Alice!// Decimal
msg, _ := icu.Parse("Price: {price, number}")
renderer.Render(ctx, msg, map[string]any{"price": 1234.56})
// Output: Price: 1,234.56
// Currency
msg, _ := icu.Parse("Total: {amount, number, style=currency currency=USD}")
renderer.Render(ctx, msg, map[string]any{"amount": 99.99})
// Output: Total: $99.99
// Percent
msg, _ := icu.Parse("Progress: {ratio, number, style=percent}")
renderer.Render(ctx, msg, map[string]any{"ratio": 0.75})
// Output: Progress: 75%import "time"
now := time.Now()
// Date only
msg, _ := icu.Parse("Date: {today, date, style=long}")
renderer.Render(ctx, msg, map[string]any{"today": now})
// Output: Date: November 8, 2024
// Time only
msg, _ := icu.Parse("Time: {now, time, style=short}")
renderer.Render(ctx, msg, map[string]any{"now": now})
// Output: Time: 2:30 PM
// Date and time
msg, _ := icu.Parse("When: {timestamp, date}")
renderer.Render(ctx, msg, map[string]any{"timestamp": now})
// Output: When: Nov 8, 2024 2:30 PM
// Relative time
msg, _ := icu.Parse("Posted: {created, date, type=relative}")
renderer.Render(ctx, msg, map[string]any{"created": time.Now().Add(-2 * time.Hour)})
// Output: Posted: 2 hours agomsg, _ := icu.Parse(
"{count, plural, =0{No items} =1{One item} other{# items}}"
)
renderer.Render(ctx, msg, map[string]any{"count": 0}) // No items
renderer.Render(ctx, msg, map[string]any{"count": 1}) // One item
renderer.Render(ctx, msg, map[string]any{"count": 5}) // 5 itemsmsg, _ := icu.Parse(
"{gender, select, male{He} female{She} other{They}} said hello."
)
renderer.Render(ctx, msg, map[string]any{"gender": "male"})
// Output: He said hello.
renderer.Render(ctx, msg, map[string]any{"gender": "female"})
// Output: She said hello.msg, _ := icu.Parse("You finished {place, ordinal}!")
renderer.Render(ctx, msg, map[string]any{"place": 1}) // You finished 1st!
renderer.Render(ctx, msg, map[string]any{"place": 2}) // You finished 2nd!
renderer.Render(ctx, msg, map[string]any{"place": 3}) // You finished 3rd!import "time"
msg, _ := icu.Parse("Duration: {elapsed, duration}")
renderer.Render(ctx, msg, map[string]any{"elapsed": 90 * time.Second})
// Output: Duration: 1 minute 30 seconds
renderer.Render(ctx, msg, map[string]any{"elapsed": 3665 * time.Second})
// Output: Duration: 1 hour 1 minute 5 seconds// Register custom formatter
renderer.Context().SetSpecialFormatter("custom", &MyFormatter{})
// Use in message
msg, _ := icu.Parse("{value, custom}")// English
enRenderer := icu.New(language.English)
result, _ := enRenderer.Render(ctx, msg, vars)
// Chinese
zhRenderer := icu.New(language.Chinese)
result, _ := zhRenderer.Render(ctx, msg, vars)
// Japanese
jaRenderer := icu.New(language.Japanese)
result, _ := jaRenderer.Render(ctx, msg, vars)msg, _ := icu.Parse(`{num, number,
style=decimal
useGrouping=true
minimumFractionDigits=2
maximumFractionDigits=4
}`)msg, _ := icu.Parse(`{date, date,
type=date
style=full
timeZone=America/New_York
}`)The following locales are supported out of the box with default formatting rules:
en- Englishzh- Chinese (Simplified)ja- Japaneseko- Koreande- Germanfr- Frenches- Spanishit- Italianpt- Portugueseru- Russian
Note: You can support additional locales by implementing custom formatters. The library is designed to be extensible and not limited to these built-in locales.
| Type | Description | Example |
|---|---|---|
number |
Number formatting | {count, number} |
date |
Date/time formatting | {now, date} |
time |
Time formatting | {now, time} |
plural |
Plural rules | {n, plural, one{item} other{items}} |
select |
Selection | {gender, select, male{He} female{She}} |
ordinal |
Ordinal numbers | {place, ordinal} |
duration |
Duration | {elapsed, duration} |
decimal- Standard decimal (default)currency- Currency with symbolpercent- Percentagescientific- Scientific notation
short- Shortest format (e.g., "11/8/24")medium- Medium format (default)long- Long format (e.g., "November 8, 2024")full- Full format with day of week
func Parse(input string) (*ast.Message, error)Parses ICU message format string into AST.
func New(tag language.Tag) *RendererCreates a new renderer with the specified locale.
func (r *Renderer) Render(ctx context.Context, msg *ast.Message, variables map[string]any) (string, error)Renders a parsed message with the given variables.
MIT