Skip to main content

Ghidul complet pentru internaționalizarea Go

De la fișierele de mesaje la determinarea sigură pentru goroutine a setării regionale: configurați internaționalizarea aplicației Go cu go-i18n, apoi automatizați traducerile cu IA.

1

Instalați go-i18n

go-i18n este cea mai populară bibliotecă de internaționalizare pentru Go. Utilizează reguli de plural CLDR și șabloane Go pentru interpolarea variabilelor și acceptă fișiere de mesaje JSON, TOML și YAML. Pentru asocierea etichetelor de limbă aveți nevoie și de golang.org/x/text.

go-i18n v2 necesită Go 1.16+. Pachetul golang.org/x/text asigură analizarea și asocierea etichetelor de limbă BCP 47, utilizate intern de go-i18n pentru selectarea regulilor de plural.
Terminal
go get -u github.com/nicksnyder/go-i18n/v2/i18n
go get -u golang.org/x/text/language
2

Creați fișierele de mesaje

Creați câte un fișier JSON pentru fiecare limbă într-un director locales. Fiecare mesaj are un ID și una sau mai multe forme de plural. Go-i18n utilizează sintaxa șabloanelor Go (acolade duble cu un punct ca prefix) pentru interpolarea variabilelor.

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."
  }
}
Utilizați ID-uri descriptive pentru mesaje, precum 'ItemCount' sau 'WelcomeBack', în locul căilor separate prin puncte. go-i18n utilizează ID-uri plate, nu chei imbricate. Păstrați ID-urile în PascalCase pentru a respecta convențiile Go.
3

Încărcați Bundle

Bundle este registrul central al go-i18n. Creați unul la pornire, înregistrați formatul fișierului și încărcați toate fișierele de mesaje. Bundle poate fi utilizat în siguranță de goroutine — creați-l o singură dată și partajați-l în întreaga aplicație.

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) // "こんにちは、世界!"
}
Dacă vedeți 'message not found', verificați trei aspecte: 1) Fișierul de mesaje a fost încărcat cu LoadMessageFile sau MustLoadMessageFile. 2) Extensia fișierului corespunde funcției de deserializare înregistrate. 3) MessageID din LocalizeConfig corespunde exact cheii din fișierul JSON, inclusiv scrierea cu majuscule și minuscule.
4

Utilizați Localizer

Creați câte un Localizer pentru fiecare solicitare, folosind limba preferată a utilizatorului. Localizer identifică mesajele din Bundle, gestionează redarea șabloanelor cu motorul text/template din Go și selectează forma corectă de plural pe baza 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 acceptă mai multe șiruri de limbă și le încearcă în ordine. Transmiteți direct antetul Accept-Language: i18n.NewLocalizer(bundle, r.Header.Get("Accept-Language")). go-i18n analizează antetul și îl asociază automat traducerilor disponibile.
5

Gestionați regulile de plural

