Skip to content
/ i18n Public

🆕 High-performant and powerful localization and internationalization support for Go

License

Notifications You must be signed in to change notification settings

kataras/i18n

Repository files navigation

i18n (Go)

build status report card godocs donate on Stripe

Efficient and easy to use localization and internationalization support for Go.

Installation

The only requirement is the Go Programming Language.

$ go get github.com/kataras/i18n@latest

Examples

Getting started

Create a folder named ./locales and put some YAML, TOML, JSON or INI files.

│   main.go
└───locales
    ├───el-GR
    │       example.yml
    ├───en-US
    │       example.yml
    └───zh-CN
            example.yml

Now, put the key-values content for each locale, e.g. ./locales/en-US/example.yml

hi: "Hi %s"
#
# Templates are supported
# hi: "Hi {{ .Name }}
#
# Template functions are supported
# hi: "Hi {{sayHi .Name}}
# ./locales/el-GR/example.yaml
hi: "Γειά σου %s"
# ./locales/zh-CN/example.yaml
hi: 您好 %s

Some other possible filename formats...

  • page.en.yaml
  • home.cart.el-GR.json
  • /el/file.tml

The language code MUST be right before the file extension.

The Default I18n instance will try to load locale files from ./locales directory. Use the Tr package-level function to translate a text based on the given language code. Use the GetMessage function to translate a text based on the incoming http.Request. Use the Router function to wrap an http.Handler (i.e an http.ServeMux) to set the language based on path prefix such as /zh-CN/some-path and subdomains such as zh.domain.com without the requirement of different routes per language.

Let's take a look at the simplest usage of this package.

package main

import (
	"fmt"

	"github.com/kataras/i18n"
)

type user struct {
	Name string
	Age  int
}

func main() {
	// i18n.SetDefaultLanguage("en-US")

	// Fmt style.
	enText := i18n.Tr("en", "hi", "John Doe") // or "en-US"
	elText := i18n.Tr("el", "hi", "John Doe")
	zhText := i18n.Tr("zh", "hi", "John Doe")

	fmt.Println(enText)
	fmt.Println(elText)
	fmt.Println(zhText)

	// Templates style.
	templateData := user{
		Name: "John Doe",
		Age:  66,
	}

	enText = i18n.Tr("en-US", "intro", templateData) // or "en"
	elText = i18n.Tr("el-GR", "intro", templateData)
	zhText = i18n.Tr("zh-CN", "intro", templateData)

	fmt.Println(enText)
	fmt.Println(elText)
	fmt.Println(zhText)
}

Load specific languages over a new I18n instance. The default language is the first registered, in that case is the "en-US".

I18n, err := i18n.New(i18n.Glob("./locales/*/*"), "en-US", "el-GR", "zh-CN")

Load embedded files through a go-bindata package:

I18n, err := i18n.New(i18n.Assets(AssetNames, Asset), "en-US", "el-GR", "zh-CN")

Load embedded files through Go's embed directive (recommended):

//go:embed static/locales/*
var staticFS embed.FS

loader, err := i18n.FS(staticFS, "./static/locales/*/*.yml")
// [handle error...]
I18n, err := i18n.New(loader, "en-US", "el-GR", "zh-CN")

Load through a simple Go map:

m := i18n.LangMap{
    "en-US": i18n.Map{
        "buy":               `buy %d`,
        "cart.checkout":     `checkout - {{.Param}}`,
        "cart.after.thanks": `thanks`,
        //
        "JSONTemplateExample":  `value of {{.Value}}`,
        "TypeOf":               `type of %T`,
        "KeyOnlyOnDefaultLang": `value`,
        //
        "title": `Title`,
        "hi":    `Hi {{.Name}}`,
        "int":   `1`,
        "hello": `Hello %s`,
        //
        "welcome": `welcome`,
    },
    "el-GR": i18n.Map{
        "buy":               `αγοράστε %d`,
        "cart.checkout":     `ολοκλήρωση παραγγελίας - {{.Param}}`,
        "cart.after.thanks": `ευχαριστούμε`,
        //
        "JSONTemplateExample": `τιμή του {{.Value}}`,
        "TypeOf":              `τύπος %T`,
        //
        "title": `Τίτλος`,
        "hi":    `Γειά σου {{.Name}}`,
        "int":   `1`,
        //
        "welcome": `καλώς ήρθατε`,
    },
}

