hao1118 / fasthttp-rendering-jet-template

2 stars 0 forks source link

GetUserLocale() #2

Open nkev opened 7 years ago

nkev commented 7 years ago

Hi, I'm still learning so much from your code. Thank you so much. I think you should share more of your talent :) I have a few questions if you don't mind:

Could I study your GetUserLocale(ctx) and LS() functions please?

What marshal/unmarshal library do you use to render JSON API calls?

How do you use middleware in your request chain with fasthttp?

Thank you!

hao1118 commented 7 years ago

Hi nkev, welcome!

package main
import (
    "fmt"
    "log"
    "os"
    "strings"
    "time"
    "runtime/debug"
    "github.com/CloudyKit/jet"
    "github.com/fasthttp-contrib/sessions"
    "github.com/patrickmn/go-cache"
    "github.com/valyala/fasthttp"
    "gopkg.in/mgo.v2"
    "gopkg.in/mgo.v2/bson"
)
//config.json
{
  "Locales": [["zh","中文"],["en","English"]],
  "Port": ":80",
  "Database": "mymongodb", 
  "Admin": "admin",
  "Password": "admin#2016",
  "Debug": true
}
//globals
......
var myConfig Config
var mapLocale map[string](map[string]string)   //locale strings
var mongo *mgo.Session
//auto run before main
func init() {
    if !LoadConfig() {
        fmt.Println("Load Config failed. ")
        os.Exit(1)
    }
    var err error
    mongo, err = mgo.Dial("127.0.0.1:27017")
    if err != nil {
        fmt.Println("MongoDB connection failed:", err.Error())
        os.Exit(1)
    }
    mapLocale = LoadLocales()
}
type Config struct {
    Locales  [][]string `json:"Locales"`
    Port     string     `json:"Port"`
    Database string     `json:"Database"`
    Admin    string     `json:"Admin"`
    Password string     `json:"Password"`
    Debug    bool       `json:"Debug"`
}
//use the package of "encoding/json" to unmarshal config.json file
//you can do in this way to render json:
//jsondata, err := json.Marshal(jsonstruct)
//if err!=nil...else
//ctx.SetBody(jsondata)
func LoadConfig() bool {
    fd, err := ioutil.ReadFile("config.json")
    if err != nil {
        return false
    }
    err = json.Unmarshal(fd, &myConfig)
    if err != nil {
        return false
    }
    return true
}
func LoadLocales() map[string](map[string]string) {
    ms := mongo.Clone()
    defer ms.Close()
    co := ms.DB(myConfig.Database).C("locales")
    LoadLocalesFromSource(co)
    docs := []LocaleData{}
    co.Find(bson.M{}).All(&docs)
    locs := make(map[string](map[string]string))
    for _, doc := range docs {
        locs[doc.Key] = doc.Locales
    }
    return locs
}
func LoadFileLocales(co *mgo.Collection, fn string) {
    var reg = regexp.MustCompile(`LS\([^\)]+\)`)
    data, err := ioutil.ReadFile(fn)
    if err != nil {
        panic(err)
    }
    ss := reg.FindAllString(string(data), -1)
    for _, s := range ss {
        f := strings.Index(s, "\"")
        t := strings.LastIndex(s, "\"")
        if f < 0 || t <= f {
            continue
        }
        k := strings.TrimSpace(s[f+1 : t])
        if k == "" {
            continue
        }
        ld := LocaleData{}
        err := co.Find(bson.M{"key": k}).One(&ld)
        if err != nil {
            co.Insert(LocaleData{Id: bson.NewObjectId(), Key: k, Locales: make(map[string]string)})
        }
    }
}
//load strings need to be translated from program and template files
func LoadLocalesFromSource(co *mgo.Collection) {
    paths := []string{"./", "./views/admin/", "./views/public/", "./views/shared/"}
    for _, p := range paths {
        files, _ := ioutil.ReadDir(p)
        for _, f := range files {
            if f.IsDir() || f.Name() == "locale.go" {
                continue
            }
            if strings.HasSuffix(f.Name(), ".go") || strings.HasSuffix(f.Name(), ".jet.html") {
                LoadFileLocales(co, p+f.Name())
            }
        }
    }
}
//locale data saved in mongodb
type LocaleData struct {
    Id      bson.ObjectId     `bson:"_id"`
    Key     string            `bson:"key"`
    Locales map[string]string `bson:"lcs"`
}
//the function to translate string
func LS(l, s string) string {
    v, ok := mapLocale[s]
    if ok {
        ls, ok := v[l]
        if ok && ls != "" {
            return ls
        }
    }
    return s
}
//functions to get locale from user's browser
type LangQ struct {
    Lang string
    Q    float64
}
func ParseAcceptLanguage(AcceptLangs string) []LangQ {
    var lqs []LangQ
    arrLang := strings.Split(AcceptLangs, ",")
    for _, lang := range arrLang {
        sLang := strings.TrimSpace(lang)
        langQ := strings.Split(sLang, ";")
        ln := len(langQ)
        if ln == 0 {
            continue
        }
        if ln == 1 {
            lq := LangQ{langQ[0], 1}
            lqs = append(lqs, lq)
        } else {
            qp := strings.Split(langQ[1], "=")
            if len(qp) > 1 {
                q, err := strconv.ParseFloat(qp[1], 64)
                if err != nil {
                    continue
                }
                lq := LangQ{langQ[0], q}
                lqs = append(lqs, lq)
            }
        }
    }
    return lqs
}
func GetAgentLocale(AcceptLangs string) string {
    lqs := ParseAcceptLanguage(AcceptLangs)
    q := 0.0
    l := myConfig.Locales[0][0]  //default locale
    for _, lq := range lqs {
        for _, lc := range myConfig.Locales {
            if lq.Q > q && strings.HasPrefix(lq.Lang, lc[0]) {
                q = lq.Q
                l = lc[0]
            }
        }
    }
    return l
}
func GetLocale(ctx *fasthttp.RequestCtx) string {
    langs := string(ctx.Request.Header.Peek("Accept-Language"))
    if langs != "" {
        return GetAgentLocale(langs)  //return the browser's best accepted locale
    }
    return myConfig.Locales[0][0], ""  //return default locale
}
//middleware is just a wrapper func run above router handler
//in func main
    if err := fasthttp.ListenAndServe(myConfig.Port, middleFunc); err != nil {
        log.Fatalf("Error in ListenAndServe: %s", err)
    }
//middleware
func middleFunc(ctx *fasthttp.RequestCtx){
      //middleware codes here, you can save middle variables in "context"
     ...
      //router handler
      requestHandler(ctx)
     //middleware codes also may be here
     ...
}
func requestHandler(ctx *fasthttp.RequestCtx) {
    defer func() {
        if err := recover(); err != nil {
            log.Fatalf("Recovery error: %s", err)
            log.Fatalf("Stack: %s", debug.Stack())
        }
    }()
    path := strings.ToLower(string(ctx.Path()))
        //set handle funcs according to path and use context in these funcs
       if path=="/"|| path=="/home"{
              home(ctx)
       }else if ...
    ...
}