or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

collation.mdencoding.mdformatting.mdindex.mdlanguage.mdlocalization.mdsearch-and-security.mdtext-transformation.mdunicode.md
tile.json

localization.mddocs/

Message Localization

This document covers the message localization system including message extraction, translation management, catalog building, and localized message printing with plural support.

Package Overview

  • message: Localized formatted I/O (drop-in replacement for fmt)
  • message/catalog: Message catalog building and management
  • message/pipeline: Translation pipeline tools
  • feature/plural: Plural rule handling

Message Package

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 Type

// Printer implements language-specific formatted I/O
type Printer struct{}

func NewPrinter(t language.Tag, opts ...Option) *Printer

Printer Methods

// 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 Type

// 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 Type

// 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

Package Functions

// 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.Tag

Usage Examples

import (
    "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!"

Message Catalog Package

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 Interface

// Catalog allows lookup of translated messages
type Catalog interface {
    Languages() []language.Tag
    Matcher() language.Matcher
    Context(tag language.Tag, r catmsg.Renderer) *Context
}

Builder Type

// Builder allows building a Catalog programmatically
type Builder struct{}

func NewBuilder(opts ...Option) *Builder

Builder Methods

func (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 Type

// 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 Type

// 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 Interface

// Dictionary is a source of translations for a single language
type Dictionary interface {
    Lookup(key string) (data string, ok bool)
}

Option Type

// Option configures Catalog behavior
type Option func(*options)

// Fallback specifies the default fallback language (default is Und)
func Fallback(tag language.Tag) Option

Functions

// NewFromMap creates a Catalog from the given map
func NewFromMap(dictionaries map[string]Dictionary, opts ...Option) (Catalog, error)

Errors

// ErrNotFound indicates there was no message for the given key
var ErrNotFound error

Usage Examples

import (
    "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}!"),
)

Message Pipeline Package

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 Type

// 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 Type

// 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)

State Methods

// 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 Type

// Messages is used to store translations for a single language
type Messages struct {
    Language language.Tag
    Messages []Message
    Macros   map[string]Text
}

Message Type

// 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 Type

// 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 Type

// 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 Type

// Select selects a Text based on the feature value
type Select struct {
    Feature string
    Arg     string
    Cases   map[string]Text
}

Feature Type

// Feature holds information about a feature
type Feature struct {
    Type string // Right now this is only gender and plural
}

IDList Type

// IDList is a set of identifiers
type IDList []string

func (id IDList) MarshalJSON() ([]byte, error)
func (id IDList) UnmarshalJSON(b []byte) error

Functions

// 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)

Usage Examples

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()

Feature Plural Package

Import path: golang.org/x/text/feature/plural

Provides utilities for handling linguistic plurals in text based on CLDR plural rules.

Form Type

// Form defines a plural form
type Form byte

const (
    Other Form = iota
    Zero
    One
    Two
    Few
    Many
)

Rules Type

// 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

Rules Methods

// 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 Type

// 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)
}

Functions

// Selectf returns the first case matching the arg-th substitution
func Selectf(arg int, format string, cases ...interface{}) catalog.Message

Constants

const CLDRVersion string = "32"

Usage Examples

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"

Common Patterns

Setting Up a Multi-Language Application

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))
}

Message Keys with Context

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
}

Plural Handling

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)
}

HTTP Handler with Localization

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))
}

Loading Translations from Files

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
}

Formatted Messages with Variables

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."

Message Extraction Workflow

// 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 printers

Version Information

Based on:

  • CLDR 32
  • Unicode Plural Rules (Unicode Technical Report #35)
  • ICU Message Format