PLT-7 Adding translation function to context

Этот коммит содержится в:
=Corey Hulen
2016-01-19 22:00:01 -06:00
родитель 36c5c46e24
Коммит 8e404c1dcf
12 изменённых файлов: 194 добавлений и 11 удалений

Просмотреть файл

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

Просмотреть файл

@@ -41,7 +41,7 @@ func getLogs(c *Context, w http.ResponseWriter, r *http.Request) {
file, err := os.Open(utils.GetLogFileLocation(utils.Cfg.LogSettings.FileLocation)) file, err := os.Open(utils.GetLogFileLocation(utils.Cfg.LogSettings.FileLocation))
if err != nil { 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() defer file.Close()

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@@ -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 // copy the context and create a mock session for posting the message
mockSession := model.Session{UserId: hook.CreatorId, TeamId: hook.TeamId, IsOAuth: false} 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 text, ok := respProps["text"]; ok {
if _, err := CreateWebhookPost(newContext, post.ChannelId, text, respProps["username"], respProps["icon_url"], post.Props, post.Type); err != nil { if _, err := CreateWebhookPost(newContext, post.ChannelId, text, respProps["username"], respProps["icon_url"], post.Props, post.Type); err != nil {

14
i18n/en.json Обычный файл
Просмотреть файл

@@ -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 Обычный файл
Просмотреть файл

@@ -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"
}
]

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@@ -28,7 +28,7 @@ var CurrentVersion string = versions[0]
var BuildNumber = "_BUILD_NUMBER_" var BuildNumber = "_BUILD_NUMBER_"
var BuildDate = "_BUILD_DATE_" var BuildDate = "_BUILD_DATE_"
var BuildHash = "_BUILD_HASH_" var BuildHash = "_BUILD_HASH_"
var BuildEnterpriseReady = "_BUILD_ENTERPRISE_READY_" var BuildEnterpriseReady = "false"
func SplitVersion(version string) (int64, int64, int64) { func SplitVersion(version string) (int64, int64, int64) {
parts := strings.Split(version, ".") parts := strings.Split(version, ".")

Просмотреть файл

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

140
utils/i18n.go Обычный файл
Просмотреть файл

@@ -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
}