Skip to main content

Panduan Lengkap Internasionalisasi Go

Dari file pesan hingga resolusi locale yang aman untuk goroutine: siapkan i18n dalam aplikasi Go Anda dengan go-i18n, lalu otomatiskan terjemahan dengan AI.

1

Instal go-i18n

go-i18n adalah pustaka internasionalisasi paling populer untuk Go. Pustaka ini menggunakan aturan bentuk jamak CLDR, template Go untuk interpolasi variabel, serta mendukung file pesan JSON, TOML, dan YAML. Anda juga memerlukan golang.org/x/text untuk pencocokan tag bahasa.

go-i18n v2 memerlukan Go 1.16+. Paket golang.org/x/text menyediakan penguraian dan pencocokan tag bahasa BCP 47, yang digunakan secara internal oleh go-i18n untuk memilih aturan bentuk jamak.
Terminal
go get -u github.com/nicksnyder/go-i18n/v2/i18n
go get -u golang.org/x/text/language
2

Buat File Pesan

Buat satu file JSON per bahasa dalam direktori locales. Setiap pesan memiliki ID dan satu atau beberapa bentuk jamak. Go-i18n menggunakan sintaks template Go (kurung kurawal ganda dengan awalan titik) untuk interpolasi variabel.

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."
  }
}
Gunakan ID pesan deskriptif seperti 'ItemCount' atau 'WelcomeBack', bukan jalur yang dipisahkan titik. go-i18n menggunakan ID datar, bukan kunci bersarang. Gunakan PascalCase pada ID agar sesuai dengan konvensi Go.
3

Muat Bundle

Bundle adalah registri pusat go-i18n. Buat satu saat startup, daftarkan format file Anda, dan muat semua file pesan. Bundle aman untuk goroutine — buat satu kali dan gunakan bersama di seluruh aplikasi Anda.

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) // "こんにちは、世界!"
}
Jika Anda melihat 'message not found', periksa tiga hal: 1) File pesan telah dimuat dengan LoadMessageFile atau MustLoadMessageFile. 2) Ekstensi file cocok dengan fungsi unmarshal yang terdaftar. 3) MessageID dalam LocalizeConfig sama persis dengan kunci dalam file JSON Anda (peka huruf besar-kecil).
4

Gunakan Localizer

Buat Localizer untuk setiap permintaan dengan bahasa pilihan pengguna. Localizer memilih pesan dari Bundle, menangani rendering template dengan engine text/template Go, dan memilih bentuk jamak yang benar berdasarkan 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 menerima beberapa string bahasa — fungsi ini mencobanya secara berurutan. Teruskan header Accept-Language secara langsung: i18n.NewLocalizer(bundle, r.Header.Get("Accept-Language")). go-i18n mengurai header dan secara otomatis mencocokkannya dengan terjemahan yang tersedia.
5

Tangani Aturan Bentuk Jamak

