Skip to main content

La guida completa all'internazionalizzazione di Go

Dai file dei messaggi alla risoluzione delle lingue sicura per le goroutine: configuri l'i18n nell'app Go con go-i18n, quindi automatizzi le traduzioni con l'IA.

1

Installare go-i18n

go-i18n è la biblioteca di internazionalizzazione più diffusa per Go. Usa le regole del plurale CLDR, i modelli Go per interpolare le variabili e supporta file dei messaggi JSON, TOML e YAML. Serve inoltre golang.org/x/text per la corrispondenza dei tag di lingua.

go-i18n v2 richiede Go 1.16 o versione successiva. Il pacchetto golang.org/x/text fornisce l'analisi e la corrispondenza dei tag di lingua BCP 47, usate internamente da go-i18n per selezionare le regole del plurale.
Terminal
go get -u github.com/nicksnyder/go-i18n/v2/i18n
go get -u golang.org/x/text/language
2

Creare file dei messaggi

Crei un file JSON per ogni lingua in una directory locales. Ogni messaggio ha un ID e una o più forme plurali. Go-i18n usa la sintassi dei modelli Go (doppie parentesi graffe con prefisso punto) per interpolare le variabili.

locales/en.json
{
  "HelloWorld": { "other": "Hello, World!" },
  "Greeting": { "other": "Hello, {{.Name}}!" },
  "ItemCount": {
    "one": "{{.Count}} item",
    "other": "{{.Count}} items"
  },
  "WelcomeBack": {
    "other": "Welcome back, {{.Name}}. You have {{.Count}} messages."
  }
}
Usi ID descrittivi come 'ItemCount' o 'WelcomeBack' anziché percorsi separati da punti. go-i18n usa ID semplici, non chiavi annidate. Mantenga gli ID in PascalCase per rispettare le convenzioni Go.
3

Caricare il bundle

Bundle è il registro centrale di go-i18n. Ne crei uno all'avvio, registri il formato dei file e carichi tutti i file dei messaggi. Bundle è sicuro per le goroutine: lo crei una volta e lo condivida nell'intera applicazione.

main.go
package main

import (
    "encoding/json"
    "fmt"
    "github.com/nicksnyder/go-i18n/v2/i18n"
    "golang.org/x/text/language"
)

func main() {
    // 1. Create a bundle with a default language
    bundle := i18n.NewBundle(language.English)
    // 2. Register the unmarshal function for your file format
    bundle.RegisterUnmarshalFunc("json", json.Unmarshal)
    // 3. Load message files
    bundle.MustLoadMessageFile("locales/en.json")
    bundle.MustLoadMessageFile("locales/ja.json")
    bundle.MustLoadMessageFile("locales/de.json")
    // 4. Create a localizer and localize a message
    localizer := i18n.NewLocalizer(bundle, "ja")
    msg := localizer.MustLocalize(&i18n.LocalizeConfig{
        MessageID: "HelloWorld",
    })
    fmt.Println(msg) // "こんにちは、世界!"
}
Se vede 'message not found', controlli tre aspetti: 1) Il file dei messaggi è stato caricato con LoadMessageFile o MustLoadMessageFile. 2) L'estensione del file corrisponde alla funzione unmarshal registrata. 3) MessageID in LocalizeConfig corrisponde esattamente, rispettando maiuscole e minuscole, alla chiave nel file JSON.
4

Usare Localizer

Crei un Localizer per ogni richiesta con la lingua preferita dall'utente. Localizer risolve i messaggi dal Bundle, gestisce il rendering dei modelli con il motore text/template di Go e seleziona la forma plurale corretta in base a PluralCount.

Using the Localizer
// Simple message
msg := localizer.MustLocalize(&i18n.LocalizeConfig{
    MessageID: "HelloWorld",
})

// Message with template data
msg := localizer.MustLocalize(&i18n.LocalizeConfig{
    MessageID: "Greeting",
    TemplateData: map[string]interface{}{
        "Name": "Alice",
    },
})
// "Hello, Alice!" (en) or "こんにちは、Aliceさん!" (ja)

