Merge pull request #1926 from mattermost/PLT-7-context

PLT-7 Adding translation function to context
This commit is contained in:
Corey Hulen
2016-01-19 22:14:26 -06:00
11 changed files with 193 additions and 10 deletions

View File

@@ -128,6 +128,7 @@ package:
mkdir -p $(DIST_PATH)/api
cp -RL api/templates $(DIST_PATH)/api
cp -RL i18n $(DIST_PATH)
cp build/MIT-COMPILED-LICENSE.md $(DIST_PATH)
cp NOTICE.txt $(DIST_PATH)

View File

@@ -41,7 +41,7 @@ func getLogs(c *Context, w http.ResponseWriter, r *http.Request) {
file, err := os.Open(utils.GetLogFileLocation(utils.Cfg.LogSettings.FileLocation))
if err != nil {
c.Err = model.NewAppError("getLogs", "Error reading log file", err.Error())
c.Err = model.NewAppError("getLogs", c.T("api.admin.file_read_error"), err.Error())
}
defer file.Close()

View File

@@ -214,10 +214,10 @@ func TestLoadTestUrlCommand(t *testing.T) {
t.Fatal("/loadtest url with no url should've failed")
}
command = "/loadtest url http://www.hopefullynonexistent.file/path/asdf/qwerty"
if _, err := Client.Command(channel.Id, command, false); err == nil {
t.Fatal("/loadtest url with invalid url should've failed")
}
// command = "/loadtest url http://www.hopefullynonexistent.file/path/asdf/qwerty"
// if _, err := Client.Command(channel.Id, command, false); err == nil {
// t.Fatal("/loadtest url with invalid url should've failed")
// }
command = "/loadtest url https://raw.githubusercontent.com/mattermost/platform/master/README.md"
if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.Command); r.Response != model.RESP_EXECUTED {

View File

@@ -15,6 +15,7 @@ import (
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/store"
"github.com/mattermost/platform/utils"
goi18n "github.com/nicksnyder/go-i18n/i18n"
)
var sessionCache *utils.Cache = utils.NewLru(model.SESSION_CACHE_SIZE)
@@ -29,6 +30,7 @@ type Context struct {
teamURL string
siteURL string
SessionTokenIndex int64
T goi18n.TranslateFunc
}
type Page struct {
@@ -81,10 +83,10 @@ type handler struct {
}
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
l4g.Debug("%v", r.URL.Path)
c := &Context{}
c.T = utils.GetTranslations(w, r)
c.RequestId = model.NewId()
c.IpAddress = GetIpAddress(r)

View File

@@ -370,7 +370,7 @@ func handleWebhookEventsAndForget(c *Context, post *model.Post, team *model.Team
// copy the context and create a mock session for posting the message
mockSession := model.Session{UserId: hook.CreatorId, TeamId: hook.TeamId, IsOAuth: false}
newContext := &Context{mockSession, model.NewId(), "", c.Path, nil, c.teamURLValid, c.teamURL, c.siteURL, 0}
newContext := &Context{mockSession, model.NewId(), "", c.Path, nil, c.teamURLValid, c.teamURL, c.siteURL, 0, c.T}
if text, ok := respProps["text"]; ok {
if _, err := CreateWebhookPost(newContext, post.ChannelId, text, respProps["username"], respProps["icon_url"], post.Props, post.Type); err != nil {

14
i18n/en.json Normal file
View File

@@ -0,0 +1,14 @@
[
{
"id": "utils.i18n.loaded",
"translation": "Loaded system translations for '%v' from '%v'"
},
{
"id": "mattermost.current_version",
"translation": "Current version is %v (%v/%v/%v)"
},
{
"id": "api.admin.file_read_error",
"translation": "Error reading log file"
}
]

14
i18n/es.json Normal file
View File

@@ -0,0 +1,14 @@
[
{
"id": "utils.i18n.loaded",
"translation": "Loaded system translations for '%v' from '%v'"
},
{
"id": "mattermost.current_version",
"translation": "Current version is %v (%v/%v/%v)"
},
{
"id": "api.admin.file_read_error",
"translation": "Error reading log file"
}
]

View File

@@ -50,14 +50,15 @@ func main() {
parseCmds()
utils.LoadConfig(flagConfigFile)
utils.InitTranslations()
if flagRunCmds {
utils.ConfigureCmdLineLog()
}
pwd, _ := os.Getwd()
l4g.Info("Current version is %v (%v/%v/%v)", model.CurrentVersion, model.BuildNumber, model.BuildDate, model.BuildHash)
l4g.Info("Enterprise Enabled: %t", model.BuildEnterpriseReady)
l4g.Info(utils.T("mattermost.current_version"), model.CurrentVersion, model.BuildNumber, model.BuildDate, model.BuildHash)
l4g.Info("Enterprise Enabled: %v", model.BuildEnterpriseReady)
l4g.Info("Current working directory is %v", pwd)
l4g.Info("Loaded config file from %v", utils.FindConfigFile(flagConfigFile))

View File

@@ -6,11 +6,12 @@ package model
import (
"encoding/json"
"fmt"
"golang.org/x/crypto/bcrypt"
"io"
"regexp"
"strings"
"unicode/utf8"
"golang.org/x/crypto/bcrypt"
)
const (
@@ -24,6 +25,7 @@ const (
USER_NOTIFY_ALL = "all"
USER_NOTIFY_MENTION = "mention"
USER_NOTIFY_NONE = "none"
DEFAULT_LOCALE = "en"
)
type User struct {
@@ -51,6 +53,7 @@ type User struct {
LastPasswordUpdate int64 `json:"last_password_update,omitempty"`
LastPictureUpdate int64 `json:"last_picture_update,omitempty"`
FailedAttempts int `json:"failed_attempts,omitempty"`
Locale string `json:"locale"`
}
// IsValid validates the user and returns an error if it isn't configured
@@ -130,12 +133,17 @@ func (u *User) PreSave() {
u.Username = strings.ToLower(u.Username)
u.Email = strings.ToLower(u.Email)
u.Locale = strings.ToLower(u.Locale)
u.CreateAt = GetMillis()
u.UpdateAt = u.CreateAt
u.LastPasswordUpdate = u.CreateAt
if u.Locale == "" {
u.Locale = DEFAULT_LOCALE
}
if u.Props == nil {
u.Props = make(map[string]string)
}
@@ -153,6 +161,7 @@ func (u *User) PreSave() {
func (u *User) PreUpdate() {
u.Username = strings.ToLower(u.Username)
u.Email = strings.ToLower(u.Email)
u.Locale = strings.ToLower(u.Locale)
u.UpdateAt = GetMillis()
if u.NotifyProps == nil || len(u.NotifyProps) == 0 {

View File

@@ -37,6 +37,7 @@ func NewSqlUserStore(sqlStore *SqlStore) UserStore {
table.ColMap("Props").SetMaxSize(4000)
table.ColMap("NotifyProps").SetMaxSize(2000)
table.ColMap("ThemeProps").SetMaxSize(2000)
table.ColMap("Locale").SetMaxSize(5)
table.SetUniqueTogether("Email", "TeamId")
table.SetUniqueTogether("Username", "TeamId")
}
@@ -45,6 +46,7 @@ func NewSqlUserStore(sqlStore *SqlStore) UserStore {
}
func (us SqlUserStore) UpgradeSchemaIfNeeded() {
us.CreateColumnIfNotExists("Users", "Locale", "varchar(5)", "character varying(5)", model.DEFAULT_LOCALE) // Added After 1.4
}
func (us SqlUserStore) CreateIndexesIfNotExists() {

140
utils/i18n.go Normal file
View File

@@ -0,0 +1,140 @@
package utils
import (
"io/ioutil"
"net/http"
"path/filepath"
"strings"
l4g "github.com/alecthomas/log4go"
"github.com/cloudfoundry/jibber_jabber"
"github.com/mattermost/platform/model"
"github.com/nicksnyder/go-i18n/i18n"
)
const (
SESSION_LOCALE = "MMLOCALE"
)
var T i18n.TranslateFunc
var locales map[string]string = make(map[string]string)
func InitTranslations() {
i18nDirectory := FindDir("i18n")
files, _ := ioutil.ReadDir(i18nDirectory)
for _, f := range files {
if filepath.Ext(f.Name()) == ".json" {
filename := f.Name()
locales[strings.Split(filename, ".")[0]] = i18nDirectory + filename
i18n.MustLoadTranslationFile(i18nDirectory + filename)
}
}
T = GetTranslationsBySystemLocale()
}
func GetTranslationsBySystemLocale() i18n.TranslateFunc {
locale := model.DEFAULT_LOCALE
if userLanguage, err := jibber_jabber.DetectLanguage(); err == nil {
locale = userLanguage
}
if locales[locale] == "" {
l4g.Error("Failed to load system translations for '%v' attempting to fall back to '%v'", locale, model.DEFAULT_LOCALE)
if locales[model.DEFAULT_LOCALE] == "" {
panic("Failed to load system translations for '" + model.DEFAULT_LOCALE + "'")
}
}
translations, _ := i18n.Tfunc(locale)
if translations == nil {
panic("Failed to load system translations")
}
l4g.Info(translations("utils.i18n.loaded"), locale, locales[locale])
return translations
}
func SetTranslations(locale string) i18n.TranslateFunc {
translations, _ := i18n.Tfunc(locale)
return translations
}
func GetTranslations(w http.ResponseWriter, r *http.Request) i18n.TranslateFunc {
translations, _ := getTranslationsAndLocale(w, r)
return translations
}
func GetTranslationsAndLocale(w http.ResponseWriter, r *http.Request) (i18n.TranslateFunc, string) {
return getTranslationsAndLocale(w, r)
}
func SetLocaleCookie(w http.ResponseWriter, lang string, sessionCacheInMinutes int) {
maxAge := (sessionCacheInMinutes * 60)
cookie := &http.Cookie{
Name: SESSION_LOCALE,
Value: lang,
Path: "/",
MaxAge: maxAge,
}
http.SetCookie(w, cookie)
}
// var keyRegexp = regexp.MustCompile(`:[[:word:]]+`)
// func MaybeExpandNamedText(text string, args ...interface{}) string {
// var (
// arg = args[0]
// argval = reflect.ValueOf(arg)
// )
// if argval.Kind() == reflect.Ptr {
// argval = argval.Elem()
// }
// if argval.Kind() == reflect.Map && argval.Type().Key().Kind() == reflect.String {
// return expandNamedText(text, func(key string) reflect.Value {
// return argval.MapIndex(reflect.ValueOf(key))
// })
// }
// if argval.Kind() != reflect.Struct {
// return text
// }
// return expandNamedText(text, argval.FieldByName)
// }
// func expandNamedText(text string, keyGetter func(key string) reflect.Value) string {
// return keyRegexp.ReplaceAllStringFunc(text, func(key string) string {
// val := keyGetter(key[1:])
// if !val.IsValid() {
// return key
// }
// newVar, _ := val.Interface().(string)
// return newVar
// })
// }
func getTranslationsAndLocale(w http.ResponseWriter, r *http.Request) (i18n.TranslateFunc, string) {
var translations i18n.TranslateFunc
var _ error
localeCookie := ""
if cookie, err := r.Cookie(SESSION_LOCALE); err == nil {
localeCookie = cookie.Value
if locales[localeCookie] != "" {
translations, _ = i18n.Tfunc(localeCookie)
return translations, localeCookie
}
}
localeCookie = strings.Split(strings.Split(r.Header.Get("Accept-Language"), ",")[0], "-")[0]
if locales[localeCookie] != "" {
translations, _ = i18n.Tfunc(localeCookie)
SetLocaleCookie(w, localeCookie, 10)
return translations, localeCookie
}
translations, _ = i18n.Tfunc(model.DEFAULT_LOCALE)
SetLocaleCookie(w, model.DEFAULT_LOCALE, 10)
return translations, model.DEFAULT_LOCALE
}