Improve TrHTML and add more tests (#29228)

Follow #29165.

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
(cherry picked from commit 4345cac52971c13debfe5e6f311aef3930fe2eed)
This commit is contained in:
wxiaoguang 2024-02-18 20:15:24 +08:00 committed by Earl Warren
parent 559afdad73
commit d611fe8d26
No known key found for this signature in database
GPG key ID: 0579CB2928A78A00
2 changed files with 71 additions and 3 deletions

View file

@ -4,6 +4,7 @@
package i18n package i18n
import ( import (
"html/template"
"strings" "strings"
"testing" "testing"
@ -82,6 +83,71 @@ c=22
assert.Equal(t, "22", lang1.TrString("c")) assert.Equal(t, "22", lang1.TrString("c"))
} }
type stringerPointerReceiver struct {
s string
}
func (s *stringerPointerReceiver) String() string {
return s.s
}
type stringerStructReceiver struct {
s string
}
func (s stringerStructReceiver) String() string {
return s.s
}
type errorStructReceiver struct {
s string
}
func (e errorStructReceiver) Error() string {
return e.s
}
type errorPointerReceiver struct {
s string
}
func (e *errorPointerReceiver) Error() string {
return e.s
}
func TestLocaleWithTemplate(t *testing.T) {
ls := NewLocaleStore()
assert.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", []byte(`key=<a>%s</a>`), nil))
lang1, _ := ls.Locale("lang1")
tmpl := template.New("test").Funcs(template.FuncMap{"tr": lang1.TrHTML})
tmpl = template.Must(tmpl.Parse(`{{tr "key" .var}}`))
cases := []struct {
in any
want string
}{
{"<str>", "<a>&lt;str&gt;</a>"},
{[]byte("<bytes>"), "<a>[60 98 121 116 101 115 62]</a>"},
{template.HTML("<html>"), "<a><html></a>"},
{stringerPointerReceiver{"<stringerPointerReceiver>"}, "<a>{&lt;stringerPointerReceiver&gt;}</a>"},
{&stringerPointerReceiver{"<stringerPointerReceiver ptr>"}, "<a>&lt;stringerPointerReceiver ptr&gt;</a>"},
{stringerStructReceiver{"<stringerStructReceiver>"}, "<a>&lt;stringerStructReceiver&gt;</a>"},
{&stringerStructReceiver{"<stringerStructReceiver ptr>"}, "<a>&lt;stringerStructReceiver ptr&gt;</a>"},
{errorStructReceiver{"<errorStructReceiver>"}, "<a>&lt;errorStructReceiver&gt;</a>"},
{&errorStructReceiver{"<errorStructReceiver ptr>"}, "<a>&lt;errorStructReceiver ptr&gt;</a>"},
{errorPointerReceiver{"<errorPointerReceiver>"}, "<a>{&lt;errorPointerReceiver&gt;}</a>"},
{&errorPointerReceiver{"<errorPointerReceiver ptr>"}, "<a>&lt;errorPointerReceiver ptr&gt;</a>"},
}
buf := &strings.Builder{}
for _, c := range cases {
buf.Reset()
assert.NoError(t, tmpl.Execute(buf, map[string]any{"var": c.in}))
assert.Equal(t, c.want, buf.String())
}
}
func TestLocaleStoreQuirks(t *testing.T) { func TestLocaleStoreQuirks(t *testing.T) {
const nl = "\n" const nl = "\n"
q := func(q1, s string, q2 ...string) string { q := func(q1, s string, q2 ...string) string {

View file

@ -136,12 +136,14 @@ func (l *locale) TrHTML(trKey string, trArgs ...any) template.HTML {
args := slices.Clone(trArgs) args := slices.Clone(trArgs)
for i, v := range args { for i, v := range args {
switch v := v.(type) { switch v := v.(type) {
case nil, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, template.HTML:
// for most basic types (including template.HTML which is safe), just do nothing and use it
case string: case string:
args[i] = template.HTML(template.HTMLEscapeString(v)) args[i] = template.HTMLEscapeString(v)
case fmt.Stringer: case fmt.Stringer:
args[i] = template.HTMLEscapeString(v.String()) args[i] = template.HTMLEscapeString(v.String())
default: // int, float, include template.HTML default:
// do nothing, just use it args[i] = template.HTMLEscapeString(fmt.Sprint(v))
} }
} }
return template.HTML(l.TrString(trKey, args...)) return template.HTML(l.TrString(trKey, args...))