From 8e8a0ac8853d310a0bdb70da1c2d2f42c2a23431 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 19 Jul 2024 07:20:57 +0800 Subject: [PATCH] Fix trans passing bug in SUI components --- sui/core/build.go | 210 +++---------------------------- sui/core/context.go | 8 ++ sui/core/translate.go | 205 ++++++++++++++++++++++++++++++ sui/core/types.go | 7 ++ sui/storages/local/local_test.go | 4 +- 5 files changed, 241 insertions(+), 193 deletions(-) create mode 100644 sui/core/translate.go diff --git a/sui/core/build.go b/sui/core/build.go index bccfc5df..2a0062d6 100644 --- a/sui/core/build.go +++ b/sui/core/build.go @@ -38,6 +38,7 @@ func (page *Page) Build(ctx *BuildContext, option *BuildOption) (*goquery.Docume ctx.sequence++ + page.transCtx = NewTranslateContext() namespace := Namespace(page.Route, ctx.sequence, option.ScriptMinify) page.namespace = namespace @@ -73,8 +74,7 @@ func (page *Page) Build(ctx *BuildContext, option *BuildOption) (*goquery.Docume } // Add the translation marks - sequence := 1 - err = page.TranslateMarks(ctx, doc, &sequence) + err = page.TranslateDocument(doc) if err != nil { return nil, warnings, err } @@ -85,21 +85,25 @@ func (page *Page) Build(ctx *BuildContext, option *BuildOption) (*goquery.Docume if script.Source == "" { continue } - trans, keys, err := page.translateScript(script.Source, &sequence) + trans, keys, err := page.translateScript(script.Source) if err != nil { return nil, ctx.warnings, err } 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, ",")}) } } } + if ctx.translations == nil { + ctx.translations = []Translation{} + } + ctx.translations = append(ctx.translations, page.transCtx.translations...) + // Append the scripts and styles ctx.scripts = append(ctx.scripts, scripts...) ctx.styles = append(ctx.styles, styles...) - return doc, ctx.warnings, err } @@ -134,6 +138,7 @@ func (page *Page) BuildAsComponent(sel *goquery.Selection, ctx *BuildContext, op namespace := Namespace(name, ctx.sequence, option.ScriptMinify) component := ComponentName(name, option.ScriptMinify) + page.transCtx = NewTranslateContext() page.namespace = namespace attrs := []html.Attribute{ {Key: "s:ns", Val: namespace}, @@ -142,6 +147,11 @@ func (page *Page) BuildAsComponent(sel *goquery.Selection, ctx *BuildContext, op {Key: "s:parent", Val: page.parent.namespace}, } + err := page.parent.TranslateSelection(sel) // Translate the component instance + if err != nil { + return "", err + } + ctx.sequence++ var opt = *option opt.IgnoreDocument = true @@ -178,7 +188,6 @@ func (page *Page) BuildAsComponent(sel *goquery.Selection, ctx *BuildContext, op // Pass the component props first := body.Children().First() - page.copyProps(ctx, sel, first, attrs...) page.copySlots(sel, first) page.copyChildren(sel, first) @@ -187,8 +196,7 @@ func (page *Page) BuildAsComponent(sel *goquery.Selection, ctx *BuildContext, op data.ReplaceSelectionUse(slotRe, first) // Add the translation marks - sequence := 1 - err = page.TranslateMarks(ctx, doc, &sequence) + err = page.TranslateDocument(doc) if err != nil { return "", err } @@ -199,12 +207,12 @@ func (page *Page) BuildAsComponent(sel *goquery.Selection, ctx *BuildContext, op if script.Source == "" { continue } - trans, keys, err := page.translateScript(script.Source, &sequence) + trans, keys, err := page.translateScript(script.Source) if err != nil { return "", err } 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, ",")}) } } @@ -214,11 +222,7 @@ func (page *Page) BuildAsComponent(sel *goquery.Selection, ctx *BuildContext, op ctx.scripts = append(ctx.scripts, scripts...) ctx.styles = append(ctx.styles, styles...) - source, err = body.Html() - if err != nil { - return "", err - } - sel.ReplaceWithHtml(source) + sel.ReplaceWithSelection(body.Contents()) ctx.components[page.Route] = true return source, nil } @@ -562,179 +566,3 @@ func addTabToEachLine(input string, prefix ...string) string { 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 -} diff --git a/sui/core/context.go b/sui/core/context.go index e39a3f77..f8b78b05 100644 --- a/sui/core/context.go +++ b/sui/core/context.go @@ -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 func NewGlobalBuildContext() *GlobalBuildContext { return &GlobalBuildContext{ diff --git a/sui/core/translate.go b/sui/core/translate.go new file mode 100644 index 00000000..327ac486 --- /dev/null +++ b/sui/core/translate.go @@ -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 +} diff --git a/sui/core/types.go b/sui/core/types.go index c93c2028..5324a75d 100644 --- a/sui/core/types.go +++ b/sui/core/types.go @@ -43,6 +43,7 @@ type Page struct { Attrs map[string]string `json:"-"` Attributes []html.Attribute `json:"-"` namespace string `json:"-"` + transCtx *TranslateContext `json:"-"` parent *Page `json:"-"` } @@ -63,6 +64,12 @@ type BuildContext struct { 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 type ScriptNode struct { Source string `json:"source"` diff --git a/sui/storages/local/local_test.go b/sui/storages/local/local_test.go index fa669f4b..a38caa43 100644 --- a/sui/storages/local/local_test.go +++ b/sui/storages/local/local_test.go @@ -31,9 +31,9 @@ func TestGetTemplates(t *testing.T) { assert.Equal(t, "advanced", testTmpls[0].(*Template).ID) assert.Equal(t, "The advanced template", testTmpls[0].(*Template).Name) 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.Locales, 4) + assert.Len(t, testTmpls[0].(*Template).Template.Locales, 5) // Basic Template assert.Equal(t, "basic", testTmpls[1].(*Template).ID)