Skip to main content

Le guide complet de l'internationalisation en Go

Des fichiers de messages à la résolution de locale sûre pour les goroutines : configurez l'i18n dans votre application Go avec go-i18n, puis automatisez les traductions avec l'IA.

1

Installer go-i18n

go-i18n est la bibliothèque d'internationalisation la plus utilisée pour Go. Elle utilise les règles de pluriel CLDR, les modèles Go pour l'interpolation de variables, et prend en charge les fichiers de messages JSON, TOML et YAML. Vous avez également besoin de golang.org/x/text pour la correspondance des balises de langue.

go-i18n v2 nécessite Go 1.16 ou supérieur. Le package golang.org/x/text fournit l'analyse et la correspondance des balises de langue BCP 47, que go-i18n utilise en interne pour sélectionner les règles de pluriel.
Terminal
go get -u github.com/nicksnyder/go-i18n/v2/i18n
go get -u golang.org/x/text/language
2

Créer les fichiers de messages

Créez un fichier JSON par langue dans un répertoire locales. Chaque message possède un identifiant et une ou plusieurs formes de pluriel. Go-i18n utilise la syntaxe des modèles Go (doubles accolades avec un préfixe point) pour l'interpolation de variables.

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."
  }
}
Utilisez des identifiants de message descriptifs comme « ItemCount » ou « WelcomeBack » plutôt que des chemins séparés par des points. go-i18n utilise des identifiants plats, et non des clés imbriquées. Conservez des identifiants en PascalCase pour respecter les conventions Go.
3

Charger le Bundle

Le Bundle est le registre central de go-i18n. Créez-en un au démarrage, enregistrez votre format de fichier, puis chargez tous les fichiers de messages. Le Bundle est sûr pour les goroutines : créez-le une seule fois et partagez-le dans toute votre application.

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) // "こんにちは、世界!"
}
Si vous obtenez « message not found », vérifiez trois points : 1) le fichier de messages a bien été chargé avec LoadMessageFile ou MustLoadMessageFile ; 2) l'extension du fichier correspond à la fonction de désérialisation enregistrée ; 3) le MessageID dans LocalizeConfig correspond exactement à la clé de votre fichier JSON (sensible à la casse).
4

Utiliser le Localizer

Créez un Localizer pour chaque requête avec la langue préférée de l'utilisateur. Le Localizer résout les messages à partir du Bundle, gère le rendu des modèles avec le moteur text/template de Go, et sélectionne la forme de pluriel appropriée en fonction de 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 accepte plusieurs chaînes de langue : il les essaie dans l'ordre. Transmettez directement l'en-tête Accept-Language : i18n.NewLocalizer(bundle, r.Header.Get("Accept-Language")). go-i18n analyse l'en-tête et le fait correspondre automatiquement aux traductions disponibles.
5

Gérer les règles de pluriel

