Go supplementary packages for text processing, many involving Unicode
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:
Install with Tessl CLI
npx tessl i tessl/golang-golang-org-x--text