Table des matières
Go (Golang) i18n : Modèles et Bibliothèques d'Internationalisation
Go est un langage de plus en plus populaire pour les services backend, les CLIs et les APIs qui servent des utilisateurs mondiaux. Bien que la bibliothèque standard de Go n'inclue pas un framework i18n aussi complet que certains autres écosystèmes, la combinaison de golang.org/x/text, go-i18n et une architecture soigneuse permet de créer des applications Go multilingues robustes.
Ce guide couvre le kit d'outils i18n complet pour Go—du formatage adapté aux paramètres régionaux avec x/text à la gestion des traductions avec go-i18n, ainsi que des modèles pour structurer l'i18n dans les services Go et les CLIs.
Vue d'Ensemble de l'Écosystème i18n de Go
Les outils i18n de Go sont répartis entre plusieurs paquets :
| Paquet | Objectif |
|---|---|
golang.org/x/text/language | Analyse et correspondance des balises de langues BCP 47 |
golang.org/x/text/message | Messages formatés de style Printf avec i18n |
golang.org/x/text/number | Formatage des nombres (pluriels, monnaie) |
golang.org/x/text/collate | Tri de chaînes adapté aux paramètres régionaux |
golang.org/x/text/unicode/norm | Normalisation Unicode |
github.com/nicksnyder/go-i18n/v2 | Gestion des fichiers de traduction, pluralisation |
github.com/BurntSushi/toml | Analyse TOML (format de traduction courant) |
Analyse des Balises de Langues avec golang.org/x/text
Le paquet golang.org/x/text/language implémente les balises de langues BCP 47—le standard pour les identifiants de paramètres régionaux utilisés dans les en-têtes HTTP Accept-Language, les attributs HTML lang et les chaînes de paramètres régionaux.
Analyse et Correspondance des Balises de Langues
package main
import (
"fmt"
"golang.org/x/text/language"
)
func main() {
// Analyser une balise de langue
tag, err := language.Parse("zh-Hant-TW")
if err != nil {
panic(err)
}
fmt.Println(tag) // zh-Hant-TW
// Correspondance de langues : trouver la meilleure correspondance parmi les langues supportées
supported := []language.Tag{
language.English,
language.French,
language.SimplifiedChinese,
language.TraditionalChinese,
}
matcher := language.NewMatcher(supported)
// L'utilisateur demande zh-TW (Chinois Traditionnel, Taïwan)
t, _, _ := matcher.Match(language.MustParse("zh-TW"))
fmt.Println(t) // zh-Hant → correspond au Chinois Traditionnel
// L'utilisateur demande es-MX (Espagnol Mexicain)
t, _, _ = matcher.Match(language.MustParse("es-MX"))
fmt.Println(t) // en → repli sur l'anglais (Espagnol non supporté)
}
Analyse des En-têtes Accept-Language
import (
"net/http"
"golang.org/x/text/language"
)
var supported = []language.Tag{
language.English,
language.French,
language.German,
language.Japanese,
}
var matcher = language.NewMatcher(supported)
func getLocale(r *http.Request) language.Tag {
// Analyser l'en-tête Accept-Language
accept := r.Header.Get("Accept-Language")
tags, _, err := language.ParseAcceptLanguage(accept)
if err != nil || len(tags) == 0 {
return language.English
}
tag, _, _ := matcher.Match(tags...)
return tag
}
go-i18n : Gestion des Traductions
go-i18n est la bibliothèque Go la plus utilisée pour la gestion des traductions. Elle supporte :
- Les fichiers de traduction JSON, TOML et YAML
- Le format de messages ICU pour la pluralisation
- L'interpolation de modèles
- Le chargement de plusieurs fichiers de traduction
Installation et Configuration
go get github.com/nicksnyder/go-i18n/v2@latest go get github.com/BurntSushi/toml
Structure des Fichiers de Traduction
# locales/en.toml
[welcome]
description = "Message de bienvenue pour les nouveaux utilisateurs"
one = "Bienvenue, {{.Name}} ! Vous avez {{.Count}} nouvelle notification."
other = "Bienvenue, {{.Name}} ! Vous avez {{.Count}} nouvelles notifications."
[item_count]
description = "Nombre d'éléments dans une liste"
zero = "Aucun élément"
one = "{{.Count}} élément"
other = "{{.Count}} éléments"
# locales/fr.toml
[welcome]
one = "Bienvenue, {{.Name}} ! Vous avez {{.Count}} nouvelle notification."
other = "Bienvenue, {{.Name}} ! Vous avez {{.Count}} nouvelles notifications."
[item_count]
zero = "Aucun élément"
one = "{{.Count}} élément"
other = "{{.Count}} éléments"
Initialisation de go-i18n
package i18n
import (
"embed"
"encoding/json"
"github.com/BurntSushi/toml"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
)
//go:embed locales/*.toml
var localeFS embed.FS
var bundle *i18n.Bundle
func Init() error {
bundle = i18n.NewBundle(language.English)
bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)
// Charger tous les fichiers de paramètres régionaux
entries, err := localeFS.ReadDir("locales")
if err != nil {
return err
}
for _, entry := range entries {
if _, err := bundle.LoadMessageFileFS(localeFS, "locales/"+entry.Name()); err != nil {
return fmt.Errorf("chargement des paramètres régionaux %s: %w", entry.Name(), err)
}
}
return nil
}
// NewLocalizer crée un localiseur pour les balises de langues données
func NewLocalizer(langs ...string) *i18n.Localizer {
return i18n.NewLocalizer(bundle, langs...)
}
Traduction des Messages
package handlers
import (
"net/http"
"myapp/i18n"
)
type WelcomeData struct {
Name string
Count int
}
func WelcomeHandler(w http.ResponseWriter, r *http.Request) {
// Obtenir le localiseur pour la langue de la requête
accept := r.Header.Get("Accept-Language")
localizer := i18n.NewLocalizer(accept, "en")
// Message simple avec gestion des pluriels
itemCount := 3
msg, err := localizer.Localize(&i18n.LocalizeConfig{
MessageID: "item_count",
PluralCount: itemCount,
TemplateData: map[string]interface{}{
"Count": itemCount,
},
})
if err != nil {
msg = "Nombre d'éléments inconnu" // repli
}
// Message avec nom et pluriel
welcome, err := localizer.Localize(&i18n.LocalizeConfig{
MessageID: "welcome",
PluralCount: itemCount,
TemplateData: WelcomeData{
Name: "Alice",
Count: itemCount,
},
})
fmt.Fprintf(w, "%s\n%s\n", welcome, msg)
}
Formatage des Nombres et des Monnaies
Le paquet golang.org/x/text/message fournit un formatage des nombres adapté aux paramètres régionaux :
package main
import (
"fmt"
"golang.org/x/text/language"
"golang.org/x/text/message"
"golang.org/x/text/number"
)
func main() {
// Créer des imprimantes spécifiques aux paramètres régionaux
enPrinter := message.NewPrinter(language.English)
dePrinter := message.NewPrinter(language.German)
jaPrinter := message.NewPrinter(language.Japanese)
amount := 1234567.89
// Formatage des nombres adapté aux paramètres régionaux
enPrinter.Printf("%v\n", number.Decimal(amount))
// 1,234,567.89
dePrinter.Printf("%v\n", number.Decimal(amount))
// 1.234.567,89
jaPrinter.Printf("%v\n", number.Decimal(amount))
// 1,234,567.89
// Formatage des pourcentages
enPrinter.Printf("%.2%\n", number.Percent(0.8527))
// 85.27%
dePrinter.Printf("%.2%\n", number.Percent(0.8527))
// 85,27 %
}
Formatage des Dates et Heures
Le paquet time de Go gère le formatage de base des dates, mais pour un formatage adapté aux paramètres régionaux, vous avez besoin de x/text ou d'une bibliothèque tierce :
import (
"time"
"golang.org/x/text/language"
"golang.org/x/text/language/display"
)
func formatDateLocale(t time.Time, locale language.Tag) string {
// Le formatage de l'heure intégré à Go utilise des chaînes de mise en page fixes
// Pour un formatage spécifique aux paramètres régionaux, utiliser des bibliothèques équivalentes à strftime
// ou générer des modèles à partir des données CLDR
switch locale {
case language.English:
return t.Format("January 2, 2006")
case language.German:
return t.Format("2. January 2006") // Format de date allemand
case language.Japanese:
return t.Format("2006年01月02日")
default:
return t.Format(time.RFC3339)
}
}
Pour un formatage de dates de qualité production adapté aux paramètres régionaux, utilisez une bibliothèque comme github.com/goodsign/monday pour les noms de jours et de mois.
Structuration de l'i18n dans un Service Web Go
Détection des Paramètres Régionaux Basée sur le Middleware
package middleware
import (
"context"
"net/http"
"golang.org/x/text/language"
"myapp/i18n"
)
type contextKey string
const localizerKey contextKey = "localizer"
var supported = []language.Tag{
language.English,
language.French,
language.German,
language.Japanese,
}
var matcher = language.NewMatcher(supported)
func LocaleMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Vérifier d'abord le paramètre de requête URL (ex. ?lang=fr)
lang := r.URL.Query().Get("lang")
// Puis vérifier le cookie
if lang == "" {
if cookie, err := r.Cookie("lang"); err == nil {
lang = cookie.Value
}
}
// Repli sur l'en-tête Accept-Language
if lang == "" {
lang = r.Header.Get("Accept-Language")
}
// Créer le localiseur et l'attacher au contexte
localizer := i18n.NewLocalizer(lang, "en")
ctx := context.WithValue(r.Context(), localizerKey, localizer)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// GetLocalizer récupère le localiseur depuis le contexte
func GetLocalizer(ctx context.Context) *i18n.Localizer {
l, ok := ctx.Value(localizerKey).(*i18n.Localizer)
if !ok {
return i18n.NewLocalizer("en")
}
return l
}
Messages d'Erreur en tant que Contenu Traduisible
package errors
import (
"github.com/nicksnyder/go-i18n/v2/i18n"
)
// AppError est une erreur avec un message traduisible
type AppError struct {
MessageID string
TemplateData interface{}
Err error
}
func (e *AppError) Error() string {
return e.MessageID // Identifiant interne
}
func (e *AppError) Localize(localizer *i18n.Localizer) string {
msg, err := localizer.Localize(&i18n.LocalizeConfig{
MessageID: e.MessageID,
TemplateData: e.TemplateData,
})
if err != nil {
return e.MessageID // Repli sur la clé
}
return msg
}
// Utilisation
var ErrNotFound = &AppError{MessageID: "error.not_found"}
var ErrUnauthorized = &AppError{MessageID: "error.unauthorized"}
i18n pour les Applications CLI Go
Les applications CLI ont des exigences i18n distinctes : elles doivent détecter les paramètres régionaux du système, formater la sortie de manière appropriée et gérer l'encodage du terminal.
package main
import (
"os"
"golang.org/x/text/language"
"myapp/i18n"
)
func detectSystemLocale() string {
// Vérifier la variable d'environnement LANG (Unix/Linux/macOS)
if lang := os.Getenv("LANG"); lang != "" {
// LANG est généralement au format "en_US.UTF-8"
// Extraire la balise de langue
parts := strings.Split(lang, ".")
if len(parts) > 0 {
// Convertir "en_US" en "en-US" (BCP 47)
return strings.ReplaceAll(parts[0], "_", "-")
}
}
// Vérifier LC_ALL, LC_MESSAGES
for _, env := range []string{"LC_ALL", "LC_MESSAGES"} {
if lang := os.Getenv(env); lang != "" {
return strings.ReplaceAll(strings.Split(lang, ".")[0], "_", "-")
}
}
return "en" // Défaut à l'anglais
}
func main() {
locale := detectSystemLocale()
localizer := i18n.NewLocalizer(locale, "en")
// Utiliser le localiseur pour toutes les sorties
fmt.Println(localizer.MustLocalize(&i18n.LocalizeConfig{
MessageID: "cli.welcome",
}))
}
Outils d'Extraction et de Gestion
go-i18n fournit un outil en ligne de commande goi18n pour extraire les chaînes traduisibles :
# Installer l'outil CLI go install github.com/nicksnyder/go-i18n/v2/goi18n@latest # Extraire les chaînes du code source Go (crée active.en.toml) goi18n extract -format toml -outdir locales ./... # Fusionner les nouvelles chaînes dans les traductions existantes # (crée translate.fr.toml avec uniquement les chaînes nouvelles/modifiées) goi18n merge -format toml -outdir locales locales/active.en.toml locales/active.fr.toml # Après traduction, fusionner translate.fr.toml dans active.fr.toml goi18n merge -format toml -outdir locales locales/active.fr.toml locales/translate.fr.toml
Ce workflow s'intègre bien avec l'automatisation de localisation CI/CD pour des mises à jour de traduction continues.
Pluralisation pour les Langues Complexes
Go-i18n gère automatiquement la pluralisation via les règles CLDR pour toutes les langues supportées :
# locales/ru.toml (Russe - 4 formes de pluriel)
[item_count]
zero = "Нет элементов"
one = "{{.Count}} элемент" # 1, 21, 31...
few = "{{.Count}} элемента" # 2-4, 22-24...
many = "{{.Count}} элементов" # 5-20, 25-30...
other = "{{.Count}} элемента" # fractions, autres
# locales/ar.toml (Arabe - 6 formes de pluriel)
[item_count]
zero = "لا عناصر"
one = "{{.Count}} عنصر"
two = "{{.Count}} عنصران"
few = "{{.Count}} عناصر"
many = "{{.Count}} عنصرًا"
other = "{{.Count}} عنصر"
Pour une couverture plus approfondie des règles de pluralisation, voir les règles de pluralisation dans les différentes langues.
Tests i18n en Go
package i18n_test
import (
"testing"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
)
func TestPluralForms(t *testing.T) {
bundle := setupTestBundle(t)
tests := []struct {
lang string
count int
expected string
}{
{"en", 0, "No items"},
{"en", 1, "1 item"},
{"en", 2, "2 items"},
{"ru", 1, "1 элемент"},
{"ru", 2, "2 элемента"},
{"ru", 5, "5 элементов"},
{"ru", 11, "11 элементов"}, // Délicat : 11 utilise "many", pas "one"
}
for _, tt := range tests {
t.Run(fmt.Sprintf("%s_%d", tt.lang, tt.count), func(t *testing.T) {
localizer := i18n.NewLocalizer(bundle, tt.lang)
got, err := localizer.Localize(&i18n.LocalizeConfig{
MessageID: "item_count",
PluralCount: tt.count,
TemplateData: map[string]interface{}{"Count": tt.count},
})
if err != nil {
t.Fatalf("erreur de localisation : %v", err)
}
if got != tt.expected {
t.Errorf("obtenu %q, attendu %q", got, tt.expected)
}
})
}
}
Pour des stratégies de tests complètes, voir les outils, stratégies et automatisation des tests i18n.
Internationalisez votre application avec better-i18n
better-i18n combine des traductions alimentées par l'IA, des workflows natifs git et une livraison CDN mondiale dans une plateforme centrée sur les développeurs. Arrêtez de gérer des feuilles de calcul et commencez à publier dans toutes les langues.
Commencer gratuitement → · Explorer les fonctionnalités · Lire la documentation