İçindekiler
Go (Golang) i18n: Uluslararasılaştırma Kalıpları ve Kütüphaneleri
Go, küresel kullanıcılara hizmet veren backend servisleri, CLI'lar ve API'lar için giderek daha popüler hale gelen bir dildir. Go'nun standart kütüphanesi, bazı diğer ekosistemler kadar kapsamlı bir i18n çerçevesi içermese de golang.org/x/text, go-i18n ve dikkatli mimari bir arada kullanıldığında sağlam çok dilli Go uygulamaları geliştirmek mümkündür.
Bu kılavuz, x/text ile yerel ayara duyarlı biçimlendirmeden go-i18n ile çeviri yönetimine kadar eksiksiz Go i18n araç setini ele almakta; Go servisleri ve CLI'larında i18n yapılandırma kalıplarını da kapsamaktadır.
Go'nun i18n Ekosistemine Genel Bakış
Go'nun i18n araçları birkaç paket arasında bölünmüştür:
| Paket | Amaç |
|---|---|
golang.org/x/text/language | BCP 47 dil etiketi ayrıştırma ve eşleştirme |
golang.org/x/text/message | i18n destekli Printf tarzı biçimlendirilmiş mesajlar |
golang.org/x/text/number | Sayı biçimlendirme (çoğul, para birimi) |
golang.org/x/text/collate | Yerel ayara duyarlı dize sıralama |
golang.org/x/text/unicode/norm | Unicode normalleştirme |
github.com/nicksnyder/go-i18n/v2 | Çeviri dosyası yönetimi, çoğullama |
github.com/BurntSushi/toml | TOML ayrıştırma (yaygın çeviri formatı) |
golang.org/x/text ile Dil Etiketi Ayrıştırma
golang.org/x/text/language paketi, HTTP Accept-Language başlıklarında, HTML lang niteliklerinde ve yerel ayar dizelerinde kullanılan yerel ayar tanımlayıcılarının standardı olan BCP 47 dil etiketlerini uygular.
Dil Etiketlerini Ayrıştırma ve Eşleştirme
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 Başlıklarını Ayrıştırma
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: Çeviri Yönetimi
go-i18n, çeviri yönetimi için en yaygın kullanılan Go kütüphanesidir. Şunları destekler:
- JSON, TOML ve YAML çeviri dosyaları
- Çoğullama için ICU mesaj formatı
- Şablon enterpolasyonu
- Birden fazla çeviri dosyası yükleme
Kurulum ve Yapılandırma
go get github.com/nicksnyder/go-i18n/v2@latest go get github.com/BurntSushi/toml
Çeviri Dosyası Yapısı
# 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'i Başlatma
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...)
}
Mesajları Çevirme
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)
}
Sayı ve Para Birimi Biçimlendirme
golang.org/x/text/message paketi yerel ayara duyarlı sayı biçimlendirme sağlar:
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 %
}
Tarih ve Saat Biçimlendirme
Go'nun time paketi temel tarih biçimlendirmeyi destekler; ancak yerel ayara duyarlı tarih biçimlendirme için x/text veya üçüncü taraf bir kütüphane gereklidir:
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)
}
}
Prodüksiyon kalitesinde yerel ayara duyarlı tarih biçimlendirmesi için gün/ay adları amacıyla github.com/goodsign/monday gibi bir kütüphane kullanın.
Bir Go Web Servisinde i18n Yapılandırma
Middleware Tabanlı Yerel Ayar Tespiti
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
}
Çevrilebilir İçerik Olarak Hata Mesajları
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"}
Go CLI Uygulamalarında i18n
CLI uygulamalarının i18n gereksinimleri farklıdır: sistem yerel ayarını tespit etmeli, çıktıyı uygun şekilde biçimlendirmeli ve terminal kodlamasını ele almalıdır.
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",
}))
}
Çıkarım ve Yönetim Araçları
go-i18n, çevrilebilir dizeleri ayıklamak için goi18n komut satırı aracı sunar:
# 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
Bu iş akışı, sürekli çeviri güncellemeleri için CI/CD yerelleştirme otomasyonu ile uyumlu biçimde entegre olur.
Karmaşık Diller için Çoğullama
Go-i18n, desteklenen tüm diller için CLDR kuralları aracılığıyla çoğullamayı otomatik olarak yönetir:
# 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}} عنصر"
Çoğullama kurallarının daha kapsamlı ele alınması için bkz. diller arası çoğullama kuralları.
Go'da i18n Testi
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)
}
})
}
}
Kapsamlı test stratejileri için bkz. i18n test araçları, stratejileri ve otomasyonu.
Uygulamanızı better-i18n ile küreselleştirin
better-i18n; yapay zeka destekli çeviriler, git-native iş akışları ve küresel CDN teslimatını geliştirici odaklı tek bir platformda bir araya getirir. Elektronik tablolarla uğraşmayı bırakın ve her dilde ürün çıkarmaya başlayın.
Ücretsiz başlayın → · Özellikleri keşfedin · Belgeleri okuyun