// Plural + template data
msg := localizer.MustLocalize(&i18n.LocalizeConfig{
    MessageID: "ItemCount",
    PluralCount: 5,
    TemplateData: map[string]interface{}{
        "Count": 5,
    },
})
// "5 items" (en) or "5個のアイテム" (ja)

// Combined: plurals + multiple variables
msg := localizer.MustLocalize(&i18n.LocalizeConfig{
    MessageID: "WelcomeBack",
    TemplateData: map[string]interface{}{
        "Name":  "Alice",
        "Count": 3,
    },
})
HTTP handler with locale detection
func handler(w http.ResponseWriter, r *http.Request) {
    // Accept-Language: ja,en;q=0.9,de;q=0.8
    accept := r.Header.Get("Accept-Language")

    // NewLocalizer accepts multiple languages — first match wins
    localizer := i18n.NewLocalizer(bundle, accept)

    msg := localizer.MustLocalize(&i18n.LocalizeConfig{
        MessageID: "HelloWorld",
    })

    w.Header().Set("Content-Type", "text/plain; charset=utf-8")
    w.Write([]byte(msg))
}
NewLocalizer accetta più stringhe di lingua e le prova in ordine. Passi direttamente l'intestazione Accept-Language: i18n.NewLocalizer(bundle, r.Header.Get("Accept-Language")). go-i18n analizza l'intestazione e la confronta automaticamente con le traduzioni disponibili.
5

Gestire le regole del plurale

go-i18n implementa le regole del plurale CLDR per tutte le lingue. L'inglese ha 2 forme (one, other), l'arabo 6 (zero, one, two, few, many, other) e il giapponese 1 (other). Definisca tutte le forme richieste nei file dei messaggi: go-i18n selezionerà quella corretta in base a PluralCount.

Plural forms by language
// English: 2 forms (one, other)
{
  "ItemCount": {
    "one": "{{.Count}} item",
    "other": "{{.Count}} items"
  }
}

// Arabic: 6 forms (zero, one, two, few, many, other)
{
  "ItemCount": {
    "zero": "لا عناصر",
    "one": "عنصر واحد",
    "two": "عنصران",
    "few": "{{.Count}} عناصر",
    "many": "{{.Count}} عنصرًا",
    "other": "{{.Count}} عنصر"
  }
}

// Japanese: 1 form (other)
{
  "ItemCount": {
    "other": "{{.Count}}個のアイテム"
  }
}
Using plural rules
// go-i18n selects the correct plural form based on PluralCount
localizer := i18n.NewLocalizer(bundle, "ar")

msg := localizer.MustLocalize(&i18n.LocalizeConfig{
    MessageID:   "ItemCount",
    PluralCount: 3,
    TemplateData: map[string]interface{}{
        "Count": 3,
    },
})
// Arabic "few" form: "3 عناصر"

msg = localizer.MustLocalize(&i18n.LocalizeConfig{
    MessageID:   "ItemCount",
    PluralCount: 1,
    TemplateData: map[string]interface{}{
        "Count": 1,
    },
})
// Arabic "one" form: "عنصر واحد"
PluralCount e TemplateData sono distinti. PluralCount seleziona la forma plurale, mentre TemplateData fornisce i valori per il rendering del modello. Se il conteggio deve apparire nel testo del messaggio, lo passi in entrambi: PluralCount: n e TemplateData: map[string]interface{'}'{"Count": n}.
6

Rilevamento della lingua

Nelle applicazioni web, rilevi la lingua preferita dall'utente da più fonti: parametri di query, cookie, intestazione Accept-Language o segmenti del percorso URL. Usi golang.org/x/text/language.Matcher per una negoziazione linguistica conforme a BCP 47.

Locale detection middleware
// detectLocale resolves the user's preferred language.
// Priority: query param > cookie > Accept-Language header > default
func detectLocale(r *http.Request, matcher language.Matcher) string {
    // 1. Explicit query parameter: ?lang=ja
    if lang := r.URL.Query().Get("lang"); lang != "" {
        tag, _, _ := matcher.Match(language.Make(lang))
        return tag.String()
    }
    // 2. Cookie from previous selection
    if cookie, err := r.Cookie("lang"); err == nil {
        tag, _, _ := matcher.Match(language.Make(cookie.Value))
        return tag.String()
    }
    // 3. Accept-Language header
    accept := r.Header.Get("Accept-Language")
    if accept != "" {
        tags, _, _ := language.ParseAcceptLanguage(accept)
        if len(tags) > 0 {
            tag, _, _ := matcher.Match(tags...)
            return tag.String()
        }
    }
    return "en" // 4. Default
}

// Usage:
matcher := language.NewMatcher([]language.Tag{
    language.English, language.Japanese,
    language.German, language.Spanish,
})

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    locale := detectLocale(r, matcher)
    localizer := i18n.NewLocalizer(bundle, locale)
    // ... use localizer
})
language.NewMatcher restituisce la migliore corrispondenza tra le lingue supportate, non la preferenza non elaborata dell'utente. Se un utente richiede 'pt-BR' e Lei supporta soltanto 'pt', il matcher restituisce correttamente 'pt'. Senza matcher, servirebbe una logica di fallback manuale per ogni variante regionale.
7

