Inhaltsverzeichnis
Go (Golang) i18n: Internationalisierungsmuster und Bibliotheken
Go ist eine zunehmend beliebte Sprache für Backend-Dienste, CLIs und APIs, die globale Nutzer bedienen. Obwohl die Standardbibliothek von Go kein so umfassendes i18n-Framework wie andere Ökosysteme enthält, ermöglicht die Kombination aus golang.org/x/text, go-i18n und einer durchdachten Architektur robuste mehrsprachige Go-Anwendungen.
Dieses Leitfaden behandelt das vollständige Go-i18n-Toolkit – von der gebietsschemabewussten Formatierung mit x/text bis zur Übersetzungsverwaltung mit go-i18n –, einschließlich Muster zur Strukturierung von i18n in Go-Diensten und CLIs.
Überblick über Gos i18n-Ökosystem
Gos i18n-Tooling ist auf mehrere Pakete aufgeteilt:
| Paket | Zweck |
|---|---|
golang.org/x/text/language | BCP 47-Sprach-Tag-Parsing und -Matching |
golang.org/x/text/message | Printf-artige formatierte Nachrichten mit i18n |
golang.org/x/text/number | Zahlenformatierung (Plurale, Währung) |
golang.org/x/text/collate | Gebietsschemabewusstes Sortieren von Zeichenketten |
golang.org/x/text/unicode/norm | Unicode-Normalisierung |
github.com/nicksnyder/go-i18n/v2 | Verwaltung von Übersetzungsdateien, Pluralisierung |
github.com/BurntSushi/toml | TOML-Parsing (gängiges Übersetzungsformat) |
Sprach-Tag-Parsing mit golang.org/x/text
Das Paket golang.org/x/text/language implementiert BCP 47-Sprach-Tags – den Standard für Gebietsschema-Bezeichner, der in HTTP-Accept-Language-Headern, HTML-lang-Attributen und Gebietsschema-Zeichenketten verwendet wird.
Sprach-Tags parsen und abgleichen
package main
import (
"fmt"
"golang.org/x/text/language"
)
func main() {
// Parse a language tag
tag, err := language.Parse("zh-Hant-TW")
if err != nil {
panic(err)
}
fmt.Println(tag) // zh-Hant-TW
// Language matching: find the best match from supported languages
supported := []language.Tag{
language.English,
language.French,
language.SimplifiedChinese,
language.TraditionalChinese,
}
matcher := language.NewMatcher(supported)
// User requests zh-TW (Traditional Chinese, Taiwan)
t, _, _ := matcher.Match(language.MustParse("zh-TW"))
fmt.Println(t) // zh-Hant → matches Traditional Chinese
// User requests es-MX (Mexican Spanish)
t, _, _ = matcher.Match(language.MustParse("es-MX"))
fmt.Println(t) // en → falls back to English (Spanish not supported)
}
Accept-Language-Header parsen
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 {
// Parse Accept-Language header
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: Übersetzungsverwaltung
go-i18n ist die am weitesten verbreitete Go-Bibliothek zur Übersetzungsverwaltung. Sie unterstützt:
- JSON-, TOML- und YAML-Übersetzungsdateien
- ICU-Nachrichtenformat für die Pluralisierung
- Template-Interpolation
- Laden mehrerer Übersetzungsdateien
Installation und Einrichtung
go get github.com/nicksnyder/go-i18n/v2@latest go get github.com/BurntSushi/toml
Struktur der Übersetzungsdatei
# locales/en.toml
[welcome]
description = "Welcome message for new users"
one = "Welcome, {{.Name}}! You have {{.Count}} new notification."
other = "Welcome, {{.Name}}! You have {{.Count}} new notifications."
[item_count]
description = "Number of items in a list"
zero = "No items"
one = "{{.Count}} item"
other = "{{.Count}} items"
# 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"
go-i18n initialisieren
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)
// Load all locale files
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("loading locale %s: %w", entry.Name(), err)
}
}
return nil
}
// NewLocalizer creates a localizer for the given language tags
func NewLocalizer(langs ...string) *i18n.Localizer {
return i18n.NewLocalizer(bundle, langs...)
}
Nachrichten übersetzen
package handlers
import (
"net/http"
"myapp/i18n"
)
type WelcomeData struct {
Name string
Count int
}
func WelcomeHandler(w http.ResponseWriter, r *http.Request) {
// Get localizer for request language
accept := r.Header.Get("Accept-Language")
localizer := i18n.NewLocalizer(accept, "en")
// Simple message with plural handling
itemCount := 3
msg, err := localizer.Localize(&i18n.LocalizeConfig{
MessageID: "item_count",
PluralCount: itemCount,
TemplateData: map[string]interface{}{
"Count": itemCount,
},
})
if err != nil {
msg = "Unknown number of items" // fallback
}
// Message with name and plural
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)
}
Zahlen- und Währungsformatierung
Das Paket golang.org/x/text/message bietet gebietsschemabewusste Zahlenformatierung:
package main
import (
"fmt"
"golang.org/x/text/language"
"golang.org/x/text/message"
"golang.org/x/text/number"
)
func main() {
// Create locale-specific printers
enPrinter := message.NewPrinter(language.English)
dePrinter := message.NewPrinter(language.German)
jaPrinter := message.NewPrinter(language.Japanese)
amount := 1234567.89
// Locale-aware number formatting
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
// Percentage formatting
enPrinter.Printf("%.2%\n", number.Percent(0.8527))
// 85.27%
dePrinter.Printf("%.2%\n", number.Percent(0.8527))
// 85,27 %
}
Datums- und Uhrzeitformatierung
Gos time-Paket übernimmt die grundlegende Datumsformatierung, für gebietsschemabewusste Datumsformatierung benötigt man jedoch x/text oder eine Drittanbieterbibliothek:
import (
"time"
"golang.org/x/text/language"
"golang.org/x/text/language/display"
)
func formatDateLocale(t time.Time, locale language.Tag) string {
// Go's built-in time formatting uses fixed layout strings
// For locale-specific formatting, use strftime-equivalent libraries
// or generate patterns from CLDR data
switch locale {
case language.English:
return t.Format("January 2, 2006")
case language.German:
return t.Format("2. January 2006") // German date format
case language.Japanese:
return t.Format("2006年01月02日")
default:
return t.Format(time.RFC3339)
}
}
Für produktionsreife gebietsschemabewusste Datumsformatierung empfiehlt sich eine Bibliothek wie github.com/goodsign/monday für Tages- und Monatsnamen.
i18n in einem Go-Webdienst strukturieren
Middleware-basierte Gebietsschema-Erkennung
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) {
// Check URL query parameter first (e.g., ?lang=fr)
lang := r.URL.Query().Get("lang")
// Then check cookie
if lang == "" {
if cookie, err := r.Cookie("lang"); err == nil {
lang = cookie.Value
}
}
// Fall back to Accept-Language header
if lang == "" {
lang = r.Header.Get("Accept-Language")
}
// Create localizer and attach to context
localizer := i18n.NewLocalizer(lang, "en")
ctx := context.WithValue(r.Context(), localizerKey, localizer)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// GetLocalizer retrieves the localizer from the context
func GetLocalizer(ctx context.Context) *i18n.Localizer {
l, ok := ctx.Value(localizerKey).(*i18n.Localizer)
if !ok {
return i18n.NewLocalizer("en")
}
return l
}
Fehlermeldungen als übersetzbarer Inhalt
package errors
import (
"github.com/nicksnyder/go-i18n/v2/i18n"
)
// AppError is an error with a translatable message
type AppError struct {
MessageID string
TemplateData interface{}
Err error
}
func (e *AppError) Error() string {
return e.MessageID // Internal identifier
}
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 // Fallback to key
}
return msg
}
// Usage
var ErrNotFound = &AppError{MessageID: "error.not_found"}
var ErrUnauthorized = &AppError{MessageID: "error.unauthorized"}
i18n für Go-CLI-Anwendungen
CLI-Anwendungen haben besondere i18n-Anforderungen: Sie müssen das Systemgebietsschema erkennen, die Ausgabe entsprechend formatieren und die Terminal-Codierung berücksichtigen.
package main
import (
"os"
"golang.org/x/text/language"
"myapp/i18n"
)
func detectSystemLocale() string {
// Check LANG environment variable (Unix/Linux/macOS)
if lang := os.Getenv("LANG"); lang != "" {
// LANG is typically "en_US.UTF-8" format
// Extract language tag
parts := strings.Split(lang, ".")
if len(parts) > 0 {
// Convert "en_US" to "en-US" (BCP 47)
return strings.ReplaceAll(parts[0], "_", "-")
}
}
// Check 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" // Default to English
}
func main() {
locale := detectSystemLocale()
localizer := i18n.NewLocalizer(locale, "en")
// Use localizer for all output
fmt.Println(localizer.MustLocalize(&i18n.LocalizeConfig{
MessageID: "cli.welcome",
}))
}
Extraktions- und Verwaltungswerkzeuge
go-i18n stellt ein goi18n-Kommandozeilenwerkzeug zur Extraktion übersetzbarer Zeichenketten bereit:
# Install the CLI tool go install github.com/nicksnyder/go-i18n/v2/goi18n@latest # Extract strings from Go source (creates active.en.toml) goi18n extract -format toml -outdir locales ./... # Merge new strings into existing translations # (creates translate.fr.toml with only new/changed strings) goi18n merge -format toml -outdir locales locales/active.en.toml locales/active.fr.toml # After translation, merge translate.fr.toml back into active.fr.toml goi18n merge -format toml -outdir locales locales/active.fr.toml locales/translate.fr.toml
Dieser Arbeitsablauf lässt sich gut mit der CI/CD-Lokalisierungsautomatisierung für kontinuierliche Übersetzungsaktualisierungen kombinieren.
Pluralisierung für komplexe Sprachen
go-i18n verarbeitet die Pluralisierung automatisch über CLDR-Regeln für alle unterstützten Sprachen:
# locales/ru.toml (Russian - 4 plural forms)
[item_count]
zero = "Нет элементов"
one = "{{.Count}} элемент" # 1, 21, 31...
few = "{{.Count}} элемента" # 2-4, 22-24...
many = "{{.Count}} элементов" # 5-20, 25-30...
other = "{{.Count}} элемента" # fractions, other
# locales/ar.toml (Arabic - 6 plural forms)
[item_count]
zero = "لا عناصر"
one = "{{.Count}} عنصر"
two = "{{.Count}} عنصران"
few = "{{.Count}} عناصر"
many = "{{.Count}} عنصرًا"
other = "{{.Count}} عنصر"
Eine ausführlichere Behandlung der Pluralisierungsregeln finden Sie unter Pluralisierungsregeln in verschiedenen Sprachen.
i18n in Go testen
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 элементов"}, // Tricky: 11 uses "many", not "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("localize error: %v", err)
}
if got != tt.expected {
t.Errorf("got %q, want %q", got, tt.expected)
}
})
}
}
Umfassende Teststrategien finden Sie unter i18n-Testwerkzeuge, -strategien und -automatisierung.
Machen Sie Ihre App global mit better-i18n
better-i18n kombiniert KI-gestützte Übersetzungen, Git-native Workflows und globale CDN-Auslieferung in einer entwicklerfreundlichen Plattform. Schluss mit der Verwaltung von Tabellenkalkulationen – fangen Sie an, in jeder Sprache zu veröffentlichen.
Kostenlos starten → · Funktionen erkunden · Dokumentation lesen