Skip to main content

Комплетан водич за Go интернационализацију

Од датотека порука до разрешавања локала безбедног за goroutine: подесите i18n у Go апликацији помоћу go-i18n, а затим аутоматизујте преводе помоћу AI технологије.

1

Инсталирајте go-i18n

go-i18n је најпопуларнија библиотека за Go интернационализацију. Користи CLDR правила множине, Go шаблоне за интерполацију променљивих и подржава JSON, TOML и YAML датотеке порука. Потребан Вам је и golang.org/x/text за подударање језичких ознака.

go-i18n v2 захтева Go 1.16+. Пакет golang.org/x/text пружа BCP 47 рашчлањивање и подударање језичких ознака, које go-i18n интерно користи за избор правила множине.
Terminal
go get -u github.com/nicksnyder/go-i18n/v2/i18n
go get -u golang.org/x/text/language
2

Направите датотеке порука

Направите по једну JSON датотеку за сваки језик у директоријуму locales. Свака порука има ID и један или више облика множине. go-i18n користи синтаксу Go шаблона (двоструке витичасте заграде са префиксом тачке) за интерполацију променљивих.

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."
  }
}
Користите описне ID ознаке порука као што су 'ItemCount' или 'WelcomeBack' уместо путања раздвојених тачкама. go-i18n користи равне ID ознаке, а не угнежђене кључеве. Задржите PascalCase у ID ознакама да пратите Go конвенције.
3

Учитајте Bundle

Bundle је централни регистар библиотеке go-i18n. Направите га једном при покретању, региструјте формат датотеке и учитајте све датотеке порука. Bundle је безбедан за goroutine — направите га једном и делите у целој апликацији.

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) // "こんにちは、世界!"
}
Ако видите 'message not found', проверите три ствари: 1) датотека порука је учитана помоћу LoadMessageFile или MustLoadMessageFile; 2) наставак датотеке одговара регистрованој функцији за рашчлањивање; 3) MessageID у LocalizeConfig потпуно одговара кључу у JSON датотеци (уз разликовање великих и малих слова).
4

Користите Localizer

Направите Localizer за сваки захтев са жељеним језиком корисника. Localizer разрешава поруке из Bundle објекта, обрађује приказивање шаблона помоћу Go text/template покретача и бира исправан облик множине на основу 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 прихвата више језичких текстова — испробава их редом. Директно проследите Accept-Language заглавље: i18n.NewLocalizer(bundle, r.Header.Get("Accept-Language")). go-i18n рашчлањује заглавље и аутоматски га подудара са доступним преводима.
5

Обрадите правила множине

go-i18n имплементира CLDR правила множине за све језике. Енглески има 2 облика (one, other). Арапски има 6 (zero, one, two, few, many, other). Јапански има 1 (other). Дефинишите све потребне облике у датотекама порука — go-i18n бира исправан на основу 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 и TemplateData су одвојени. PluralCount бира облик множине, а TemplateData пружа вредности за приказивање шаблона. Ако Вам је број потребан у тексту поруке, проследите га у оба: PluralCount: n и TemplateData: map[string]interface{'}'{"Count": n}.
6

Откривање локала

У веб-апликацијама откријте жељени језик корисника из више извора: параметара упита, колачића, Accept-Language заглавља или сегмената URL путање. Користите golang.org/x/text/language.Matcher за избор језика усклађен са 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 враћа најбоље подударање међу подржаним језицима, а не необрађену жељену поставку корисника. Ако корисник затражи 'pt-BR', а подржавате само 'pt', matcher исправно враћа 'pt'. Без matcher објекта била би Вам потребна ручна логика резерве за сваку регионалну варијанту.
7

Исправите резерву по кључу помоћу go-locale-chain

go-i18n има познато ограничење: када се локал подудари (има било који учитани превод), недостајући кључеви не прелазе на следећи локал у ланцу. Корисник локала pt-BR са делимично преведеном pt-BR датотеком добија празне текстове уместо резерве за pt-PT или pt. go-locale-chain то исправља разрешавањем резерве по кључу кроз 75 уграђених ланаца локала.

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 је Go пакет отвореног кода без спољних зависности. Допуњује go-i18n — користите go-i18n за учитавање порука, множину и приказивање шаблона, а go-locale-chain за правилно разрешавање ланаца резерве.
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
Користите ConfigureWithOverrides() да прилагодите одређене ланце и задржите подразумеване вредности. На пример, поједноставите pt-BR тако да прелази само на pt или додајте ланац за локал који није у подразумеваним вредностима, као што је sv-FI -> sv.
8

Аутоматизујте преводе

Када подесите i18n систем, преведите датотеке локала помоћу AI технологије. Генеришите преводе за све циљне језике из енглеске изворне датотеке — директно из IDE окружења или у CI/CD pipeline систему.

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
Преводите постепено — када додате нове ID ознаке порука у изворну датотеку, преведите само нове кључеве уместо поновног генерисања свих датотека. Тиме се чувају преводи које су људи прегледали.

Аутоматизујте квалитет превода

Помоћу i18n-validate алатке откријте недостајуће кључеве и неисправне чуваре места пре испоруке. Тестирајте кориснички интерфејс псеудопреводима помоћу i18n-pseudo алатке пре него што стигну прави преводи.

Уобичајене замке

Неслагање PluralCount и TemplateData вредности

PluralCount бира облик множине, али не убацује вредност у шаблон. Морате и да проследите број у TemplateData да би се појавио у приказаној поруци. Без TemplateData, '{'{.Count}'}' се приказује као '<no value>'.

Недостаје RegisterUnmarshalFunc

LoadMessageFile неприметно не враћа ниједну поруку ако заборавите да позовете bundle.RegisterUnmarshalFunc() за формат датотеке. Увек региструјте json.Unmarshal (или toml/yaml) пре учитавања датотека.

Резерва по кључу не ради

go-i18n NewLocalizer прихвата више језика, али када се локал подудари са једним кључем, недостајући кључеви враћају празне текстове уместо преласка на резерву. Користите go-locale-chain да то исправите правилним надовезивањем по кључу.

Синтакса шаблона: {'{.Var}'}, а не {'{Var}'}

go-i18n користи Go text/template синтаксу. Променљиве морају да имају префикс тачке: {'{.Name}'}, а не {'{Name}'}. Тачка упућује на мапу TemplateData. Без тачке долази до грешке при извршавању шаблона.

Препоручена структура датотека

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

Испробајте i18n Agent сада

Пустите датотеку за превођење овде

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

или кликните за избор

Циљни језици

Регистрација није потребнаТренутна процена

Резервни локали уз go-locale-chain

Када кључ превода недостаје у регионалном локалу као што је pt-BR, go-i18n одмах прелази на подразумевани језик уместо да прво провери надређени локал 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"},
    },
})

Погледајте наш водич за резервне локале за целу листу подржаних система и 75 уграђених ланаца. Learn more →

Честа питања о Go i18n систему