go-i18n implementează regulile de plural CLDR pentru toate limbile. Engleza are 2 forme (one, other). Araba are 6 (zero, one, two, few, many, other). Japoneza are 1 (other). Definiți toate formele necesare în fișierele de mesaje — go-i18n o selectează pe cea corectă pe baza 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 și TemplateData sunt separate. PluralCount selectează forma de plural, iar TemplateData furnizează valorile pentru redarea șablonului. Dacă numărul trebuie să apară în textul mesajului, transmiteți-l în ambele: PluralCount: n și TemplateData: map[string]interface{'}'{"Count": n}.
6

Detectarea setării regionale

În aplicațiile web, detectați limba preferată a utilizatorului din mai multe surse: parametri de interogare, module cookie, antetul Accept-Language sau segmentele căii URL. Utilizați golang.org/x/text/language.Matcher pentru negocierea limbii conformă cu 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 returnează cea mai bună potrivire dintre limbile acceptate, nu preferința brută a utilizatorului. Dacă un utilizator solicită 'pt-BR', iar dumneavoastră acceptați numai 'pt', instrumentul de asociere returnează corect 'pt'. Fără acesta, ar trebui să implementați manual logica de rezervă pentru fiecare variantă regională.
7

Corectați rezerva pentru fiecare cheie cu go-locale-chain

go-i18n are o limitare cunoscută: după asocierea unei setări regionale care conține cel puțin o traducere, cheile lipsă nu sunt căutate în următoarea setare regională din lanț. Un utilizator pt-BR cu un fișier pt-BR tradus parțial primește șiruri goale în loc să se utilizeze pt-PT sau pt ca rezervă. go-locale-chain remediază problema prin determinarea rezervei pentru fiecare cheie în 75 de lanțuri integrate de setări regionale.

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 este un pachet Go cu sursă deschisă și fără dependențe externe. Acesta completează go-i18n — utilizați go-i18n pentru încărcarea mesajelor, pluralizare și redarea șabloanelor, iar go-locale-chain pentru identificarea corectă a lanțului de rezervă.
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
Utilizați ConfigureWithOverrides() pentru a personaliza anumite lanțuri, păstrând valorile implicite. De exemplu, simplificați pt-BR astfel încât să revină numai la pt sau adăugați un lanț pentru o setare regională care nu există în configurația implicită, precum sv-FI -> sv.
8

Automatizați traducerile

După finalizarea configurării i18n, traduceți fișierele de localizare cu ajutorul IA. Generați traduceri pentru toate limbile-țintă din fișierul-sursă în engleză — direct din IDE sau în fluxul 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
Traduceți incremental — când adăugați ID-uri noi de mesaje în fișierul-sursă, traduceți numai cheile noi, fără a regenera toate fișierele. Astfel păstrați traducerile verificate de oameni.

Automatizați controlul calității traducerilor

Identificați cheile lipsă și substituenții nevalizi înainte de lansare cu i18n-validate. Testați interfața cu pseudotraduceri folosind i18n-pseudo înainte de sosirea traducerilor reale.

Probleme frecvente

PluralCount și TemplateData nu corespund

PluralCount selectează forma de plural, dar nu introduce valoarea în șablon. Trebuie să transmiteți numărul și în TemplateData pentru ca acesta să apară în mesajul redat. Fără TemplateData, '{'{.Count}'}' este redat ca '<no value>'.

RegisterUnmarshalFunc lipsește

LoadMessageFile nu returnează niciun mesaj și nu afișează erori dacă omiteți apelarea bundle.RegisterUnmarshalFunc() pentru formatul fișierului. Înregistrați întotdeauna json.Unmarshal (sau toml/yaml) înainte de încărcarea fișierelor.

Rezerva pentru fiecare cheie nu funcționează

NewLocalizer din go-i18n acceptă mai multe limbi, dar după ce o setare regională corespunde cel puțin unei chei, cheile lipsă returnează șiruri goale în loc să utilizeze mecanismul de rezervă. Utilizați go-locale-chain pentru a remedia problema printr-o succesiune corectă de rezerve pentru fiecare cheie.

Sintaxa șablonului: {'{.Var}'}, nu {'{Var}'}

go-i18n utilizează sintaxa text/template din Go. Variabilele trebuie să aibă ca prefix un punct: {'{.Name}'}, nu {'{Name}'}. Punctul face referire la harta TemplateData. Omiterea punctului provoacă o eroare la executarea șablonului.

Structura recomandată a fișierelor

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

Încercați acum i18n Agent

Plasați aici fișierul de traducere

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

sau faceți clic pentru a-l selecta

Limbi țintă

Nu este necesară înregistrareaEstimare instantanee

Mecanism de rezervă pentru setările regionale cu go-locale-chain

Atunci când lipsește o cheie de traducere dintr-o setare regională precum pt-BR, go-i18n revine direct la limba implicită în loc să verifice mai întâi setarea regională părinte 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"},
    },
})

Consultați Ghidul nostru privind mecanismele de rezervă pentru setările regionale, care conține lista completă a platformelor acceptate și cele 75 de lanțuri integrate. Learn more →

Întrebări frecvente despre Go i18n