diff options
| author | Christine Dodrill <me@christine.website> | 2018-10-31 23:10:04 +0000 |
|---|---|---|
| committer | Christine Dodrill <me@christine.website> | 2018-10-31 23:10:04 +0000 |
| commit | 24d63f4c9832538a8b4d119f54df2507ffe46578 (patch) | |
| tree | 95975a665382f7ebe3d7666e5fd0292092dd966d /cmd/site | |
| parent | b7493881049aaa908de04837d004ec5cf2d91b90 (diff) | |
| download | xesite-i18n.tar.xz xesite-i18n.zip | |
add translationsi18n
Diffstat (limited to 'cmd/site')
| -rw-r--r-- | cmd/site/html.go | 46 | ||||
| -rw-r--r-- | cmd/site/i18n.go | 46 | ||||
| -rw-r--r-- | cmd/site/locale.go | 97 | ||||
| -rw-r--r-- | cmd/site/main.go | 41 |
4 files changed, 199 insertions, 31 deletions
diff --git a/cmd/site/html.go b/cmd/site/html.go index f42b947..1037baf 100644 --- a/cmd/site/html.go +++ b/cmd/site/html.go @@ -19,29 +19,39 @@ func logTemplateTime(name string, from time.Time) { func (s *Site) renderTemplatePage(templateFname string, data interface{}) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer logTemplateTime(templateFname, time.Now()) - s.tlock.RLock() - defer s.tlock.RUnlock() - var t *template.Template - var err error - - if s.templates[templateFname] == nil { - t, err = template.ParseFiles("templates/base.html", "templates/"+templateFname) + getTranslation := func(group, key string, vals ...interface{}) string { + var lc locale + var ok bool + locale, err := GetPreferredLocale(r) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - ln.Error(context.Background(), err, ln.F{"action": "renderTemplatePage", "page": templateFname}) - fmt.Fprintf(w, "error: %v", err) + ln.Error(r.Context(), err) + lc = s.t.locales["en"] + goto overwith } - ln.Log(context.Background(), ln.F{"action": "loaded_new_template", "fname": templateFname}) + lc, ok = s.t.Get(locale.Lang) + if !ok { + ln.Log(r.Context(), ln.F{"msg": "unknown language requested", "lang": locale.Lang, "header": r.Header.Get("Accept-Language")}) + lc = s.t.locales["en"] + goto overwith + } - s.tlock.RUnlock() - s.tlock.Lock() - s.templates[templateFname] = t - s.tlock.Unlock() - s.tlock.RLock() - } else { - t = s.templates[templateFname] + overwith: + return lc.Value(group, key, vals...) + } + funcMap := template.FuncMap{ + "trans": getTranslation, + } + + var t *template.Template = template.New(templateFname).Funcs(funcMap) + var err error + + t, err = t.ParseFiles("templates/base.html", "templates/"+templateFname) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + ln.Error(context.Background(), err, ln.F{"action": "renderTemplatePage", "page": templateFname}) + fmt.Fprintf(w, "error: %v", err) } err = t.Execute(w, data) diff --git a/cmd/site/i18n.go b/cmd/site/i18n.go new file mode 100644 index 0000000..6c8fc92 --- /dev/null +++ b/cmd/site/i18n.go @@ -0,0 +1,46 @@ +package main + +import ( + "encoding/json" + "fmt" + "io" +) + +type locale map[string]map[string]string + +func (l locale) Value(group, key string, args ...interface{}) string { + sg, ok := l[group] + if !ok { + return "no group " + group + } + + result, ok := sg[key] + if !ok { + return fmt.Sprintf("in group %s, no key %s", group, key) + } + + return fmt.Sprintf(result, args...) +} + +type translations struct { + locales map[string]locale +} + +func (t *translations) LoadLocale(name string, r io.Reader) error { + l := locale{} + d := json.NewDecoder(r) + err := d.Decode(&l) + if err == nil { + t.locales[name] = l + } + return err +} + +func (t *translations) Get(name string) (locale, bool) { + l, ok := t.locales[name] + if !ok { + return nil, false + } + + return l, ok +} diff --git a/cmd/site/locale.go b/cmd/site/locale.go new file mode 100644 index 0000000..a65034a --- /dev/null +++ b/cmd/site/locale.go @@ -0,0 +1,97 @@ +package main + +import ( + "errors" + "net/http" + "strconv" + "strings" +) + +// Locale is locale value from the Accept-Language header in request +type Locale struct { + Lang, Country string + Qual float64 +} + +// Name returns the locale value in 'lang' or 'lang_country' format +// eg: de_DE, en_US, gb +func (l *Locale) Name() string { + if len(l.Country) > 0 { + return l.Lang + "_" + l.Country + } + return l.Lang +} + +// ParseLocale creates a Locale from a locale string +func ParseLocale(locale string) Locale { + locsplt := strings.Split(locale, "_") + resp := Locale{} + resp.Lang = locsplt[0] + if len(locsplt) > 1 { + resp.Country = locsplt[1] + } + return resp +} + +const ( + acceptLanguage = "Accept-Language" +) + +func supportedLocales(alstr string) []Locale { + locales := make([]Locale, 0) + alstr = strings.Replace(alstr, " ", "", -1) + if alstr == "" { + return locales + } + al := strings.Split(alstr, ",") + for _, lstr := range al { + locales = append(locales, Locale{ + Lang: parseLang(lstr), + Country: parseCountry(lstr), + Qual: parseQual(lstr), + }) + } + return locales +} + +// GetLocales returns supported locales for the given requet +func GetLocales(r *http.Request) []Locale { + return supportedLocales(r.Header.Get(acceptLanguage)) +} + +// GetPreferredLocale return preferred locale for the given reuqest +// returns error if there is no preferred locale +func GetPreferredLocale(r *http.Request) (*Locale, error) { + locales := GetLocales(r) + if len(locales) == 0 { + return &Locale{}, errors.New("No locale found") + } + return &locales[0], nil +} + +func parseLang(val string) string { + locale := strings.Split(val, ";")[0] + lang := strings.Split(locale, "-")[0] + return lang +} + +func parseCountry(val string) string { + locale := strings.Split(val, ";")[0] + spl := strings.Split(locale, "-") + if len(spl) > 1 { + return spl[1] + } + return "" +} + +func parseQual(val string) float64 { + spl := strings.Split(val, ";") + if len(spl) > 1 { + qual, err := strconv.ParseFloat(strings.Split(spl[1], "=")[1], 64) + if err != nil { + return 1 + } + return qual + } + return 1 +} diff --git a/cmd/site/main.go b/cmd/site/main.go index e9b4d21..83cbeb2 100644 --- a/cmd/site/main.go +++ b/cmd/site/main.go @@ -9,7 +9,6 @@ import ( "path/filepath" "sort" "strings" - "sync" "time" "github.com/Xe/jsonfeed" @@ -46,10 +45,8 @@ type Site struct { mux *http.ServeMux - templates map[string]*template.Template - tlock sync.RWMutex - segment analytics.Client + t *translations } func (s *Site) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -78,31 +75,49 @@ func Build() (*Site, error) { Date string } + t := &translations{ + locales: map[string]locale{}, + } + + for _, lang := range []string{"en", "tp"} { + fin, err := os.Open(filepath.Join("locales", lang+".json")) + if err != nil { + return nil, err + } + defer fin.Close() + + err = t.LoadLocale(lang, fin) + if err != nil { + return nil, err + } + } + + l := t.locales["en"] + s := &Site{ rssFeed: &feeds.Feed{ - Title: "Christine Dodrill's Blog", + Title: l.Value("blog", "title"), Link: &feeds.Link{Href: "https://christine.website/blog"}, - Description: "My blog posts and rants about various technology things.", + Description: l.Value("blog", "description"), Author: &feeds.Author{Name: "Christine Dodrill", Email: "me@christine.website"}, Created: bootTime, - Copyright: "This work is copyright Christine Dodrill. My viewpoints are my own and not the view of any employer past, current or future.", + Copyright: l.Value("meta", "rss_copyright"), }, jsonFeed: &jsonfeed.Feed{ Version: jsonfeed.CurrentVersion, - Title: "Christine Dodrill's Blog", + Title: l.Value("blog", "title"), HomePageURL: "https://christine.website", FeedURL: "https://christine.website/blog.json", - Description: "My blog posts and rants about various technology things.", - UserComment: "This is a JSON feed of my blogposts. For more information read: https://jsonfeed.org/version/1", + Description: l.Value("blog", "description"), + UserComment: l.Value("meta", "json_feed"), Icon: icon, Favicon: icon, Author: jsonfeed.Author{ - Name: "Christine Dodrill", + Name: l.Value("header", "name"), Avatar: icon, }, }, - mux: http.NewServeMux(), - templates: map[string]*template.Template{}, + mux: http.NewServeMux(), } if wk := os.Getenv("SEGMENT_WRITE_KEY"); wk != "" { |