Correggere il fallback per singola chiave con go-locale-chain

go-i18n presenta un limite noto: quando una lingua corrisponde, ossia contiene una qualsiasi traduzione caricata, le chiavi mancanti non passano alla lingua successiva della catena. Un utente pt-BR con un file pt-BR tradotto solo in parte riceve stringhe vuote anziché il fallback a pt-PT o pt. go-locale-chain risolve il problema con il fallback per singola chiave in 75 catene linguistiche integrate.

Terminal
go get github.com/i18n-agent/go-locale-chain
go-locale-chain with go-i18n
package main

import (
    "encoding/json"
    "fmt"

    "github.com/nicksnyder/go-i18n/v2/i18n"
    localechain "github.com/i18n-agent/go-locale-chain"
    "golang.org/x/text/language"
)

func main() {
    // 1. Configure fallback chains (call once at startup)
    localechain.Configure()

    // 2. Set up go-i18n bundle as usual
    bundle := i18n.NewBundle(language.English)
    bundle.RegisterUnmarshalFunc("json", json.Unmarshal)
    bundle.MustLoadMessageFile("locales/en.json")
    bundle.MustLoadMessageFile("locales/pt.json")
    bundle.MustLoadMessageFile("locales/pt-PT.json")
    bundle.MustLoadMessageFile("locales/pt-BR.json")

    // 3. Resolve with per-key fallback
    result, _ := localechain.ResolveWithLoader("pt-BR", func(locale string) (map[string]string, error) {
        localizer := i18n.NewLocalizer(bundle, locale)
        messages := make(map[string]string)
        for _, id := range []string{"hello", "goodbye", "thanks"} {
            msg, err := localizer.Localize(&i18n.LocalizeConfig{MessageID: id})
            if err == nil {
                messages[id] = msg
            }
        }
        return messages, nil
    })

    fmt.Println(result["hello"])   // "Olá (BR)"   — from pt-BR
    fmt.Println(result["goodbye"]) // "Adeus (PT)" — fallback to pt-PT
    fmt.Println(result["thanks"])  // "Obrigado"   — fallback to pt
}
go-locale-chain è un pacchetto Go open source senza dipendenze esterne. Completa go-i18n: usi go-i18n per caricare i messaggi, gestire i plurali e visualizzare i modelli e go-locale-chain per risolvere correttamente le catene di fallback.
Standalone usage (no go-i18n)
// Standalone: zero external dependencies, works with any format
localechain.Configure()

result, _ := localechain.ResolveWithLoader("es-MX", func(locale string) (map[string]string, error) {
    data, err := os.ReadFile(fmt.Sprintf("locales/%s.json", locale))
    if err != nil {
        return nil, err // Locale file doesn't exist — skip
    }
    var msgs map[string]string
    json.Unmarshal(data, &msgs)
    return msgs, nil
})
// es-MX -> es-419 -> es: each key resolved from most specific locale
Usi ConfigureWithOverrides() per personalizzare catene specifiche mantenendo quelle predefinite. Ad esempio, semplifichi pt-BR affinché passi soltanto a pt oppure aggiunga una catena per una lingua non inclusa nelle impostazioni predefinite, come sv-FI → sv.
8

