PLT-7522 Cleaned up translation of templates (#7351)
* PLT-7522 Cleaned up translation of templates * Added unit tests * Changed TranslateAsHtml to not be variadic
Этот коммит содержится в:
коммит произвёл
Joram Wilander
родитель
016b5daa1c
Коммит
6ca443a255
35
app/email.go
35
app/email.go
@@ -5,7 +5,6 @@ package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/url"
|
||||
|
||||
l4g "github.com/alecthomas/log4go"
|
||||
@@ -24,8 +23,8 @@ func SendChangeUsernameEmail(oldUsername, newUsername, email, locale, siteURL st
|
||||
bodyPage := utils.NewHTMLTemplate("email_change_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.username_change_body.title")
|
||||
bodyPage.Html["Info"] = template.HTML(T("api.templates.username_change_body.info",
|
||||
map[string]interface{}{"TeamDisplayName": utils.Cfg.TeamSettings.SiteName, "NewUsername": newUsername}))
|
||||
bodyPage.Html["Info"] = utils.TranslateAsHtml(T, "api.templates.username_change_body.info",
|
||||
map[string]interface{}{"TeamDisplayName": utils.Cfg.TeamSettings.SiteName, "NewUsername": newUsername})
|
||||
|
||||
if err := utils.SendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
return model.NewAppError("SendChangeUsernameEmail", "api.user.send_email_change_username_and_forget.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
@@ -68,8 +67,8 @@ func SendEmailChangeEmail(oldEmail, newEmail, locale, siteURL string) *model.App
|
||||
bodyPage := utils.NewHTMLTemplate("email_change_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.email_change_body.title")
|
||||
bodyPage.Html["Info"] = template.HTML(T("api.templates.email_change_body.info",
|
||||
map[string]interface{}{"TeamDisplayName": utils.Cfg.TeamSettings.SiteName, "NewEmail": newEmail}))
|
||||
bodyPage.Html["Info"] = utils.TranslateAsHtml(T, "api.templates.email_change_body.info",
|
||||
map[string]interface{}{"TeamDisplayName": utils.Cfg.TeamSettings.SiteName, "NewEmail": newEmail})
|
||||
|
||||
if err := utils.SendMail(oldEmail, subject, bodyPage.Render()); err != nil {
|
||||
return model.NewAppError("SendEmailChangeEmail", "api.user.send_email_change_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
@@ -111,8 +110,8 @@ func SendSignInChangeEmail(email, method, locale, siteURL string) *model.AppErro
|
||||
bodyPage := utils.NewHTMLTemplate("signin_change_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.signin_change_email.body.title")
|
||||
bodyPage.Html["Info"] = template.HTML(T("api.templates.signin_change_email.body.info",
|
||||
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"], "Method": method}))
|
||||
bodyPage.Html["Info"] = utils.TranslateAsHtml(T, "api.templates.signin_change_email.body.info",
|
||||
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"], "Method": method})
|
||||
|
||||
if err := utils.SendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
return model.NewAppError("SendSignInChangeEmail", "api.user.send_sign_in_change_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
@@ -170,8 +169,8 @@ func SendPasswordChangeEmail(email, method, locale, siteURL string) *model.AppEr
|
||||
bodyPage := utils.NewHTMLTemplate("password_change_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.password_change_body.title")
|
||||
bodyPage.Html["Info"] = template.HTML(T("api.templates.password_change_body.info",
|
||||
map[string]interface{}{"TeamDisplayName": utils.Cfg.TeamSettings.SiteName, "TeamURL": siteURL, "Method": method}))
|
||||
bodyPage.Html["Info"] = utils.TranslateAsHtml(T, "api.templates.password_change_body.info",
|
||||
map[string]interface{}{"TeamDisplayName": utils.Cfg.TeamSettings.SiteName, "TeamURL": siteURL, "Method": method})
|
||||
|
||||
if err := utils.SendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
return model.NewAppError("SendPasswordChangeEmail", "api.user.send_password_change_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
@@ -188,8 +187,8 @@ func SendUserAccessTokenAddedEmail(email, locale string) *model.AppError {
|
||||
|
||||
bodyPage := utils.NewHTMLTemplate("password_change_body", locale)
|
||||
bodyPage.Props["Title"] = T("api.templates.user_access_token_body.title")
|
||||
bodyPage.Html["Info"] = template.HTML(T("api.templates.user_access_token_body.info",
|
||||
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"], "SiteURL": utils.GetSiteURL()}))
|
||||
bodyPage.Html["Info"] = utils.TranslateAsHtml(T, "api.templates.user_access_token_body.info",
|
||||
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"], "SiteURL": utils.GetSiteURL()})
|
||||
|
||||
if err := utils.SendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
return model.NewAppError("SendUserAccessTokenAddedEmail", "api.user.send_user_access_token.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
@@ -210,7 +209,7 @@ func SendPasswordResetEmail(email string, token *model.Token, locale, siteURL st
|
||||
bodyPage := utils.NewHTMLTemplate("reset_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.reset_body.title")
|
||||
bodyPage.Html["Info"] = template.HTML(T("api.templates.reset_body.info"))
|
||||
bodyPage.Html["Info"] = utils.TranslateAsHtml(T, "api.templates.reset_body.info", nil)
|
||||
bodyPage.Props["ResetUrl"] = link
|
||||
bodyPage.Props["Button"] = T("api.templates.reset_body.button")
|
||||
|
||||
@@ -239,8 +238,7 @@ func SendMfaChangeEmail(email string, activated bool, locale, siteURL string) *m
|
||||
bodyPage.Props["Title"] = T("api.templates.mfa_deactivated_body.title")
|
||||
}
|
||||
|
||||
bodyPage.Html["Info"] = template.HTML(T(bodyText,
|
||||
map[string]interface{}{"SiteURL": siteURL}))
|
||||
bodyPage.Html["Info"] = utils.TranslateAsHtml(T, bodyText, map[string]interface{}{"SiteURL": siteURL})
|
||||
|
||||
if err := utils.SendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
return model.NewAppError("SendMfaChangeEmail", "api.user.send_mfa_change_email.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
@@ -262,11 +260,12 @@ func SendInviteEmails(team *model.Team, senderName string, invites []string, sit
|
||||
bodyPage := utils.NewHTMLTemplate("invite_body", model.DEFAULT_LOCALE)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = utils.T("api.templates.invite_body.title")
|
||||
bodyPage.Html["Info"] = template.HTML(utils.T("api.templates.invite_body.info",
|
||||
map[string]interface{}{"SenderStatus": senderRole, "SenderName": senderName, "TeamDisplayName": team.DisplayName}))
|
||||
bodyPage.Html["Info"] = utils.TranslateAsHtml(utils.T, "api.templates.invite_body.info",
|
||||
map[string]interface{}{"SenderStatus": senderRole, "SenderName": senderName, "TeamDisplayName": team.DisplayName})
|
||||
bodyPage.Props["Info"] = map[string]interface{}{}
|
||||
bodyPage.Props["Button"] = utils.T("api.templates.invite_body.button")
|
||||
bodyPage.Html["ExtraInfo"] = template.HTML(utils.T("api.templates.invite_body.extra_info",
|
||||
map[string]interface{}{"TeamDisplayName": team.DisplayName, "TeamURL": siteURL + "/" + team.Name}))
|
||||
bodyPage.Html["ExtraInfo"] = utils.TranslateAsHtml(utils.T, "api.templates.invite_body.extra_info",
|
||||
map[string]interface{}{"TeamDisplayName": team.DisplayName, "TeamURL": siteURL + "/" + team.Name})
|
||||
|
||||
props := make(map[string]string)
|
||||
props["email"] = invite
|
||||
|
||||
@@ -427,11 +427,11 @@ func getNotificationEmailBody(recipient *model.User, post *model.Post, channel *
|
||||
t := getFormattedPostTime(post, translateFunc)
|
||||
|
||||
var bodyText string
|
||||
var info string
|
||||
var info template.HTML
|
||||
if channel.Type == model.CHANNEL_DIRECT {
|
||||
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
||||
bodyText = translateFunc("app.notification.body.intro.direct.full")
|
||||
info = translateFunc("app.notification.body.text.direct.full",
|
||||
info = utils.TranslateAsHtml(translateFunc, "app.notification.body.text.direct.full",
|
||||
map[string]interface{}{
|
||||
"SenderName": senderName,
|
||||
"Hour": t.Hour,
|
||||
@@ -444,7 +444,7 @@ func getNotificationEmailBody(recipient *model.User, post *model.Post, channel *
|
||||
bodyText = translateFunc("app.notification.body.intro.direct.generic", map[string]interface{}{
|
||||
"SenderName": senderName,
|
||||
})
|
||||
info = translateFunc("app.notification.body.text.direct.generic",
|
||||
info = utils.TranslateAsHtml(translateFunc, "app.notification.body.text.direct.generic",
|
||||
map[string]interface{}{
|
||||
"Hour": t.Hour,
|
||||
"Minute": t.Minute,
|
||||
@@ -456,7 +456,7 @@ func getNotificationEmailBody(recipient *model.User, post *model.Post, channel *
|
||||
} else {
|
||||
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
||||
bodyText = translateFunc("app.notification.body.intro.notification.full")
|
||||
info = translateFunc("app.notification.body.text.notification.full",
|
||||
info = utils.TranslateAsHtml(translateFunc, "app.notification.body.text.notification.full",
|
||||
map[string]interface{}{
|
||||
"ChannelName": channelName,
|
||||
"SenderName": senderName,
|
||||
@@ -470,7 +470,7 @@ func getNotificationEmailBody(recipient *model.User, post *model.Post, channel *
|
||||
bodyText = translateFunc("app.notification.body.intro.notification.generic", map[string]interface{}{
|
||||
"SenderName": senderName,
|
||||
})
|
||||
info = translateFunc("app.notification.body.text.notification.generic",
|
||||
info = utils.TranslateAsHtml(translateFunc, "app.notification.body.text.notification.generic",
|
||||
map[string]interface{}{
|
||||
"Hour": t.Hour,
|
||||
"Minute": t.Minute,
|
||||
@@ -482,7 +482,7 @@ func getNotificationEmailBody(recipient *model.User, post *model.Post, channel *
|
||||
}
|
||||
|
||||
bodyPage.Props["BodyText"] = bodyText
|
||||
bodyPage.Html["Info"] = template.HTML(info)
|
||||
bodyPage.Html["Info"] = info
|
||||
bodyPage.Props["Button"] = translateFunc("api.templates.post_body.button")
|
||||
|
||||
return bodyPage.Render()
|
||||
|
||||
@@ -93,8 +93,8 @@ func (t *HTMLTemplate) addDefaultProps() {
|
||||
t.Props["Organization"] = ""
|
||||
}
|
||||
|
||||
t.Html["EmailInfo"] = template.HTML(localT("api.templates.email_info",
|
||||
map[string]interface{}{"SupportEmail": Cfg.SupportSettings.SupportEmail, "SiteName": Cfg.TeamSettings.SiteName}))
|
||||
t.Html["EmailInfo"] = TranslateAsHtml(localT, "api.templates.email_info",
|
||||
map[string]interface{}{"SupportEmail": Cfg.SupportSettings.SupportEmail, "SiteName": Cfg.TeamSettings.SiteName})
|
||||
}
|
||||
|
||||
func (t *HTMLTemplate) Render() string {
|
||||
@@ -116,5 +116,26 @@ func (t *HTMLTemplate) RenderToWriter(w http.ResponseWriter) error {
|
||||
l4g.Error(T("api.api.render.error"), t.TemplateName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TranslateAsHtml(t i18n.TranslateFunc, translationID string, args map[string]interface{}) template.HTML {
|
||||
return template.HTML(t(translationID, escapeForHtml(args)))
|
||||
}
|
||||
|
||||
func escapeForHtml(arg interface{}) interface{} {
|
||||
switch typedArg := arg.(type) {
|
||||
case string:
|
||||
return template.HTMLEscapeString(typedArg)
|
||||
case map[string]interface{}:
|
||||
safeArg := make(map[string]interface{}, len(typedArg))
|
||||
for key, value := range typedArg {
|
||||
safeArg[key] = escapeForHtml(value)
|
||||
}
|
||||
return safeArg
|
||||
default:
|
||||
l4g.Warn("Unable to escape value for HTML template %v", arg)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
53
utils/html_test.go
Обычный файл
53
utils/html_test.go
Обычный файл
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTranslateAsHtml(t *testing.T) {
|
||||
TranslationsPreInit()
|
||||
|
||||
translateFunc := TfuncWithFallback("en")
|
||||
|
||||
expected := "To finish updating your email address for YOUR TEAM HERE, please click the link below to confirm this is the right address."
|
||||
if actual := TranslateAsHtml(translateFunc, "api.templates.email_change_verify_body.info",
|
||||
map[string]interface{}{"TeamDisplayName": "YOUR TEAM HERE"}); actual != template.HTML(expected) {
|
||||
t.Fatalf("Incorrectly translated template, got %v, expected %v", actual, expected)
|
||||
}
|
||||
|
||||
expected = "To finish updating your email address for <b>YOUR TEAM HERE</b>, please click the link below to confirm this is the right address."
|
||||
if actual := TranslateAsHtml(translateFunc, "api.templates.email_change_verify_body.info",
|
||||
map[string]interface{}{"TeamDisplayName": "<b>YOUR TEAM HERE</b>"}); actual != template.HTML(expected) {
|
||||
t.Fatalf("Incorrectly translated template, got %v, expected %v", actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEscapeForHtml(t *testing.T) {
|
||||
input := "abc"
|
||||
expected := "abc"
|
||||
if actual := escapeForHtml(input).(string); actual != expected {
|
||||
t.Fatalf("incorrectly escaped %v, got %v expected %v", input, actual, expected)
|
||||
}
|
||||
|
||||
input = "<b>abc</b>"
|
||||
expected = "<b>abc</b>"
|
||||
if actual := escapeForHtml(input).(string); actual != expected {
|
||||
t.Fatalf("incorrectly escaped %v, got %v expected %v", input, actual, expected)
|
||||
}
|
||||
|
||||
inputMap := map[string]interface{}{
|
||||
"abc": "abc",
|
||||
"123": "<b>123</b>",
|
||||
}
|
||||
expectedMap := map[string]interface{}{
|
||||
"abc": "abc",
|
||||
"123": "<b>123</b>",
|
||||
}
|
||||
if actualMap := escapeForHtml(inputMap).(map[string]interface{}); actualMap["abc"] != expectedMap["abc"] || actualMap["123"] != expectedMap["123"] {
|
||||
t.Fatalf("incorrectly escaped %v, got %v expected %v", inputMap, actualMap, expectedMap)
|
||||
}
|
||||
}
|
||||
Ссылка в новой задаче
Block a user