This document covers locale-specific formatting for numbers, percentages, and currencies.
Import path: golang.org/x/text/number
Formats numbers according to the customs of different locales.
// Formatter formats numbers for display
type Formatter interface{}// Decimal formats a number as a floating point decimal
func Decimal(x interface{}, opts ...Option) Formatter
// Percent formats a number as a percentage (1.0 = 100%)
func Percent(x interface{}, opts ...Option) Formatter
// PerMille formats a number as a per mille indication (1.0 = 1000‰)
func PerMille(x interface{}, opts ...Option) Formatter
// Scientific formats a number in scientific notation
func Scientific(x interface{}, opts ...Option) Formatter
// Engineering formats a number using engineering notation
func Engineering(x interface{}, opts ...Option) Formatterfunc (f Formatter) Format(state format.State, verb rune)
func (f Formatter) Digits(buf []byte, tag language.Tag, scale int) number.Digits// Option configures a Formatter
type Option interface{}// Scale simultaneously sets MinFractionDigits and MaxFractionDigits
func Scale(decimals int) Option
// Precision sets the maximum number of significant digits
func Precision(prec int) Option
// MinIntegerDigits specifies the minimum number of integer digits
func MinIntegerDigits(min int) Option
// MaxIntegerDigits limits the number of integer digits
func MaxIntegerDigits(max int) Option
// MinFractionDigits specifies the minimum number of fractional digits
func MinFractionDigits(min int) Option
// MaxFractionDigits specifies the maximum number of fractional digits
func MaxFractionDigits(max int) Option
// IncrementString sets the incremental value for rounding
func IncrementString(decimal string) Option
// FormatWidth sets the total format width
func FormatWidth(n int) Option
// Pad sets the rune to be used for padding
func Pad(r rune) Option
// NoSeparator causes a number to be displayed without grouping separators
func NoSeparator() Option
// PatternOverrides allows users to specify alternative patterns for specific languages
func PatternOverrides(patterns map[string]string) Option// FormatFunc formats a number
type FormatFunc func(x interface{}, opts ...Option) Formatter
// NewFormat creates a FormatFunc based on another FormatFunc and new options
func NewFormat(format FormatFunc, opts ...Option) FormatFuncimport (
"fmt"
"golang.org/x/text/language"
"golang.org/x/text/message"
"golang.org/x/text/number"
)
// Basic decimal formatting
p := message.NewPrinter(language.English)
fmt.Fprintln(p, number.Decimal(1234.56)) // "1,234.56"
// German formatting
p = message.NewPrinter(language.German)
fmt.Fprintln(p, number.Decimal(1234.56)) // "1.234,56"
// Percentage formatting
p = message.NewPrinter(language.English)
fmt.Fprintln(p, number.Percent(0.1234)) // "12.34%"
// Per mille formatting
fmt.Fprintln(p, number.PerMille(0.1234)) // "123.4‰"
// Scientific notation
fmt.Fprintln(p, number.Scientific(1234.56)) // "1.23456E3"
// Engineering notation (powers of 3)
fmt.Fprintln(p, number.Engineering(1234.56)) // "1.23456E3"
// Control decimal places
fmt.Fprintln(p, number.Decimal(1234.5678, number.Scale(2))) // "1,234.57"
// Minimum and maximum fraction digits
fmt.Fprintln(p, number.Decimal(1234, number.MinFractionDigits(2))) // "1,234.00"
fmt.Fprintln(p, number.Decimal(1234.5678, number.MaxFractionDigits(2))) // "1,234.57"
// Precision (significant digits)
fmt.Fprintln(p, number.Decimal(1234.5678, number.Precision(4))) // "1,235"
// No separator
fmt.Fprintln(p, number.Decimal(1234.56, number.NoSeparator())) // "1234.56"
// Padding
fmt.Fprintln(p, number.Decimal(12.34,
number.FormatWidth(10),
number.Pad('0'),
)) // "000012.34"
// Minimum integer digits
fmt.Fprintln(p, number.Decimal(12.34, number.MinIntegerDigits(4))) // "0,012.34"
// Increment (rounding)
fmt.Fprintln(p, number.Decimal(12.34, number.IncrementString("0.25"))) // "12.25"
// Create custom format function
percentFormat := number.NewFormat(
number.Percent,
number.Scale(1),
)
fmt.Fprintln(p, percentFormat(0.1234)) // "12.3%"Import path: golang.org/x/text/currency
Provides currency-related functionality including currency units, queries, and formatting.
NOTE: The formatting functionality is currently under development and may change without notice.
// Unit is an ISO 4217 currency designator
type Unit struct{}
func ParseISO(s string) (Unit, error)
func MustParseISO(s string) Unit
func FromRegion(r language.Region) (currency Unit, ok bool)
func FromTag(t language.Tag) (Unit, language.Confidence)
func (u Unit) String() string
func (u Unit) Amount(amount interface{}) Amount// G10 currencies
var XXX Unit // Undefined and testing
var XTS Unit // Testing
var USD Unit // US Dollar
var EUR Unit // Euro
var JPY Unit // Japanese Yen
var GBP Unit // British Pound
var CHF Unit // Swiss Franc
var AUD Unit // Australian Dollar
var NZD Unit // New Zealand Dollar
var CAD Unit // Canadian Dollar
var SEK Unit // Swedish Krona
var NOK Unit // Norwegian Krone
// Additional common currencies
var BRL Unit // Brazilian Real
var CNY Unit // Chinese Yuan
var DKK Unit // Danish Krone
var INR Unit // Indian Rupee
var RUB Unit // Russian Ruble
var HKD Unit // Hong Kong Dollar
var IDR Unit // Indonesian Rupiah
var KRW Unit // South Korean Won
var MXN Unit // Mexican Peso
var PLN Unit // Polish Zloty
var SAR Unit // Saudi Riyal
var THB Unit // Thai Baht
var TRY Unit // Turkish Lira
var TWD Unit // New Taiwan Dollar
var ZAR Unit // South African Rand
// Precious metals
var XAG Unit // Silver
var XAU Unit // Gold
var XPT Unit // Platinum
var XPD Unit // Palladium// Amount is an amount-currency unit pair
type Amount struct{}
func (a Amount) Currency() Unit
func (a Amount) Format(s fmt.State, verb rune)// Formatter decorates a number, Unit or Amount with formatting options
type Formatter func(amount interface{}) formattedValue
// Predefined formatters
var NarrowSymbol Formatter // Use narrow symbols
var Symbol Formatter // Use symbols instead of ISO codes
var ISO Formatter // Use ISO code as symbol
func Default(currency Unit) Formatter
func Kind(k Kind) Formatter// Kind determines the rounding and rendering properties
type Kind struct{}
var Standard Kind // Standard rounding and formatting
var Cash Kind // Rounding for cash transactions
var Accounting Kind // Rounding for accounting
func (k Kind) Rounding(cur Unit) (scale, increment int)// Query represents a set of currency units
func Query(options ...QueryOption) QueryIter
// QueryIter represents a set of Units
type QueryIter interface {
Next() bool
Unit() Unit
Region() language.Region
From() (time.Time, bool)
To() (time.Time, bool)
IsTender() bool
}
// QueryOption modifies the set of unit information returned
type QueryOption func(*iter)
var Historical QueryOption // Select units for all dates
var NonTender QueryOption // Include matching units that are not legal tender
func Date(t time.Time) QueryOption
func Region(r language.Region) QueryOptionconst CLDRVersion string = "32"import (
"fmt"
"golang.org/x/text/currency"
"golang.org/x/text/language"
"golang.org/x/text/message"
)
// Parse currency from ISO code
usd, err := currency.ParseISO("USD")
eur := currency.MustParseISO("EUR")
// Get currency for region
curr, ok := currency.FromRegion(language.MustParseRegion("US")) // USD
curr, ok = currency.FromRegion(language.MustParseRegion("DE")) // EUR
// Get currency from language tag
curr, conf := currency.FromTag(language.Make("en-US")) // USD
// Create amounts
amount := usd.Amount(123.45)
fmt.Println(amount.Currency()) // USD
// Format currency
p := message.NewPrinter(language.English)
fmt.Fprintf(p, "%v", usd.Amount(1234.56)) // "$1,234.56" (with symbol)
// Use ISO code instead of symbol
fmt.Fprintf(p, "%v", currency.ISO(usd.Amount(1234.56))) // "USD 1,234.56"
// Use narrow symbol
fmt.Fprintf(p, "%v", currency.NarrowSymbol(eur.Amount(1234.56)))
// Set default currency for bare numbers
formatter := currency.Default(usd)
fmt.Fprintf(p, "%v", formatter(1234.56)) // "$1,234.56"
// Query currencies
iter := currency.Query(currency.Region(language.MustParseRegion("US")))
for iter.Next() {
unit := iter.Unit()
from, hasFrom := iter.From()
to, hasTo := iter.To()
isTender := iter.IsTender()
// Process currency
}
// Query historical currencies
iter = currency.Query(
currency.Region(language.MustParseRegion("DE")),
currency.Historical,
)
for iter.Next() {
unit := iter.Unit() // EUR, DEM (Deutsche Mark), etc.
}
// Query at specific date
import "time"
date := time.Date(1995, 1, 1, 0, 0, 0, 0, time.UTC)
iter = currency.Query(
currency.Region(language.MustParseRegion("DE")),
currency.Date(date),
)
for iter.Next() {
unit := iter.Unit() // DEM (before Euro)
}
// Include non-tender currencies
iter = currency.Query(
currency.Region(language.MustParseRegion("US")),
currency.NonTender,
)
// Get rounding information
scale, increment := currency.Standard.Rounding(usd)
// scale = 2 (cents), increment = 0 (round to any cent)
scale, increment = currency.Cash.Rounding(currency.MustParseISO("CHF"))
// scale = 2, increment = 5 (round to 0.05 CHF)Import path: golang.org/x/text/date
NOTE: The date package is UNDER CONSTRUCTION and currently contains only internal data tables for CLDR date/time information. It does not provide a public API for date or time formatting.
const CLDRVersion string = "32"The package contains generated data tables used internally by other golang.org/x/text packages but does not expose date formatting functionality to users.
import (
"fmt"
"golang.org/x/text/language"
"golang.org/x/text/message"
"golang.org/x/text/number"
)
func formatNumber(value float64, lang language.Tag) string {
p := message.NewPrinter(lang)
return p.Sprintf("%v", number.Decimal(value))
}
// Usage
fmt.Println(formatNumber(1234567.89, language.English)) // "1,234,567.89"
fmt.Println(formatNumber(1234567.89, language.German)) // "1.234.567,89"
fmt.Println(formatNumber(1234567.89, language.French)) // "1 234 567,89"import (
"golang.org/x/text/currency"
"golang.org/x/text/language"
"golang.org/x/text/message"
)
type ConversionRate struct {
From currency.Unit
To currency.Unit
Rate float64
}
func displayConversion(rate ConversionRate, amount float64, lang language.Tag) string {
p := message.NewPrinter(lang)
fromAmount := rate.From.Amount(amount)
toAmount := rate.To.Amount(amount * rate.Rate)
return p.Sprintf("%v = %v", fromAmount, toAmount)
}
// Usage
rate := ConversionRate{
From: currency.USD,
To: currency.EUR,
Rate: 0.85,
}
result := displayConversion(rate, 100, language.English)
// "$100.00 = €85.00"import (
"golang.org/x/text/currency"
"golang.org/x/text/language"
"golang.org/x/text/message"
"golang.org/x/text/number"
)
func formatFinancialValue(value float64, curr currency.Unit, lang language.Tag) string {
p := message.NewPrinter(lang)
formatter := number.NewFormat(
number.Decimal,
number.Scale(2),
number.MinFractionDigits(2),
)
amount := curr.Amount(value)
if value < 0 {
// Use accounting format for negative numbers
amount = currency.Accounting(amount)
}
return p.Sprintf("%v", formatter(amount))
}import (
"math"
"golang.org/x/text/currency"
"golang.org/x/text/language"
"golang.org/x/text/message"
)
func formatPrice(value float64, curr currency.Unit, kind currency.Kind, lang language.Tag) string {
// Get rounding rules for currency
scale, increment := kind.Rounding(curr)
// Apply rounding
multiplier := math.Pow(10, float64(scale))
if increment > 0 {
multiplier /= float64(increment)
}
rounded := math.Round(value*multiplier) / multiplier
// Format
p := message.NewPrinter(lang)
return p.Sprintf("%v", curr.Amount(rounded))
}
// Usage
price := formatPrice(12.347, currency.USD, currency.Standard, language.English)
// "$12.35" (rounded to nearest cent)
// Swiss Franc cash rounding (0.05)
price = formatPrice(12.347, currency.CHF, currency.Cash, language.German)
// "CHF 12.35" (rounded to nearest 0.05)import (
"golang.org/x/text/language"
"golang.org/x/text/message"
"golang.org/x/text/number"
)
func formatPercentageChange(oldValue, newValue float64, lang language.Tag) string {
change := (newValue - oldValue) / oldValue
p := message.NewPrinter(lang)
sign := ""
if change > 0 {
sign = "+"
}
return sign + p.Sprintf("%v", number.Percent(change, number.Scale(2)))
}
// Usage
fmt.Println(formatPercentageChange(100, 120, language.English)) // "+20.00%"
fmt.Println(formatPercentageChange(100, 85, language.English)) // "-15.00%"import (
"golang.org/x/text/currency"
"golang.org/x/text/language"
"golang.org/x/text/message"
)
type Balance struct {
Currency currency.Unit
Amount float64
}
func displayBalances(balances []Balance, lang language.Tag) []string {
p := message.NewPrinter(lang)
results := make([]string, len(balances))
for i, bal := range balances {
amount := bal.Currency.Amount(bal.Amount)
results[i] = p.Sprintf("%v", amount)
}
return results
}
// Usage
balances := []Balance{
{currency.USD, 1000.00},
{currency.EUR, 850.00},
{currency.GBP, 750.00},
}
lines := displayBalances(balances, language.English)
// ["$1,000.00", "€850.00", "£750.00"]import (
"golang.org/x/text/language"
"golang.org/x/text/message"
"golang.org/x/text/number"
)
func formatScientific(value float64, precision int, lang language.Tag) string {
p := message.NewPrinter(lang)
formatter := number.Scientific(value,
number.Precision(precision),
)
return p.Sprintf("%v", formatter)
}
// Usage
fmt.Println(formatScientific(0.00001234, 3, language.English)) // "1.23E-5"
fmt.Println(formatScientific(1234567890, 4, language.English)) // "1.235E9"import (
"strconv"
"strings"
"golang.org/x/text/language"
)
// Note: This is a simplified example. The package doesn't provide
// built-in parsing, so you'd need to implement it based on locale rules.
func parseNumber(s string, lang language.Tag) (float64, error) {
// This is simplified - real implementation would need to handle
// locale-specific decimal and grouping separators
// For English: "1,234.56"
// For German: "1.234,56"
if lang == language.German {
s = strings.ReplaceAll(s, ".", "")
s = strings.ReplaceAll(s, ",", ".")
} else {
s = strings.ReplaceAll(s, ",", "")
}
return strconv.ParseFloat(s, 64)
}import (
"golang.org/x/text/currency"
"golang.org/x/text/language"
)
func getActiveCurrencies() []currency.Unit {
var currencies []currency.Unit
iter := currency.Query()
for iter.Next() {
if iter.IsTender() {
currencies = append(currencies, iter.Unit())
}
}
return currencies
}
func getCurrenciesForRegion(region language.Region) []currency.Unit {
var currencies []currency.Unit
iter := currency.Query(
currency.Region(region),
currency.Historical,
)
for iter.Next() {
currencies = append(currencies, iter.Unit())
}
return currencies
}
// Usage
allActive := getActiveCurrencies()
usHistory := getCurrenciesForRegion(language.MustParseRegion("US"))Based on: