Skip to content

Commit

Permalink
merge packages
Browse files Browse the repository at this point in the history
  • Loading branch information
chonglou committed May 11, 2017
1 parent 40df762 commit f0803dc
Show file tree
Hide file tree
Showing 22 changed files with 898 additions and 0 deletions.
34 changes: 34 additions & 0 deletions cache/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package cache

import (
"bytes"
"encoding/gob"
"time"
)

// Cache cache
type Cache struct {
Store Store `inject:""`
}

//Set cache item
func (p *Cache) Set(key string, val interface{}, ttl time.Duration) error {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
if err := enc.Encode(val); err != nil {
return err
}
return p.Store.Set(key, buf.Bytes(), ttl)
}

//Get get from cache
func (p *Cache) Get(key string, val interface{}) error {
bys, err := p.Store.Get(key)
if err != nil {
return err
}
var buf bytes.Buffer
dec := gob.NewDecoder(&buf)
buf.Write(bys)
return dec.Decode(val)
}
52 changes: 52 additions & 0 deletions cache/redis/store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package redis

import (
"time"

_redis "github.com/garyburd/redigo/redis"
)

// Store cache by redis
type Store struct {
Pool *_redis.Pool `inject:""`
}

// Status status
func (p *Store) Status() (string, error) {
c := p.Pool.Get()
defer c.Close()
return _redis.String(c.Do("INFO"))
}

//Flush clear cache items
func (p *Store) Flush() error {
c := p.Pool.Get()
defer c.Close()
keys, err := _redis.Values(c.Do("KEYS", "*"))
if err == nil && len(keys) > 0 {
_, err = c.Do("DEL", keys...)
}
return err
}

//Keys list cache items
func (p *Store) Keys() ([]string, error) {
c := p.Pool.Get()
defer c.Close()
return _redis.Strings(c.Do("KEYS", "*"))
}

// Set set bytes
func (p *Store) Set(key string, val []byte, ttl time.Duration) error {
c := p.Pool.Get()
defer c.Close()
_, err := c.Do("SET", key, val, "EX", int(ttl/time.Second))
return err
}

// Get get bytes
func (p *Store) Get(key string) ([]byte, error) {
c := p.Pool.Get()
defer c.Close()
return _redis.Bytes(c.Do("GET", key))
}
12 changes: 12 additions & 0 deletions cache/store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package cache

import "time"

// Store store
type Store interface {
Set(key string, val []byte, ttl time.Duration) error
Get(key string) ([]byte, error)
Flush() error
Keys() ([]string, error)
Status() (string, error)
}
48 changes: 48 additions & 0 deletions i18n/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package i18n

import (
"net/http"

"github.com/kapmahc/h2o"
"golang.org/x/text/language"
)

const (
// LOCALE locale key
LOCALE = "locale"
)

// Middleware detect language from http request
func (p *I18n) Middleware(c *h2o.Context) error {
langs, err := p.Store.Languages()
if err != nil {
return err
}
var tags []language.Tag
for _, l := range langs {
tags = append(tags, language.Make(l))
}
matcher := language.NewMatcher(tags)
tag, _, _ := matcher.Match(language.Make(p.detect(c.Request)))
c.Set(LOCALE, tag.String())
return nil
}

func (p *I18n) detect(r *http.Request) string {
// 1. Check URL arguments.
if lang := r.URL.Query().Get(LOCALE); lang != "" {
return lang
}

// 2. Get language information from cookies.
if ck, er := r.Cookie(LOCALE); er == nil {
return ck.Value
}

// 3. Get language information from 'Accept-Language'.
if al := r.Header.Get("Accept-Language"); len(al) > 4 {
return al[:5] // Only compare first 5 letters.
}

return ""
}
144 changes: 144 additions & 0 deletions i18n/i18n.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package i18n

import (
"bytes"
"fmt"
"html/template"
"os"
"path/filepath"
"strings"
"time"

log "github.com/Sirupsen/logrus"
"github.com/go-ini/ini"
"github.com/kapmahc/h2o"
"github.com/kapmahc/h2o/cache"
"golang.org/x/text/language"
)

// I18n i18n
type I18n struct {
Store Store `inject:""`
Cache *cache.Cache `inject:""`
}

// F format message
func (p *I18n) F(lang, code string, obj interface{}) (string, error) {
msg, err := p.Store.Get(lang, code)
if err != nil {
return "", err
}
tpl, err := template.New("").Parse(msg)
if err != nil {
return "", err
}
var buf bytes.Buffer
err = tpl.Execute(&buf, obj)
return buf.String(), err
}

//E create an i18n error
func (p *I18n) E(status int, lang, code string, args ...interface{}) error {
return &h2o.HTTPError{
Message: p.T(lang, code, args...),
Status: status,
}
}

//T translate by lang tag
func (p *I18n) T(lang, code string, args ...interface{}) string {
msg := p.get(lang, code)
if msg == "" {
return code
}
return fmt.Sprintf(msg, args...)
}

// All all items
func (p *I18n) All(lang string) (map[string]interface{}, error) {
rt := make(map[string]interface{})

items, err := p.Store.All(lang)
if err != nil {
return nil, err
}
for k, v := range items {
codes := strings.Split(k, ".")
tmp := rt
for i, c := range codes {
if i+1 == len(codes) {
tmp[c] = v
} else {
if tmp[c] == nil {
tmp[c] = make(map[string]interface{})
}
tmp = tmp[c].(map[string]interface{})
}
}

}
return rt, nil
}

// Load sync records
func (p *I18n) Load(dir string) error {
return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
const ext = ".ini"
name := info.Name()
if info.Mode().IsRegular() && filepath.Ext(name) == ext {
log.Debugf("Find locale file %s", path)
if err != nil {
return err
}
lang := name[0 : len(name)-len(ext)]
if _, err := language.Parse(lang); err != nil {
return err
}
cfg, err := ini.Load(path)
if err != nil {
return err
}

items := cfg.Section(ini.DEFAULT_SECTION).KeysHash()
for k, v := range items {
if err := p.Store.Set(lang, k, v, false); err != nil {
return err
}
}
log.Infof("find %d items", len(items))
}
return nil
})
}

// Set update locale
func (p *I18n) Set(lang, code, message string) error {
key := p.key(lang, code)
if err := p.Store.Set(lang, code, message, true); err != nil {
return err
}
return p.Cache.Set(key, message, defaultTTL)
}

func (p *I18n) key(lang, code string) string {
return lang + "://locales/" + code
}

func (p *I18n) get(lang, code string) string {
key := p.key(lang, code)
var msg string
err := p.Cache.Get(key, &msg)
if err == nil {
return msg
}
msg, err = p.Store.Get(lang, code)
if err == nil {
p.Cache.Set(key, msg, defaultTTL)
return msg
}
return ""
}

const (
defaultTTL = time.Hour * 24 * 30
)
18 changes: 18 additions & 0 deletions i18n/orm/models.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package orm

import "time"

//Model locale model
type Model struct {
ID uint `gorm:"primary_key"`
Lang string `gorm:"not null;type:varchar(8);index;unique_index:idx_locales_lang_code"`
Code string `gorm:"not null;index;type:VARCHAR(255);unique_index:idx_locales_lang_code"`
Message string `gorm:"not null;type:varchar(1024)"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}

// TableName table name
func (Model) TableName() string {
return "locales"
}
66 changes: 66 additions & 0 deletions i18n/orm/store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package orm

import "github.com/jinzhu/gorm"

// Store gorm-store
type Store struct {
Db *gorm.DB `inject:""`
}

//Set set locale
func (p *Store) Set(lang, code, message string, override bool) error {
var l Model
if p.Db.Where("lang = ? AND code = ?", lang, code).First(&l).RecordNotFound() {
l.Lang = lang
l.Code = code
l.Message = message
return p.Db.Create(&l).Error
}
if override {
l.Message = message
return p.Db.Save(&l).Error
}

return nil
}

//Del del locale
func (p *Store) Del(lang, code string) error {
return p.Db.Where("lang = ? AND code = ?", lang, code).Delete(Model{}).Error
}

// Get get message
func (p *Store) Get(lang, code string) (string, error) {
var l Model
if err := p.Db.
Select("message").
Where("lang = ? AND code = ?", lang, code).
First(&l).Error; err != nil {
return "", err
}
return l.Message, nil
}

// Languages languages
func (p *Store) Languages() ([]string, error) {
var val []string
err := p.Db.Model(&Model{}).Pluck("DISTINCT lang", &val).Error
return val, err
}

// All list all items
func (p *Store) All(lang string) (map[string]string, error) {
var items []Model
if err := p.Db.
Select([]string{"code", "message"}).
Where("lang = ?", lang).
Find(&items).Error; err != nil {
return nil, err
}

rt := make(map[string]string)
for _, l := range items {
rt[l.Code] = l.Message
}
return rt, nil
}
10 changes: 10 additions & 0 deletions i18n/store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package i18n

// Store store
type Store interface {
Set(lang, code, message string, override bool) error
Get(lang, code string) (string, error)
All(lang string) (map[string]string, error)
Del(lang, code string) error
Languages() ([]string, error)
}
Loading

0 comments on commit f0803dc

Please sign in to comment.