loader := i18n.KV(m)

i18N, err := i18n.New(loader, "en-US", "el-GR")

Template variables & functions

Using template variables & functions as values in your locale value entry via LoaderConfig.

We are going to use a 3rd-party package for plural and singular words. Note that this is only for english dictionary, but you can use the "current" Locale and make a map with dictionaries to pluralize words based on the given language.

Before we get started, install the necessary packages:

$ go get github.com/kataras/i18n@master
$ go get github.com/gertd/go-pluralize@master

Let's create two simple translation files for our example. The ./locales/en-US/welcome.yml and ./locales/el-GR/welcome.yml respectfully:

Dog: "dog"
HiDogs: Hi {{plural (tr "Dog") .count }}
Dog: "σκυλί"
HiDogs: Γειά {{plural (tr "Dog") .count }}

The tr template function is a builtin function registered per locale. It returns the key's translated value. E.g. on english file the tr "Dog" returns the Dog:'s value: "dog" and on greek file it returns "σκυλί". This function helps importing a key to another key to complete a sentence.

Now, create a main.go file and store the following contents:

package main

import (
    "fmt"
    "text/template"

    "github.com/kataras/i18n"
    "github.com/gertd/go-pluralize"
)

var pluralizeClient = pluralize.NewClient()

func getFuncs(current *i18n.Locale) template.FuncMap {
    return template.FuncMap{
        "plural": func(word string, count int) string {
            return pluralizeClient.Pluralize(word, count, true)
        },
    }
}

func main() {
    I18n, err := i18n.New(i18n.Glob("./locales/*/*", i18n.LoaderConfig{
        // Set custom functions per locale!
        Funcs: getFuncs,
    }), "en-US", "el-GR", "zh-CN")
    if err != nil {
        panic(err)
    }

    textEnglish := I18n.Tr("en", "HiDogs", map[string]interface{}{
        "count": 2,
    }) // prints "Hi 2 dogs".
    fmt.Println(textEnglish)

    textEnglishSingular := I18n.Tr("en", "HiDogs", map[string]interface{}{
        "count": 1,
    }) // prints "Hi 1 dog".
    fmt.Println(textEnglishSingular)

    textGreek := I18n.Tr("el", "HiDogs", map[string]interface{}{
        "count": 1,
    }) // prints "Γειά 1 σκυλί".
    fmt.Println(textGreek)
}

Use go run main.go to run our small Go program. The output should look like this:

Hi 2 dogs
Hi 1 dog
Γειά 1 σκυλί

HTTP

HTTP, automatically searches for url parameter, cookie, custom function and headers for the current user language.

mux := http.NewServeMux()

I18n.URLParameter = "lang" // i.e https://domain.com?lang=el
I18n.Cookie = "lang"
I18n.ExtractFunc = func(r *http.Request) string { /* custom logic */ }

mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    translated := I18n.GetMessage(r, "hi", "John Doe")
    fmt.Fprintf(w, "Text: %s", translated)
})

Prefer GetLocale if more than one GetMessage call.

mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    locale := I18n.GetLocale(r)
    translated := locale.GetMessage("hi", "John Doe")
    // [...some locale.GetMessage calls]
})

Optionally, identify the current language by subdomain or path prefix, e.g. en.domain.com and domain.com/en or domain.com/en-US and e.t.c.

I18n.Subdomain = true

http.ListenAndServe(":8080", I18n.Router(mux))

If the ContextKey field is not empty then the Router will set the current language.

I18n.ContextKey = "lang" 

mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    currentLang := r.Context().Value("lang").(string)
    fmt.Fprintf(w, "Language: %s", currentLang)
})

Set the translate function as a key on a HTML Template.

templates, _ := template.ParseGlob("./templates/*.html")

mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    // per-request.
    translateFunc := I18n.GetLocale(r).GetMessage

    templates.ExecuteTemplate(w, "index.html", map[string]interface{}{
        "tr": translateFunc,
    })

    // {{ call .tr "hi" "John Doe" }}
})

Global function with the language as its first input argument.

translateLangFunc := I18n.Tr
templates.Funcs(template.FuncMap{
    "tr": translateLangFunc,
})

// {{ tr "en" "hi" "John Doe" }}

For a more detailed technical documentation you can head over to our godocs. And for executable code you can always visit the _examples repository's subdirectory.

License

kataras/i18n is free and open-source software licensed under the MIT License.