Automatizzare le traduzioni

Dopo aver completato la configurazione i18n, traduca i file di lingua con l'IA. Generi le traduzioni per tutte le lingue di destinazione dal file di origine inglese, direttamente dall'IDE o nella pipeline CI/CD.

Terminal
# In your IDE, ask your AI assistant:
> Translate locales/en.json to German, Japanese, and Spanish

✓ locales/de.json created (1.2s)
✓ locales/ja.json created (1.5s)
✓ locales/es.json created (1.1s)

# Or use the CLI in CI/CD:
npx i18n-agent translate locales/en.json --lang de,ja,es
Traduca in modo incrementale: quando aggiunge nuovi ID di messaggio al file di origine, traduca soltanto le nuove chiavi anziché rigenerare tutti i file. In questo modo preserva le traduzioni revisionate da persone.

Automatizzare la qualità

Con i18n-validate, rilevi chiavi mancanti e segnaposto non validi prima del rilascio. Testi l'interfaccia con le pseudotraduzioni di i18n-pseudo prima che arrivino le traduzioni reali.

Problemi comuni

Mancata corrispondenza tra PluralCount e TemplateData

PluralCount seleziona la forma plurale, ma non inserisce il valore nel modello. Deve passare il conteggio anche in TemplateData affinché appaia nel messaggio visualizzato. Senza TemplateData, '{'{.Count}'}' viene visualizzato come '<no value>'.

RegisterUnmarshalFunc mancante

LoadMessageFile non restituisce alcun messaggio e non segnala errori se dimentica di chiamare bundle.RegisterUnmarshalFunc() per il formato del file. Registri sempre json.Unmarshal, oppure toml/yaml, prima di caricare i file.

Il fallback per singola chiave non funziona

NewLocalizer di go-i18n accetta più lingue, ma dopo che una lingua corrisponde a una sola chiave, le chiavi mancanti restituiscono stringhe vuote anziché passare alla lingua successiva. Usi go-locale-chain per risolvere il problema con una corretta applicazione in sequenza per singola chiave.

Sintassi dei modelli: {'{.Var}'} e non {'{Var}'}

go-i18n usa la sintassi text/template di Go. Le variabili devono avere il prefisso punto: {'{.Name}'}, non {'{Name}'}. Il punto si riferisce alla mappa TemplateData. Senza il punto, si verifica un errore di esecuzione del modello.

Struttura dei file consigliata

Project Structure
my-go-app/
├── locales/
│   ├── en.json           # Source language (English)
│   ├── de.json           # German translations
│   ├── ja.json           # Japanese translations
│   ├── es.json           # Spanish translations
│   ├── pt.json           # Portuguese (base)
│   ├── pt-PT.json        # Portuguese (Portugal)
│   └── pt-BR.json        # Portuguese (Brazil)
├── i18n/
│   ├── bundle.go         # Bundle initialization
│   ├── detect.go         # Locale detection logic
│   └── middleware.go      # HTTP middleware for locale
├── main.go
├── go.mod
└── go.sum

Provi subito i18n Agent

Trascinare qui il file di traduzione

JSON, YAML, PO, XML, CSV, Markdown, Properties

oppure fare clic per selezionarlo

Lingue di destinazione

Nessuna registrazione richiestaPreventivo immediato

Fallback della lingua con go-locale-chain

Quando manca una chiave di traduzione in una lingua regionale come pt-BR, go-i18n passa direttamente alla lingua predefinita anziché controllare prima la lingua principale pt.

Terminal
go get github.com/i18n-agent/go-locale-chain
Configuration
import localechain "github.com/i18n-agent/go-locale-chain"

chain := localechain.New(localechain.Config{
    Fallbacks: map[string][]string{
        "pt-BR":      {"pt", "en"},
        "zh-Hant-HK": {"zh-Hant", "zh", "en"},
    },
})

Consultare la Guida al fallback delle lingue per l'elenco completo dei framework supportati e delle 75 catene integrate. Learn more →

Domande frequenti sull'i18n di Go