localization.go 2.15 KB
package webutility

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"path"
	"strings"
	"sync"
)

type Dictionary struct {
	my            sync.Mutex
	locales       map[string]map[string]string
	supported     []string
	defaultLocale string
}

func NewDictionary() *Dictionary {
	return &Dictionary{
		locales: map[string]map[string]string{},
	}
}

func (d *Dictionary) AddTranslations(directory string) error {
	files, err := ioutil.ReadDir(directory)
	if err != nil {
		return err
	}

	for _, fileInfo := range files {
		fName := fileInfo.Name()
		path := directory + "/" + fName
		file, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}

		loc := stripFileExtension(fName)

		var data interface{}
		err = json.Unmarshal(file, &data)
		if err != nil {
			return err
		}

		l := map[string]string{}
		for k, v := range data.(map[string]interface{}) {
			l[k] = v.(string)
		}

		mu.Lock()
		defer mu.Unlock()
		d.locales[loc] = l
		d.supported = append(d.supported, loc)
	}

	if len(d.supported) > 0 {
		d.setDefaultLocale(d.supported[0])
	}

	return nil
}

func (d *Dictionary) GetBestMatchLocale(acceptedLanguages string) (best string) {
	accepted := d.parseAcceptedLanguageHeader(acceptedLanguages)
	best = accepted[0]
	return
}

func (d *Dictionary) Translate(loc, key string) string {
	return d.locales[loc][key]
}

func (d *Dictionary) hasLocale(loc string) bool {
	for _, v := range d.supported {
		if v == loc {
			return true
		}
	}
	return false
}

func (d *Dictionary) setDefaultLocale(loc string) error {
	if !d.hasLocale(loc) {
		return errors.New("dictionary does not contain translations for " + loc)
	}
	d.defaultLocale = loc
	return nil
}

func (d *Dictionary) getDefaultLocale() string {
	return d.defaultLocale
}

func (d *Dictionary) parseAcceptedLanguageHeader(acceptedLanguages string) (langs []string) {
	if acceptedLanguages == "" {
		langs = append(langs, d.getDefaultLocale())
		return
	}

	parts := strings.Split(acceptedLanguages, ",")
	for _, p := range parts {
		langs = append(langs, p)
	}

	return
}

func stripFileExtension(full string) (stripped string) {
	extension := path.Ext(full)
	stripped = strings.TrimSuffix(full, extension)
	return stripped
}