aboutsummaryrefslogtreecommitdiff
path: root/cmd/site
diff options
context:
space:
mode:
authorChristine Dodrill <me@christine.website>2018-10-31 23:10:04 +0000
committerChristine Dodrill <me@christine.website>2018-10-31 23:10:04 +0000
commit24d63f4c9832538a8b4d119f54df2507ffe46578 (patch)
tree95975a665382f7ebe3d7666e5fd0292092dd966d /cmd/site
parentb7493881049aaa908de04837d004ec5cf2d91b90 (diff)
downloadxesite-i18n.tar.xz
xesite-i18n.zip
add translationsi18n
Diffstat (limited to 'cmd/site')
-rw-r--r--cmd/site/html.go46
-rw-r--r--cmd/site/i18n.go46
-rw-r--r--cmd/site/locale.go97
-rw-r--r--cmd/site/main.go41
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 != "" {