go-i18n mengimplementasikan aturan bentuk jamak CLDR untuk semua bahasa. Bahasa Inggris memiliki 2 bentuk (satu, lainnya). Bahasa Arab memiliki 6 (nol, satu, dua, sedikit, banyak, lainnya). Bahasa Jepang memiliki 1 (lainnya). Tentukan semua bentuk yang diperlukan dalam file pesan Anda — go-i18n memilih bentuk yang benar berdasarkan 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 dan TemplateData terpisah. PluralCount memilih bentuk jamak, sedangkan TemplateData menyediakan nilai untuk rendering template. Jika Anda memerlukan jumlah dalam teks pesan, teruskan ke keduanya: PluralCount: n dan TemplateData: map[string]interface{'}'{"Count": n}.
6

Deteksi Locale

Dalam aplikasi web, deteksi bahasa pilihan pengguna dari beberapa sumber: parameter kueri, cookie, header Accept-Language, atau segmen jalur URL. Gunakan golang.org/x/text/language.Matcher untuk negosiasi bahasa yang sesuai dengan 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 mengembalikan kecocokan terbaik dari bahasa yang Anda dukung, bukan preferensi mentah pengguna. Jika pengguna meminta 'pt-BR' dan Anda hanya mendukung 'pt', matcher akan mengembalikan 'pt' dengan benar. Tanpa matcher, Anda memerlukan logika fallback manual untuk setiap varian regional.
7

Perbaiki Fallback Per Kunci dengan go-locale-chain

go-i18n memiliki keterbatasan yang diketahui: setelah suatu locale cocok (memiliki terjemahan yang dimuat), kunci yang tidak tersedia tidak beralih ke locale berikutnya dalam rantai. Pengguna pt-BR dengan file pt-BR yang baru diterjemahkan sebagian akan mendapatkan string kosong alih-alih fallback ke pt-PT atau pt. go-locale-chain memperbaikinya dengan resolusi fallback per kunci di 75 rantai locale bawaan.

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 adalah paket Go sumber terbuka tanpa dependensi eksternal. Paket ini melengkapi go-i18n — gunakan go-i18n untuk memuat pesan, menangani bentuk jamak, dan merender template, serta go-locale-chain untuk resolusi rantai fallback yang tepat.
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
Gunakan ConfigureWithOverrides() untuk menyesuaikan rantai tertentu sambil mempertahankan default. Misalnya, sederhanakan pt-BR agar hanya melakukan fallback ke pt, atau tambahkan rantai untuk locale yang tidak ada dalam default seperti sv-FI -> sv.
8

Otomatiskan Penerjemahan

Setelah penyiapan i18n selesai, terjemahkan file locale Anda menggunakan AI. Hasilkan terjemahan untuk semua bahasa target dari file sumber bahasa Inggris — langsung dari IDE atau dalam pipeline CI/CD Anda.

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
Terjemahkan secara bertahap — ketika Anda menambahkan ID pesan baru ke file sumber, terjemahkan hanya kunci baru alih-alih membuat ulang semua file. Cara ini mempertahankan terjemahan yang telah ditinjau manusia.

Otomatiskan Kualitas Terjemahan

Temukan kunci hilang dan placeholder rusak sebelum dirilis dengan i18n-validate. Uji UI dengan terjemahan semu menggunakan i18n-pseudo sebelum terjemahan asli tersedia.

Kesalahan Umum

PluralCount dan TemplateData Tidak Cocok

PluralCount memilih bentuk jamak, tetapi tidak menyuntikkan nilainya ke template. Anda juga harus meneruskan jumlah dalam TemplateData agar muncul dalam pesan yang dirender. Tanpa TemplateData, '{'{.Count}'}' dirender sebagai '<no value>'.

RegisterUnmarshalFunc Tidak Tersedia

LoadMessageFile diam-diam tidak mengembalikan pesan jika Anda lupa memanggil bundle.RegisterUnmarshalFunc() untuk format file tersebut. Selalu daftarkan json.Unmarshal (atau toml/yaml) sebelum memuat file.

Fallback Per Kunci Tidak Berfungsi

NewLocalizer milik go-i18n menerima beberapa bahasa, tetapi setelah suatu locale cocok dengan satu kunci, kunci yang tidak tersedia akan mengembalikan string kosong alih-alih melakukan fallback. Gunakan go-locale-chain untuk memperbaikinya dengan kaskade per kunci yang tepat.

Sintaks Template: {'{.Var}'}, Bukan {'{Var}'}

go-i18n menggunakan sintaks text/template Go. Variabel harus diawali titik: {'{.Name}'}, bukan {'{Name}'}. Titik tersebut merujuk pada peta TemplateData. Tanpa titik, terjadi kesalahan eksekusi template.

Struktur File yang Disarankan

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

Coba i18n Agent Sekarang

Lepaskan file terjemahan Anda di sini

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

atau klik untuk menjelajahi

Bahasa target

Tidak perlu mendaftarEstimasi instan

Fallback Locale dengan go-locale-chain

Ketika kunci terjemahan tidak tersedia dalam locale regional seperti pt-BR, go-i18n langsung beralih ke bahasa default alih-alih memeriksa locale induk pt terlebih dahulu.

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"},
    },
})

Lihat Panduan Fallback Bahasa kami untuk daftar lengkap framework yang didukung dan 75 rantai bawaan. Learn more →

Tanya Jawab Go i18n