go-i18n implémente les règles de pluriel CLDR pour toutes les langues. L'anglais compte 2 formes (one, other). L'arabe en compte 6 (zero, one, two, few, many, other). Le japonais n'en a qu'une (other). Définissez toutes les formes requises dans vos fichiers de messages : go-i18n sélectionne la bonne en fonction de 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 et TemplateData sont indépendants. PluralCount sélectionne la forme de pluriel, TemplateData fournit les valeurs pour le rendu du modèle. Si vous avez besoin du nombre dans le texte du message, transmettez-le dans les deux : PluralCount: n et TemplateData: map[string]interface{'}'{"Count": n}.
6

Détection de la locale

Dans les applications web, détectez la langue préférée de l'utilisateur à partir de plusieurs sources : paramètres de requête, cookies, en-tête Accept-Language ou segments du chemin de l'URL. Utilisez golang.org/x/text/language.Matcher pour une négociation de langue conforme à 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 renvoie la meilleure correspondance parmi vos langues prises en charge, et non la préférence brute de l'utilisateur. Si un utilisateur demande « pt-BR » et que vous ne prenez en charge que « pt », le matcher renvoie correctement « pt ». Sans matcher, vous devriez implémenter une logique de repli manuelle pour chaque variante régionale.
7

Corriger le repli par clé avec go-locale-chain

go-i18n présente une limitation connue : dès qu'une locale correspond (dès qu'elle possède des traductions chargées), les clés manquantes ne basculent pas vers la locale suivante de la chaîne. Un utilisateur pt-BR disposant d'un fichier pt-BR partiellement traduit obtient des chaînes vides au lieu d'un repli vers pt-PT ou pt. go-locale-chain corrige ce comportement grâce à une résolution de repli par clé s'appuyant sur 75 chaînes de locales intégrées.

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 est un package Go open source sans aucune dépendance externe. Il complète go-i18n : utilisez go-i18n pour le chargement des messages, la pluralisation et le rendu des modèles, et go-locale-chain pour une résolution correcte de la chaîne de repli.
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
Utilisez ConfigureWithOverrides() pour personnaliser certaines chaînes tout en conservant les valeurs par défaut. Par exemple, simplifiez pt-BR pour qu'il ne se replie que sur pt, ou ajoutez une chaîne pour une locale absente des valeurs par défaut, comme sv-FI -> sv.
8

Automatiser les traductions

Une fois votre configuration i18n terminée, traduisez vos fichiers de locale avec l'IA. Générez les traductions pour toutes les langues cibles à partir de votre fichier source en anglais, directement depuis votre IDE ou dans votre 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
Traduisez de façon incrémentale : lorsque vous ajoutez de nouveaux identifiants de message à votre fichier source, ne traduisez que les nouvelles clés plutôt que de régénérer tous les fichiers. Cela préserve les traductions déjà relues par un humain.

Automatiser la qualité des traductions

Détectez les clés manquantes et les espaces réservés cassés avant leur mise en production grâce à i18n-validate. Testez votre interface avec des pseudo-traductions grâce à i18n-pseudo avant l'arrivée des traductions réelles.

Pièges courants

Incohérence entre PluralCount et TemplateData

PluralCount sélectionne la forme de pluriel, mais n'injecte pas la valeur dans le modèle. Vous devez également transmettre le nombre dans TemplateData pour qu'il apparaisse dans le message généré. Sans TemplateData, '{'{.Count}'}' s'affiche sous la forme '<no value>'.

RegisterUnmarshalFunc manquant

LoadMessageFile ne renvoie silencieusement aucun message si vous oubliez d'appeler bundle.RegisterUnmarshalFunc() pour le format de fichier concerné. Enregistrez toujours json.Unmarshal (ou toml/yaml) avant de charger les fichiers.

Le repli par clé ne fonctionne pas

NewLocalizer de go-i18n accepte plusieurs langues, mais dès qu'une locale correspond à une seule clé, les clés manquantes renvoient des chaînes vides au lieu d'effectuer un repli. Utilisez go-locale-chain pour corriger ce comportement grâce à une cascade correcte par clé.

Syntaxe de modèle : {'{.Var}'} et non {'{Var}'}

go-i18n utilise la syntaxe text/template de Go. Les variables doivent être précédées d'un point : {'{.Name}'}, et non {'{Name}'}. Le point fait référence à la map TemplateData. L'omission du point provoque une erreur d'exécution du modèle.

Structure de fichiers recommandée

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

Essayez i18n Agent maintenant

Déposez votre fichier de traduction ici

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

ou cliquez pour parcourir

Langues cibles

Aucune inscription requiseEstimation instantanée

Repli de locale avec go-locale-chain

Lorsqu'une clé de traduction est manquante dans une locale régionale comme pt-BR, go-i18n passe directement à la langue par défaut au lieu de vérifier d'abord la locale parente 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"},
    },
})

Consultez notre guide de repli de locale pour découvrir la liste complète des frameworks pris en charge et les 75 chaînes intégrées. Learn more →

FAQ Go i18n