Merge pull request #697 from trheyi/main
Fix trans passing bug in SUI components
This commit is contained in:
commit
f23ebada01
7 changed files with 422 additions and 242 deletions
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/PuerkitoBio/goquery"
|
"github.com/PuerkitoBio/goquery"
|
||||||
|
"github.com/yaoapp/kun/log"
|
||||||
"golang.org/x/net/html"
|
"golang.org/x/net/html"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -38,6 +39,7 @@ func (page *Page) Build(ctx *BuildContext, option *BuildOption) (*goquery.Docume
|
||||||
|
|
||||||
ctx.sequence++
|
ctx.sequence++
|
||||||
|
|
||||||
|
page.transCtx = NewTranslateContext()
|
||||||
namespace := Namespace(page.Route, ctx.sequence, option.ScriptMinify)
|
namespace := Namespace(page.Route, ctx.sequence, option.ScriptMinify)
|
||||||
page.namespace = namespace
|
page.namespace = namespace
|
||||||
|
|
||||||
|
|
@ -73,8 +75,7 @@ func (page *Page) Build(ctx *BuildContext, option *BuildOption) (*goquery.Docume
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the translation marks
|
// Add the translation marks
|
||||||
sequence := 1
|
err = page.TranslateDocument(doc)
|
||||||
err = page.TranslateMarks(ctx, doc, &sequence)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, warnings, err
|
return nil, warnings, err
|
||||||
}
|
}
|
||||||
|
|
@ -85,21 +86,25 @@ func (page *Page) Build(ctx *BuildContext, option *BuildOption) (*goquery.Docume
|
||||||
if script.Source == "" {
|
if script.Source == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
trans, keys, err := page.translateScript(script.Source, &sequence)
|
trans, keys, err := page.translateScript(script.Source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ctx.warnings, err
|
return nil, ctx.warnings, err
|
||||||
}
|
}
|
||||||
if len(keys) > 0 {
|
if len(keys) > 0 {
|
||||||
ctx.translations = append(ctx.translations, trans...)
|
page.transCtx.translations = append(page.transCtx.translations, trans...)
|
||||||
scripts[i].Attrs = append(script.Attrs, html.Attribute{Key: "s:trans-script", Val: strings.Join(keys, ",")})
|
scripts[i].Attrs = append(script.Attrs, html.Attribute{Key: "s:trans-script", Val: strings.Join(keys, ",")})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctx.translations == nil {
|
||||||
|
ctx.translations = []Translation{}
|
||||||
|
}
|
||||||
|
ctx.translations = append(ctx.translations, page.transCtx.translations...)
|
||||||
|
|
||||||
// Append the scripts and styles
|
// Append the scripts and styles
|
||||||
ctx.scripts = append(ctx.scripts, scripts...)
|
ctx.scripts = append(ctx.scripts, scripts...)
|
||||||
ctx.styles = append(ctx.styles, styles...)
|
ctx.styles = append(ctx.styles, styles...)
|
||||||
|
|
||||||
return doc, ctx.warnings, err
|
return doc, ctx.warnings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,6 +139,7 @@ func (page *Page) BuildAsComponent(sel *goquery.Selection, ctx *BuildContext, op
|
||||||
|
|
||||||
namespace := Namespace(name, ctx.sequence, option.ScriptMinify)
|
namespace := Namespace(name, ctx.sequence, option.ScriptMinify)
|
||||||
component := ComponentName(name, option.ScriptMinify)
|
component := ComponentName(name, option.ScriptMinify)
|
||||||
|
page.transCtx = NewTranslateContext()
|
||||||
page.namespace = namespace
|
page.namespace = namespace
|
||||||
attrs := []html.Attribute{
|
attrs := []html.Attribute{
|
||||||
{Key: "s:ns", Val: namespace},
|
{Key: "s:ns", Val: namespace},
|
||||||
|
|
@ -142,6 +148,11 @@ func (page *Page) BuildAsComponent(sel *goquery.Selection, ctx *BuildContext, op
|
||||||
{Key: "s:parent", Val: page.parent.namespace},
|
{Key: "s:parent", Val: page.parent.namespace},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := page.parent.TranslateSelection(sel) // Translate the component instance
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
ctx.sequence++
|
ctx.sequence++
|
||||||
var opt = *option
|
var opt = *option
|
||||||
opt.IgnoreDocument = true
|
opt.IgnoreDocument = true
|
||||||
|
|
@ -178,17 +189,18 @@ func (page *Page) BuildAsComponent(sel *goquery.Selection, ctx *BuildContext, op
|
||||||
|
|
||||||
// Pass the component props
|
// Pass the component props
|
||||||
first := body.Children().First()
|
first := body.Children().First()
|
||||||
|
// page.copyProps(ctx, sel, first, attrs...)
|
||||||
page.copyProps(ctx, sel, first, attrs...)
|
page.parseProps(sel, first, attrs...)
|
||||||
page.copySlots(sel, first)
|
page.copySlots(sel, first)
|
||||||
page.copyChildren(sel, first)
|
page.copyChildren(sel, first)
|
||||||
page.buildComponents(doc, ctx, &opt)
|
page.buildComponents(doc, ctx, &opt)
|
||||||
data := Data{"$props": page.Attrs}
|
page.replaceProps(first)
|
||||||
data.ReplaceSelectionUse(slotRe, first)
|
|
||||||
|
// data := Data{"$props": page.Attrs}
|
||||||
|
// data.ReplaceSelectionUse(slotRe, first)
|
||||||
|
|
||||||
// Add the translation marks
|
// Add the translation marks
|
||||||
sequence := 1
|
err = page.TranslateDocument(doc)
|
||||||
err = page.TranslateMarks(ctx, doc, &sequence)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
@ -199,12 +211,12 @@ func (page *Page) BuildAsComponent(sel *goquery.Selection, ctx *BuildContext, op
|
||||||
if script.Source == "" {
|
if script.Source == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
trans, keys, err := page.translateScript(script.Source, &sequence)
|
trans, keys, err := page.translateScript(script.Source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if len(keys) > 0 {
|
if len(keys) > 0 {
|
||||||
ctx.translations = append(ctx.translations, trans...)
|
page.transCtx.translations = append(page.transCtx.translations, trans...)
|
||||||
scripts[i].Attrs = append(script.Attrs, html.Attribute{Key: "s:trans-script", Val: strings.Join(keys, ",")})
|
scripts[i].Attrs = append(script.Attrs, html.Attribute{Key: "s:trans-script", Val: strings.Join(keys, ",")})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -214,11 +226,7 @@ func (page *Page) BuildAsComponent(sel *goquery.Selection, ctx *BuildContext, op
|
||||||
ctx.scripts = append(ctx.scripts, scripts...)
|
ctx.scripts = append(ctx.scripts, scripts...)
|
||||||
ctx.styles = append(ctx.styles, styles...)
|
ctx.styles = append(ctx.styles, styles...)
|
||||||
|
|
||||||
source, err = body.Html()
|
sel.ReplaceWithSelection(body.Contents())
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
sel.ReplaceWithHtml(source)
|
|
||||||
ctx.components[page.Route] = true
|
ctx.components[page.Route] = true
|
||||||
return source, nil
|
return source, nil
|
||||||
}
|
}
|
||||||
|
|
@ -258,54 +266,145 @@ func (page *Page) copyChildren(from *goquery.Selection, to *goquery.Selection) e
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (page *Page) copyProps(ctx *BuildContext, from *goquery.Selection, to *goquery.Selection, extra ...html.Attribute) error {
|
func (page *Page) parseProps(from *goquery.Selection, to *goquery.Selection, extra ...html.Attribute) {
|
||||||
attrs := from.Get(0).Attr
|
attrs := from.Get(0).Attr
|
||||||
prefix := "s:prop"
|
if page.props == nil {
|
||||||
if page.Attrs == nil {
|
page.props = map[string]PageProp{}
|
||||||
page.Attrs = map[string]string{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if attrs == nil {
|
||||||
|
attrs = []html.Attribute{}
|
||||||
|
}
|
||||||
|
|
||||||
for _, attr := range attrs {
|
for _, attr := range attrs {
|
||||||
|
|
||||||
if strings.HasPrefix(attr.Key, "s:") || attr.Key == "is" || attr.Key == "parsed" {
|
if strings.HasPrefix(attr.Key, "s:") || attr.Key == "is" || attr.Key == "parsed" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(attr.Key, "...$props") {
|
if attr.Key == "...$props" && page.parent != nil {
|
||||||
data := Data{"$props": page.parent.Attrs}
|
if page.parent.props != nil {
|
||||||
val, err := data.Exec(fmt.Sprintf("{{ %s }}", attr.Key[3:]))
|
for key, prop := range page.parent.props {
|
||||||
if err != nil {
|
page.props[key] = prop
|
||||||
ctx.warnings = append(ctx.warnings, err.Error())
|
|
||||||
setError(to, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
switch value := val.(type) {
|
|
||||||
case map[string]string:
|
|
||||||
for key, value := range value {
|
|
||||||
page.Attrs[key] = value
|
|
||||||
key = fmt.Sprintf("%s:%s", prefix, key)
|
|
||||||
to.SetAttr(key, value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
val := attr.Val
|
if strings.HasPrefix(attr.Key, "...") && page.parent != nil {
|
||||||
if strings.HasPrefix(attr.Key, `...\$props`) {
|
val := attr.Key[3:]
|
||||||
val = fmt.Sprintf("{{ $props.%s }}", attr.Key[9:])
|
key := fmt.Sprintf("s:prop:%s", attr.Key)
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(attr.Key, "...") {
|
|
||||||
val = attr.Key[3:]
|
|
||||||
}
|
|
||||||
page.Attrs[attr.Key] = val
|
|
||||||
key := fmt.Sprintf("%s:%s", prefix, attr.Key)
|
|
||||||
to.SetAttr(key, val)
|
to.SetAttr(key, val)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(extra) > 0 {
|
trans := from.AttrOr(fmt.Sprintf("s:trans-attr-%s", attr.Key), "")
|
||||||
|
exp := stmtRe.Match([]byte(attr.Val))
|
||||||
|
prop := PageProp{Key: attr.Key, Val: attr.Val, Trans: trans, Exp: exp}
|
||||||
|
page.props[attr.Key] = prop
|
||||||
|
}
|
||||||
|
|
||||||
|
if extra != nil && len(extra) > 0 {
|
||||||
for _, attr := range extra {
|
for _, attr := range extra {
|
||||||
|
attrs = append(attrs, attr)
|
||||||
to.SetAttr(attr.Key, attr.Val)
|
to.SetAttr(attr.Key, attr.Val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (page *Page) replaceProps(sel *goquery.Selection) error {
|
||||||
|
if page.props == nil || len(page.props) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
data := Data{}
|
||||||
|
for key, prop := range page.props {
|
||||||
|
data[key] = prop.Val
|
||||||
|
}
|
||||||
|
data["$props"] = data
|
||||||
|
return page.replacePropsNode(data, sel.Nodes[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (page *Page) replacePropsText(text string, data Data) (string, []string) {
|
||||||
|
trans := []string{}
|
||||||
|
matched := PropFindAllStringSubmatch(text)
|
||||||
|
for _, match := range matched {
|
||||||
|
stmt := match[1]
|
||||||
|
val, err := data.ExecString(stmt)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("[replaceProps] Replace %s: %s", stmt, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
text = strings.ReplaceAll(text, match[0], val)
|
||||||
|
vars := PropGetVarNames(stmt)
|
||||||
|
for _, v := range vars {
|
||||||
|
if v == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if prop, has := page.props[v]; has {
|
||||||
|
if prop.Trans == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
trans = append(trans, prop.Trans)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return text, trans
|
||||||
|
}
|
||||||
|
|
||||||
|
func (page *Page) replacePropsNode(data Data, node *html.Node) error {
|
||||||
|
switch node.Type {
|
||||||
|
case html.TextNode:
|
||||||
|
text := node.Data
|
||||||
|
if strings.TrimSpace(text) == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
text, trans := page.replacePropsText(text, data)
|
||||||
|
if len(trans) > 0 && node.Parent != nil {
|
||||||
|
node.Parent.Attr = append(node.Parent.Attr, html.Attribute{
|
||||||
|
Key: "s:trans-text",
|
||||||
|
Val: strings.Join(trans, ","),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
node.Data = text
|
||||||
|
break
|
||||||
|
|
||||||
|
case html.ElementNode:
|
||||||
|
|
||||||
|
// Attrs
|
||||||
|
attrs := []html.Attribute{}
|
||||||
|
for i, attr := range node.Attr {
|
||||||
|
|
||||||
|
if (strings.HasPrefix(attr.Key, "s:") || attr.Key == "is") && !allowUsePropAttrs[attr.Key] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val, trans := page.replacePropsText(attr.Val, data)
|
||||||
|
node.Attr[i] = html.Attribute{Key: attr.Key, Val: val}
|
||||||
|
if len(trans) > 0 {
|
||||||
|
key := fmt.Sprintf("s:trans-attr-%s", attr.Key)
|
||||||
|
attrs = append(attrs, html.Attribute{Key: key, Val: strings.Join(trans, ",")})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, attr := range attrs {
|
||||||
|
node.Attr = append(node.Attr, attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Children
|
||||||
|
for c := node.FirstChild; c != nil; c = c.NextSibling {
|
||||||
|
err := page.replacePropsNode(data, c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -562,179 +661,3 @@ func addTabToEachLine(input string, prefix ...string) string {
|
||||||
|
|
||||||
return strings.Join(lines, "\n")
|
return strings.Join(lines, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TranslateMarks add the translation marks to the document
|
|
||||||
func (page *Page) TranslateMarks(ctx *BuildContext, doc *goquery.Document, sequence *int) error {
|
|
||||||
|
|
||||||
if doc.Length() == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx == nil {
|
|
||||||
ctx = NewBuildContext(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.translations == nil {
|
|
||||||
ctx.translations = []Translation{}
|
|
||||||
}
|
|
||||||
|
|
||||||
root := doc.First()
|
|
||||||
translations, err := page.translateNode(root.Nodes[0], sequence)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if translations != nil {
|
|
||||||
ctx.translations = append(ctx.translations, translations...)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (page *Page) translateNode(node *html.Node, sequence *int) ([]Translation, error) {
|
|
||||||
|
|
||||||
translations := []Translation{}
|
|
||||||
|
|
||||||
switch node.Type {
|
|
||||||
case html.DocumentNode:
|
|
||||||
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
|
||||||
trans, err := page.translateNode(child, sequence)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
translations = append(translations, trans...)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
case html.ElementNode:
|
|
||||||
|
|
||||||
sel := goquery.NewDocumentFromNode(node)
|
|
||||||
// Script
|
|
||||||
if node.Data == "script" {
|
|
||||||
if _, has := sel.Attr("s:trans-script"); has {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
code := goquery.NewDocumentFromNode(node).Text()
|
|
||||||
trans, keys, err := page.translateScript(code, sequence)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(keys) > 0 {
|
|
||||||
raw := strings.Join(keys, ",")
|
|
||||||
sel.SetAttr("s:trans-script", raw)
|
|
||||||
translations = append(translations, trans...)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, attr := range node.Attr {
|
|
||||||
|
|
||||||
if _, has := sel.Attr("s:trans-attr-" + attr.Key); has {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
trans, keys, err := page.translateText(attr.Val, sequence, "attr")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(keys) > 0 {
|
|
||||||
raw := strings.Join(keys, ",")
|
|
||||||
sel.SetAttr("s:trans-attr-"+attr.Key, raw)
|
|
||||||
translations = append(translations, trans...)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Node Attributes
|
|
||||||
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
|
||||||
trans, err := page.translateNode(child, sequence)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
translations = append(translations, trans...)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
case html.TextNode:
|
|
||||||
parentSel := goquery.NewDocumentFromNode(node.Parent)
|
|
||||||
if _, has := parentSel.Attr("s:trans"); has {
|
|
||||||
if _, has := parentSel.Attr("s:trans-node"); has {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
key := TranslationKey(page.Route, *sequence)
|
|
||||||
message := strings.TrimSpace(node.Data)
|
|
||||||
if message != "" {
|
|
||||||
translations = append(translations, Translation{
|
|
||||||
Key: key,
|
|
||||||
Message: message,
|
|
||||||
Type: "text",
|
|
||||||
})
|
|
||||||
parentSel.SetAttr("s:trans-node", key)
|
|
||||||
*sequence = *sequence + 1
|
|
||||||
}
|
|
||||||
parentSel.SetAttr("s:trans-escape", "true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, has := parentSel.Attr("s:trans-text"); has {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
trans, keys, err := page.translateText(node.Data, sequence, "text")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(keys) > 0 {
|
|
||||||
raw := strings.Join(keys, ",")
|
|
||||||
parentSel.SetAttr("s:trans-text", raw)
|
|
||||||
translations = append(translations, trans...)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return translations, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (page *Page) translateText(text string, sequence *int, transType string) ([]Translation, []string, error) {
|
|
||||||
translations := []Translation{}
|
|
||||||
matches := stmtRe.FindAllStringSubmatch(text, -1)
|
|
||||||
keys := []string{}
|
|
||||||
for _, match := range matches {
|
|
||||||
text := strings.TrimSpace(match[1])
|
|
||||||
transMatches := transStmtReSingle.FindAllStringSubmatch(text, -1)
|
|
||||||
if len(transMatches) == 0 {
|
|
||||||
transMatches = transStmtReDouble.FindAllStringSubmatch(text, -1)
|
|
||||||
}
|
|
||||||
for _, transMatch := range transMatches {
|
|
||||||
message := strings.TrimSpace(transMatch[1])
|
|
||||||
key := TranslationKey(page.Route, *sequence)
|
|
||||||
keys = append(keys, key)
|
|
||||||
translations = append(translations, Translation{
|
|
||||||
Key: key,
|
|
||||||
Message: message,
|
|
||||||
Type: transType,
|
|
||||||
})
|
|
||||||
*sequence = *sequence + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return translations, keys, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (page *Page) translateScript(code string, sequence *int) ([]Translation, []string, error) {
|
|
||||||
|
|
||||||
translations := []Translation{}
|
|
||||||
keys := []string{}
|
|
||||||
if code == "" {
|
|
||||||
return translations, keys, nil
|
|
||||||
}
|
|
||||||
matches := transFuncRe.FindAllStringSubmatch(code, -1)
|
|
||||||
for _, match := range matches {
|
|
||||||
key := TranslationKey(page.Route, *sequence)
|
|
||||||
translations = append(translations, Translation{
|
|
||||||
Key: key,
|
|
||||||
Message: match[1],
|
|
||||||
Type: "script",
|
|
||||||
})
|
|
||||||
*sequence = *sequence + 1
|
|
||||||
keys = append(keys, key)
|
|
||||||
}
|
|
||||||
return translations, keys, nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,14 @@ func NewBuildContext(global *GlobalBuildContext) *BuildContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewTranslateContext create a new translate context
|
||||||
|
func NewTranslateContext() *TranslateContext {
|
||||||
|
return &TranslateContext{
|
||||||
|
sequence: 1,
|
||||||
|
translations: []Translation{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NewGlobalBuildContext create a new global build context
|
// NewGlobalBuildContext create a new global build context
|
||||||
func NewGlobalBuildContext() *GlobalBuildContext {
|
func NewGlobalBuildContext() *GlobalBuildContext {
|
||||||
return &GlobalBuildContext{
|
return &GlobalBuildContext{
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,9 @@ import (
|
||||||
|
|
||||||
// If set the map value, should keep the space at the end of the statement
|
// If set the map value, should keep the space at the end of the statement
|
||||||
var stmtRe = regexp.MustCompile(`\{\{([\s\S]*?)\}\}`)
|
var stmtRe = regexp.MustCompile(`\{\{([\s\S]*?)\}\}`)
|
||||||
var propRe = regexp.MustCompile(`\[\{([\s\S]*?)\}\]`)
|
var propRe = regexp.MustCompile(`\[\{([\s\S]*?)\}\]`) // [{ xxx }] will be deprecated
|
||||||
|
var propNewRe = regexp.MustCompile(`\{%([\s\S]*)?%\}`) // {% xxx %}
|
||||||
|
var propVarNameRe = regexp.MustCompile(`(?:\$props\.)?(?:$begin:math:display$'([^']+)'$end:math:display$|(\w+))`)
|
||||||
|
|
||||||
// Data data for the template
|
// Data data for the template
|
||||||
type Data map[string]interface{}
|
type Data map[string]interface{}
|
||||||
|
|
@ -184,3 +186,29 @@ func _process(args ...any) (interface{}, error) {
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PropFindAllStringSubmatch find all string submatch
|
||||||
|
func PropFindAllStringSubmatch(value string) [][]string {
|
||||||
|
matched := propNewRe.FindAllStringSubmatch(value, -1)
|
||||||
|
oldVersion := propRe.FindAllStringSubmatch(value, -1) // will be deprecated
|
||||||
|
if len(oldVersion) > 0 {
|
||||||
|
matched = append(matched, oldVersion...)
|
||||||
|
}
|
||||||
|
return matched
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropGetVarNames get the variable names
|
||||||
|
func PropGetVarNames(value string) []string {
|
||||||
|
matched := propVarNameRe.FindAllStringSubmatch(value, -1)
|
||||||
|
varNames := []string{}
|
||||||
|
for _, m := range matched {
|
||||||
|
m1 := strings.TrimSpace(m[1])
|
||||||
|
m2 := strings.TrimSpace(m[2])
|
||||||
|
if m1 != "" {
|
||||||
|
varNames = append(varNames, m1)
|
||||||
|
} else {
|
||||||
|
varNames = append(varNames, m2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return varNames
|
||||||
|
}
|
||||||
|
|
|
||||||
205
sui/core/translate.go
Normal file
205
sui/core/translate.go
Normal file
|
|
@ -0,0 +1,205 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/PuerkitoBio/goquery"
|
||||||
|
"golang.org/x/net/html"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TranslateDocument translates the document
|
||||||
|
func (page *Page) TranslateDocument(doc *goquery.Document) error {
|
||||||
|
|
||||||
|
if doc.Length() == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if page.transCtx == nil {
|
||||||
|
return fmt.Errorf("TranslateMarks: context is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if page.transCtx.translations == nil {
|
||||||
|
page.transCtx.translations = []Translation{}
|
||||||
|
}
|
||||||
|
|
||||||
|
root := doc.First()
|
||||||
|
return page.TranslateSelection(root)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TranslateSelection translates the selection
|
||||||
|
func (page *Page) TranslateSelection(sel *goquery.Selection) error {
|
||||||
|
|
||||||
|
if sel.Length() == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if page.transCtx == nil {
|
||||||
|
return fmt.Errorf("TranslateMarks: context is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if page.transCtx.translations == nil {
|
||||||
|
page.transCtx.translations = []Translation{}
|
||||||
|
}
|
||||||
|
|
||||||
|
translations, err := page.translateNode(sel.Nodes[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if translations != nil {
|
||||||
|
page.transCtx.translations = append(page.transCtx.translations, translations...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (page *Page) translateNode(node *html.Node) ([]Translation, error) {
|
||||||
|
|
||||||
|
translations := []Translation{}
|
||||||
|
|
||||||
|
switch node.Type {
|
||||||
|
case html.DocumentNode:
|
||||||
|
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
||||||
|
trans, err := page.translateNode(child)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
translations = append(translations, trans...)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case html.ElementNode:
|
||||||
|
|
||||||
|
sel := goquery.NewDocumentFromNode(node)
|
||||||
|
// Script
|
||||||
|
if node.Data == "script" {
|
||||||
|
if _, has := sel.Attr("s:trans-script"); has {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
code := goquery.NewDocumentFromNode(node).Text()
|
||||||
|
trans, keys, err := page.translateScript(code)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(keys) > 0 {
|
||||||
|
raw := strings.Join(keys, ",")
|
||||||
|
sel.SetAttr("s:trans-script", raw)
|
||||||
|
translations = append(translations, trans...)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, attr := range node.Attr {
|
||||||
|
|
||||||
|
if _, has := sel.Attr("s:trans-attr-" + attr.Key); has {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
trans, keys, err := page.translateText(attr.Val, "attr")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(keys) > 0 {
|
||||||
|
raw := strings.Join(keys, ",")
|
||||||
|
sel.SetAttr("s:trans-attr-"+attr.Key, raw)
|
||||||
|
translations = append(translations, trans...)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node Attributes
|
||||||
|
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
||||||
|
trans, err := page.translateNode(child)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
translations = append(translations, trans...)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case html.TextNode:
|
||||||
|
parentSel := goquery.NewDocumentFromNode(node.Parent)
|
||||||
|
if _, has := parentSel.Attr("s:trans"); has {
|
||||||
|
if _, has := parentSel.Attr("s:trans-node"); has {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
key := TranslationKey(page.Route, page.transCtx.sequence)
|
||||||
|
message := strings.TrimSpace(node.Data)
|
||||||
|
if message != "" {
|
||||||
|
translations = append(translations, Translation{
|
||||||
|
Key: key,
|
||||||
|
Message: message,
|
||||||
|
Type: "text",
|
||||||
|
})
|
||||||
|
parentSel.SetAttr("s:trans-node", key)
|
||||||
|
page.transCtx.sequence = page.transCtx.sequence + 1
|
||||||
|
}
|
||||||
|
parentSel.SetAttr("s:trans-escape", "true")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, has := parentSel.Attr("s:trans-text"); has {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
trans, keys, err := page.translateText(node.Data, "text")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(keys) > 0 {
|
||||||
|
raw := strings.Join(keys, ",")
|
||||||
|
parentSel.SetAttr("s:trans-text", raw)
|
||||||
|
translations = append(translations, trans...)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return translations, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (page *Page) translateText(text string, transType string) ([]Translation, []string, error) {
|
||||||
|
translations := []Translation{}
|
||||||
|
matches := stmtRe.FindAllStringSubmatch(text, -1)
|
||||||
|
keys := []string{}
|
||||||
|
for _, match := range matches {
|
||||||
|
text := strings.TrimSpace(match[1])
|
||||||
|
transMatches := transStmtReSingle.FindAllStringSubmatch(text, -1)
|
||||||
|
if len(transMatches) == 0 {
|
||||||
|
transMatches = transStmtReDouble.FindAllStringSubmatch(text, -1)
|
||||||
|
}
|
||||||
|
for _, transMatch := range transMatches {
|
||||||
|
message := strings.TrimSpace(transMatch[1])
|
||||||
|
key := TranslationKey(page.Route, page.transCtx.sequence)
|
||||||
|
keys = append(keys, key)
|
||||||
|
translations = append(translations, Translation{
|
||||||
|
Key: key,
|
||||||
|
Message: message,
|
||||||
|
Type: transType,
|
||||||
|
})
|
||||||
|
page.transCtx.sequence = page.transCtx.sequence + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return translations, keys, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (page *Page) translateScript(code string) ([]Translation, []string, error) {
|
||||||
|
|
||||||
|
translations := []Translation{}
|
||||||
|
keys := []string{}
|
||||||
|
if code == "" {
|
||||||
|
return translations, keys, nil
|
||||||
|
}
|
||||||
|
matches := transFuncRe.FindAllStringSubmatch(code, -1)
|
||||||
|
for _, match := range matches {
|
||||||
|
key := TranslationKey(page.Route, page.transCtx.sequence)
|
||||||
|
translations = append(translations, Translation{
|
||||||
|
Key: key,
|
||||||
|
Message: match[1],
|
||||||
|
Type: "script",
|
||||||
|
})
|
||||||
|
page.transCtx.sequence = page.transCtx.sequence + 1
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
return translations, keys, nil
|
||||||
|
}
|
||||||
|
|
@ -43,7 +43,17 @@ type Page struct {
|
||||||
Attrs map[string]string `json:"-"`
|
Attrs map[string]string `json:"-"`
|
||||||
Attributes []html.Attribute `json:"-"`
|
Attributes []html.Attribute `json:"-"`
|
||||||
namespace string `json:"-"`
|
namespace string `json:"-"`
|
||||||
|
transCtx *TranslateContext `json:"-"`
|
||||||
parent *Page `json:"-"`
|
parent *Page `json:"-"`
|
||||||
|
props map[string]PageProp `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PageProp is the struct for the page prop
|
||||||
|
type PageProp struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Val string `json:"val"`
|
||||||
|
Exp bool `json:"exp"`
|
||||||
|
Trans string `json:"trans"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildContext is the struct for the build context
|
// BuildContext is the struct for the build context
|
||||||
|
|
@ -63,6 +73,12 @@ type BuildContext struct {
|
||||||
stack []string // Stack to manage build states
|
stack []string // Stack to manage build states
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TranslateContext is the struct for the translate context
|
||||||
|
type TranslateContext struct {
|
||||||
|
sequence int
|
||||||
|
translations []Translation
|
||||||
|
}
|
||||||
|
|
||||||
// ScriptNode is the struct for the script node
|
// ScriptNode is the struct for the script node
|
||||||
type ScriptNode struct {
|
type ScriptNode struct {
|
||||||
Source string `json:"source"`
|
Source string `json:"source"`
|
||||||
|
|
|
||||||
|
|
@ -559,13 +559,13 @@ func (page *Page) writeLocaleFiles(ctx *core.BuildContext, data map[string]inter
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
components := ctx.GetComponents()
|
||||||
translations := ctx.GetTranslations()
|
translations := ctx.GetTranslations()
|
||||||
if len(translations) == 0 {
|
if len(translations) == 0 && len(components) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
prefix := core.TranslationKeyPrefix(page.Route)
|
prefix := core.TranslationKeyPrefix(page.Route)
|
||||||
files := page.localeFiles(data)
|
files := page.localeFiles(data)
|
||||||
components := ctx.GetComponents()
|
|
||||||
for name, file := range files {
|
for name, file := range files {
|
||||||
locale := page.tmpl.getLocale(name, page.Route)
|
locale := page.tmpl.getLocale(name, page.Route)
|
||||||
locale.MergeTranslations(translations, prefix)
|
locale.MergeTranslations(translations, prefix)
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,9 @@ func TestGetTemplates(t *testing.T) {
|
||||||
assert.Equal(t, "advanced", testTmpls[0].(*Template).ID)
|
assert.Equal(t, "advanced", testTmpls[0].(*Template).ID)
|
||||||
assert.Equal(t, "The advanced template", testTmpls[0].(*Template).Name)
|
assert.Equal(t, "The advanced template", testTmpls[0].(*Template).Name)
|
||||||
assert.Len(t, testTmpls[0].Themes(), 2)
|
assert.Len(t, testTmpls[0].Themes(), 2)
|
||||||
assert.Len(t, testTmpls[0].Locales(), 4)
|
assert.Len(t, testTmpls[0].Locales(), 5)
|
||||||
assert.Len(t, testTmpls[0].(*Template).Template.Themes, 2)
|
assert.Len(t, testTmpls[0].(*Template).Template.Themes, 2)
|
||||||
assert.Len(t, testTmpls[0].(*Template).Template.Locales, 4)
|
assert.Len(t, testTmpls[0].(*Template).Template.Locales, 5)
|
||||||
|
|
||||||
// Basic Template
|
// Basic Template
|
||||||
assert.Equal(t, "basic", testTmpls[1].(*Template).ID)
|
assert.Equal(t, "basic", testTmpls[1].(*Template).ID)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue