This document covers the message localization system including message extraction, translation management, catalog building, and localized message printing with plural support.
Import path: golang.org/x/text/message
Implements formatted I/O for localized strings with functions analogous to fmt's print functions. It is a drop-in replacement for fmt that supports message catalogs and localization.
// Printer implements language-specific formatted I/O
type Printer struct{}
func NewPrinter(t language.Tag, opts ...Option) *Printer// Print functions (like fmt.Print but with localization)
func (p *Printer) Print(a ...interface{}) (n int, err error)
func (p *Printer) Println(a ...interface{}) (n int, err error)
func (p *Printer) Printf(key Reference, a ...interface{}) (n int, err error)
// Sprint functions (like fmt.Sprint but with localization)
func (p *Printer) Sprint(a ...interface{}) string
func (p *Printer) Sprintln(a ...interface{}) string
func (p *Printer) Sprintf(key Reference, a ...interface{}) string
// Fprint functions (like fmt.Fprint but with localization)
func (p *Printer) Fprint(w io.Writer, a ...interface{}) (n int, err error)
func (p *Printer) Fprintln(w io.Writer, a ...interface{}) (n int, err error)
func (p *Printer) Fprintf(w io.Writer, key Reference, a ...interface{}) (n int, err error)// Reference is a string or a message reference
type Reference interface{}
// Key creates a message Reference with id for lookup and fallback for when no match is found
func Key(id string, fallback string) Reference// Option defines an option of a Printer
type Option func(o *options)
// Catalog defines the catalog to be used
func Catalog(c catalog.Catalog) Option// DefaultCatalog is used by SetString
var DefaultCatalog catalog.Catalog
// Set calls Set on the initial default Catalog
func Set(tag language.Tag, key string, msg ...catalog.Message) error
// SetString calls SetString on the initial default Catalog
func SetString(tag language.Tag, key string, msg string) error
// MatchLanguage reports the matched tag from language.MatchStrings for DefaultCatalog's Matcher
func MatchLanguage(preferred ...string) language.Tagimport (
"fmt"
"golang.org/x/text/language"
"golang.org/x/text/message"
)
// Create a printer for a specific language
p := message.NewPrinter(language.English)
// Use like fmt.Printf
p.Printf("Hello, %s!\n", "World") // "Hello, World!"
// With message keys
p.Printf("greeting", "World") // Looks up "greeting" in catalog
// Use Key for explicit message lookup with fallback
p.Printf(message.Key("greeting", "Hello, %s!"), "World")
// Sprint variants
msg := p.Sprintf("You have %d messages", 5)
// Language-specific formatting
pDE := message.NewPrinter(language.German)
pDE.Printf("Guten Tag, %s!\n", "Welt")
// Set messages in default catalog
message.SetString(language.English, "greeting", "Hello, %s!")
message.SetString(language.German, "greeting", "Guten Tag, %s!")
// Print using default catalog
p.Printf("greeting", "World") // "Hello, World!"
pDE.Printf("greeting", "Welt") // "Guten Tag, Welt!"Import path: golang.org/x/text/message/catalog
Defines collections of translated format strings.
NOTE: This package is UNDER CONSTRUCTION and its API may change.
// Catalog allows lookup of translated messages
type Catalog interface {
Languages() []language.Tag
Matcher() language.Matcher
Context(tag language.Tag, r catmsg.Renderer) *Context
}// Builder allows building a Catalog programmatically
type Builder struct{}
func NewBuilder(opts ...Option) *Builderfunc (b *Builder) Languages() []language.Tag
func (b *Builder) Matcher() language.Matcher
func (b *Builder) Context(tag language.Tag, r catmsg.Renderer) *Context
// Set sets the translation for the given language and key
func (b *Builder) Set(tag language.Tag, key string, msg ...Message) error
// SetString is shorthand for Set(tag, key, String(msg))
func (b *Builder) SetString(tag language.Tag, key string, msg string) error
// SetMacro defines a Message that may be substituted in another message
func (b *Builder) SetMacro(tag language.Tag, name string, msg ...Message) error// Context is used for evaluating Messages
type Context struct{}
// Execute looks up and executes the message with the given key
func (c *Context) Execute(key string) error// Message holds a collection of translations
type Message catmsg.Message
// String specifies a plain message string
func String(name string) Message
// Var sets a variable that may be substituted in formatting patterns
func Var(name string, msg ...Message) Message// Dictionary is a source of translations for a single language
type Dictionary interface {
Lookup(key string) (data string, ok bool)
}// Option configures Catalog behavior
type Option func(*options)
// Fallback specifies the default fallback language (default is Und)
func Fallback(tag language.Tag) Option// NewFromMap creates a Catalog from the given map
func NewFromMap(dictionaries map[string]Dictionary, opts ...Option) (Catalog, error)// ErrNotFound indicates there was no message for the given key
var ErrNotFound errorimport (
"golang.org/x/text/language"
"golang.org/x/text/message"
"golang.org/x/text/message/catalog"
)
// Build a catalog
builder := catalog.NewBuilder()
// Add messages for different languages
builder.SetString(language.English, "welcome", "Welcome!")
builder.SetString(language.German, "welcome", "Willkommen!")
builder.SetString(language.French, "welcome", "Bienvenue!")
builder.SetString(language.English, "goodbye", "Goodbye, %s!")
builder.SetString(language.German, "goodbye", "Auf Wiedersehen, %s!")
// Use the catalog with a printer
p := message.NewPrinter(language.English, message.Catalog(builder))
p.Printf("welcome") // "Welcome!"
pDE := message.NewPrinter(language.German, message.Catalog(builder))
pDE.Printf("welcome") // "Willkommen!"
// With arguments
p.Printf("goodbye", "John") // "Goodbye, John!"
// Set with fallback language
builder = catalog.NewBuilder(catalog.Fallback(language.English))
// Get supported languages
languages := builder.Languages()
// Use variables in messages
builder.Set(language.English, "user_greeting",
catalog.Var("name", catalog.String("user")),
catalog.String("Hello, ${name}!"),
)Import path: golang.org/x/text/message/pipeline
Provides tools for creating translation pipelines including message extraction, merging, and generation.
NOTE: UNDER DEVELOPMENT. API MAY CHANGE.
// Config contains configuration for the translation pipeline
type Config struct {
Supported []language.Tag
SourceLanguage language.Tag
Packages []string
Dir string
TranslationsPattern string
OutPattern string
Format string
Ext string
GenFile string
GenPackage string
DeclareVar string
SetDefault bool
}// State holds all accumulated information on translations during processing
type State struct {
Config Config
Package string
Extracted Messages
Messages []Messages
Translations []Messages
}
// Extract extracts all strings from the package defined in Config
func Extract(c *Config) (*State, error)// Import loads existing translation files
func (s *State) Import() error
// Merge merges the extracted messages with existing translations
func (s *State) Merge() error
// Export writes out the messages to translation out files
func (s *State) Export() error
// Generate writes a Go file that defines a Catalog with translated messages
func (s *State) Generate() error
// WriteGen writes a Go file with the given package name
func (s *State) WriteGen(w io.Writer, pkg string) error// Messages is used to store translations for a single language
type Messages struct {
Language language.Tag
Messages []Message
Macros map[string]Text
}// Message describes a message to be translated
type Message struct {
ID IDList
Key string
Meaning string
Message Text
Translation Text
Comment string
TranslatorComment string
Placeholders []Placeholder
Fuzzy bool
Position string
}
func (m *Message) Placeholder(id string) *Placeholder
func (m *Message) Substitute(msg string) (sub string, err error)// Text defines a message to be displayed
type Text struct {
Msg string
Select *Select
Var map[string]Text
Example string
}
func (t *Text) IsEmpty() bool
func (t *Text) MarshalJSON() ([]byte, error)
func (t *Text) UnmarshalJSON(b []byte) error// Placeholder is a part of the message that should not be changed
type Placeholder struct {
ID string
String string
Type string
UnderlyingType string
ArgNum int
Expr string
Comment string
Example string
Features []Feature
}// Select selects a Text based on the feature value
type Select struct {
Feature string
Arg string
Cases map[string]Text
}// Feature holds information about a feature
type Feature struct {
Type string // Right now this is only gender and plural
}// IDList is a set of identifiers
type IDList []string
func (id IDList) MarshalJSON() ([]byte, error)
func (id IDList) UnmarshalJSON(b []byte) error// Rewrite rewrites Go files to use the localization machinery
func Rewrite(w io.Writer, args ...string) error
// Generate generates translation code
// Deprecated: use (*State).Generate()
func Generate(w io.Writer, pkg string, extracted *Messages, trans ...Messages) (n int, err error)import (
"golang.org/x/text/language"
"golang.org/x/text/message/pipeline"
)
// Configure the pipeline
config := &pipeline.Config{
Supported: []language.Tag{
language.English,
language.German,
language.French,
},
SourceLanguage: language.English,
Packages: []string{"./..."},
Dir: ".",
TranslationsPattern: "locales/*.json",
OutPattern: "locales/%s.json",
Format: "json",
GenFile: "catalog.go",
GenPackage: "main",
}
// Extract messages from source code
state, err := pipeline.Extract(config)
if err != nil {
// Handle error
}
// Import existing translations
err = state.Import()
// Merge extracted messages with existing translations
err = state.Merge()
// Export for translation
err = state.Export()
// Generate Go code with compiled catalog
err = state.Generate()Import path: golang.org/x/text/feature/plural
Provides utilities for handling linguistic plurals in text based on CLDR plural rules.
// Form defines a plural form
type Form byte
const (
Other Form = iota
Zero
One
Two
Few
Many
)// Rules defines the plural rules for all languages
type Rules struct{}
// Cardinal defines plural rules for quantities
var Cardinal *Rules
// Ordinal defines plural rules for positions (first, second, etc.)
var Ordinal *Rules// MatchPlural returns the plural form based on operands
func (r *Rules) MatchPlural(lang language.Tag, i, v, w, f, t int) Form
// MatchDigits computes the plural form for decimal floating point digits
func (r *Rules) MatchDigits(t language.Tag, digits []byte, exp, scale int) Form// Interface is used for types that can determine their own plural form
type Interface interface {
PluralForm(t language.Tag, scale int) (f Form, n int)
}// Selectf returns the first case matching the arg-th substitution
func Selectf(arg int, format string, cases ...interface{}) catalog.Messageconst CLDRVersion string = "32"import (
"golang.org/x/text/language"
"golang.org/x/text/feature/plural"
"golang.org/x/text/message"
)
// Get plural form for a number
form := plural.Cardinal.MatchPlural(language.English, 1, 0, 0, 0, 0)
// form == plural.One
form = plural.Cardinal.MatchPlural(language.English, 2, 0, 0, 0, 0)
// form == plural.Other
// Ordinal forms (1st, 2nd, 3rd, etc.)
form = plural.Ordinal.MatchPlural(language.English, 1, 0, 0, 0, 0)
// form == plural.One (for "1st")
form = plural.Ordinal.MatchPlural(language.English, 2, 0, 0, 0, 0)
// form == plural.Two (for "2nd")
// Use with message formatting
import "golang.org/x/text/message/catalog"
builder := catalog.NewBuilder()
// Define plural forms for different languages
builder.Set(language.English, "items",
plural.Selectf(1, "%d",
"=0", "no items",
"one", "one item",
"other", "%d items",
),
)
builder.Set(language.French, "items",
plural.Selectf(1, "%d",
"=0", "aucun élément",
"=1", "un élément",
"other", "%d éléments",
),
)
// Use with printer
p := message.NewPrinter(language.English, message.Catalog(builder))
p.Printf("items", 0) // "no items"
p.Printf("items", 1) // "one item"
p.Printf("items", 5) // "5 items"import (
"golang.org/x/text/language"
"golang.org/x/text/message"
"golang.org/x/text/message/catalog"
)
// Initialize catalog at startup
var catalogBuilder *catalog.Builder
func init() {
catalogBuilder = catalog.NewBuilder(
catalog.Fallback(language.English),
)
// English messages
catalogBuilder.SetString(language.English, "welcome", "Welcome!")
catalogBuilder.SetString(language.English, "goodbye", "Goodbye!")
catalogBuilder.SetString(language.English, "hello", "Hello, %s!")
// German messages
catalogBuilder.SetString(language.German, "welcome", "Willkommen!")
catalogBuilder.SetString(language.German, "goodbye", "Auf Wiedersehen!")
catalogBuilder.SetString(language.German, "hello", "Hallo, %s!")
// Spanish messages
catalogBuilder.SetString(language.Spanish, "welcome", "¡Bienvenido!")
catalogBuilder.SetString(language.Spanish, "goodbye", "¡Adiós!")
catalogBuilder.SetString(language.Spanish, "hello", "¡Hola, %s!")
}
// Get printer for user's language
func getPrinter(userLang language.Tag) *message.Printer {
return message.NewPrinter(userLang, message.Catalog(catalogBuilder))
}import (
"golang.org/x/text/language"
"golang.org/x/text/message"
"golang.org/x/text/message/catalog"
)
// Set up messages with context
func setupMessages() *catalog.Builder {
b := catalog.NewBuilder()
// Button labels
b.SetString(language.English, "button.save", "Save")
b.SetString(language.English, "button.cancel", "Cancel")
b.SetString(language.English, "button.delete", "Delete")
// Error messages
b.SetString(language.English, "error.not_found", "Item not found")
b.SetString(language.English, "error.permission", "Permission denied")
// Success messages
b.SetString(language.English, "success.saved", "Successfully saved")
return b
}import (
"golang.org/x/text/language"
"golang.org/x/text/message"
"golang.org/x/text/message/catalog"
"golang.org/x/text/feature/plural"
)
func setupPluralMessages() *catalog.Builder {
b := catalog.NewBuilder()
// English plurals
b.Set(language.English, "file_count",
plural.Selectf(1, "%d",
"=0", "no files",
"one", "one file",
"other", "%d files",
),
)
// German plurals
b.Set(language.German, "file_count",
plural.Selectf(1, "%d",
"=0", "keine Dateien",
"one", "eine Datei",
"other", "%d Dateien",
),
)
// Russian plurals (more complex)
b.Set(language.Russian, "file_count",
plural.Selectf(1, "%d",
"=0", "нет файлов",
"one", "%d файл",
"few", "%d файла",
"many", "%d файлов",
"other", "%d файла",
),
)
return b
}
func displayFileCount(count int, lang language.Tag) string {
b := setupPluralMessages()
p := message.NewPrinter(lang, message.Catalog(b))
return p.Sprintf("file_count", count)
}import (
"net/http"
"golang.org/x/text/language"
"golang.org/x/text/message"
)
var supportedLanguages = []language.Tag{
language.English,
language.German,
language.Spanish,
}
var matcher = language.NewMatcher(supportedLanguages)
func localizedHandler(w http.ResponseWriter, r *http.Request) {
// Parse Accept-Language header
accept := r.Header.Get("Accept-Language")
tags, _, _ := language.ParseAcceptLanguage(accept)
// Match best language
tag, _, _ := matcher.Match(tags...)
// Get printer for language
p := getPrinter(tag)
// Use localized messages
message := p.Sprintf("welcome")
w.Write([]byte(message))
}import (
"encoding/json"
"io/ioutil"
"golang.org/x/text/language"
"golang.org/x/text/message/catalog"
)
type TranslationFile struct {
Messages map[string]string `json:"messages"`
}
func loadTranslations(lang language.Tag, filename string) error {
data, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
var tf TranslationFile
if err := json.Unmarshal(data, &tf); err != nil {
return err
}
for key, value := range tf.Messages {
catalogBuilder.SetString(lang, key, value)
}
return nil
}
// Load all translation files
func loadAllTranslations() error {
if err := loadTranslations(language.English, "locales/en.json"); err != nil {
return err
}
if err := loadTranslations(language.German, "locales/de.json"); err != nil {
return err
}
if err := loadTranslations(language.Spanish, "locales/es.json"); err != nil {
return err
}
return nil
}import (
"golang.org/x/text/language"
"golang.org/x/text/message"
"golang.org/x/text/message/catalog"
)
func setupFormattedMessages() *catalog.Builder {
b := catalog.NewBuilder()
// With multiple variables
b.SetString(language.English,
"user_greeting",
"Hello, %s! You have %d new messages.",
)
b.SetString(language.German,
"user_greeting",
"Hallo, %s! Sie haben %d neue Nachrichten.",
)
return b
}
func greetUser(name string, messageCount int, lang language.Tag) string {
b := setupFormattedMessages()
p := message.NewPrinter(lang, message.Catalog(b))
return p.Sprintf("user_greeting", name, messageCount)
}
// Usage
msg := greetUser("Alice", 5, language.English)
// "Hello, Alice! You have 5 new messages."// Step 1: Mark strings for extraction in your code
import "golang.org/x/text/message"
func myFunction(p *message.Printer) {
// Messages to be extracted
p.Printf("welcome_message")
p.Printf("error_occurred")
p.Sprintf("items_count", count)
}
// Step 2: Extract messages using pipeline
// Run extraction tool (typically via go generate or build script)
// Step 3: Translate the extracted messages
// Edit generated JSON/GOTEXT files
// Step 4: Generate catalog code
// Pipeline generates Go code with embedded translations
// Step 5: Use in application
// Import generated catalog and use with printersBased on: