i18n.go 2.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
package i18n

import (
	"archive/zip"
	"bytes"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/chai2010/gettext-go/gettext"
)

var knownTranslations = map[string][]string{
	"jcli": {
		"default",
		"en_US",
		"zh_CN",
	},
	// only used for unit tests.
	"test": {
		"default",
		"en_US",
	},
}

func loadSystemLanguage() string {
	// Implements the following locale priority order: LC_ALL, LC_MESSAGES, LANG
	// Similarly to: https://www.gnu.org/software/gettext/manual/html_node/Locale-Environment-Variables.html
	langStr := os.Getenv("LC_ALL")
	if langStr == "" {
		langStr = os.Getenv("LC_MESSAGES")
	}
	if langStr == "" {
		langStr = os.Getenv("LANG")
	}
	if langStr == "" {
		return "default"
	}
	pieces := strings.Split(langStr, ".")
	if len(pieces) != 2 {
		return "default"
	}
	return pieces[0]
}

func findLanguage(root string, getLanguageFn func() string) string {
	langStr := getLanguageFn()

	translations := knownTranslations[root]
	for ix := range translations {
		if translations[ix] == langStr {
			return langStr
		}
	}
	return "default"
}

// LoadTranslations loads translation files. getLanguageFn should return a language
// string (e.g. 'en-US'). If getLanguageFn is nil, then the loadSystemLanguage function
// is used, which uses the 'LANG' environment variable.
func LoadTranslations(root string, getLanguageFn func() string) (err error) {
	if getLanguageFn == nil {
		getLanguageFn = loadSystemLanguage
	}

	langStr := findLanguage(root, getLanguageFn)
	translationFiles := []string{
		//"jcli/zh_CN/LC_MESSAGES/jcli.mo",
		"jcli/zh_CN/LC_MESSAGES/jcli.po",
	}

	buf := new(bytes.Buffer)
	w := zip.NewWriter(buf)

	// Make sure to check the error on Close.
	var f io.Writer
	var data []byte
	for _, file := range translationFiles {
		filename := file
		if f, err = w.Create(file); err != nil {
			return
		}

		if data, err = Asset(filename); err != nil {
			return
		}

		if _, err = f.Write(data); err != nil {
			return
		}
	}

	if err = w.Close(); err == nil {
		gettext.BindTextdomain("jcli", root+".zip", buf.Bytes())
		gettext.Textdomain("jcli")
		gettext.SetLocale(langStr)
	}
	return
}

var i18nLoaded = false

// T translates a string, possibly substituting arguments into it along
// the way. If len(args) is > 0, args1 is assumed to be the plural value
// and plural translation is used.
func T(defaultValue string, args ...int) string {
	if !i18nLoaded {
		i18nLoaded = true
		if err := LoadTranslations("jcli", nil); err != nil {
			fmt.Println(err)
		}
	}

	if len(args) == 0 {
		return gettext.PGettext("", defaultValue)
	}
	return fmt.Sprintf(gettext.PNGettext("", defaultValue, defaultValue+".plural", args[0]),
		args[0])
}