diff --git a/sui/core/build.go b/sui/core/build.go
index 372108d1..a2f4a9af 100644
--- a/sui/core/build.go
+++ b/sui/core/build.go
@@ -7,15 +7,11 @@ import (
"strings"
"github.com/PuerkitoBio/goquery"
- "github.com/fatih/color"
- jsoniter "github.com/json-iterator/go"
"golang.org/x/net/html"
)
var slotRe = regexp.MustCompile(`\[\{([^\}]+)\}\]`)
var cssRe = regexp.MustCompile(`([\.a-z0-9A-Z-:# ]+)\{`)
-var langFuncRe = regexp.MustCompile(`L\s*\(\s*["'](.*?)["']\s*\)`)
-var langAttrRe = regexp.MustCompile(`'::(.*?)'`)
// Build build the page
func (page *Page) Build(ctx *BuildContext, option *BuildOption) (*goquery.Document, []string, error) {
@@ -500,104 +496,3 @@ func addTabToEachLine(input string, prefix ...string) string {
return strings.Join(lines, "\n")
}
-
-func getScriptTranslation(code string, namespace string) []Translation {
- translations := []Translation{}
- matches := langFuncRe.FindAllStringSubmatch(code, -1)
- for i, match := range matches {
- translations = append(translations, Translation{
- Key: fmt.Sprintf("%s_script_%d", namespace, i),
- Message: match[1],
- Type: "script",
- })
- }
- return translations
-}
-
-func getNodeTranslation(sel *goquery.Selection, index int, namespace string) []Translation {
-
- translations := []Translation{}
- nodeType := sel.Get(0).Type
- switch nodeType {
- case html.ElementNode:
-
- // Get the translation
- if typ, has := sel.Attr("s:trans"); has {
- typ = strings.TrimSpace(typ)
- if typ == "" {
- typ = "html"
- }
-
- key := fmt.Sprintf("%s_index_%d", namespace, index)
- translations = append(translations, Translation{
- Key: key,
- Message: strings.TrimSpace(sel.Text()),
- Type: typ,
- })
- sel.SetAttr("s:trans-node", key)
- sel.RemoveAttr("s:trans")
- }
-
- // Attributes
- keys := map[string][]string{}
- has := false
- for i, attr := range sel.Get(0).Attr {
-
- keys[attr.Key] = []string{}
-
- // value="::attr"
- if strings.HasPrefix(attr.Val, "::") {
- key := fmt.Sprintf("%s_index_attr_%d_%d", namespace, index, i)
- translations = append(translations, Translation{
- Key: fmt.Sprintf("%s_index_attr_%d_%d", namespace, index, i),
- Message: attr.Val[2:],
- Name: attr.Key,
- Type: "attr",
- })
- keys[attr.Key] = append(keys[attr.Key], key)
- has = true
- }
-
- // value="{{ 'key': '::value' }}"
- matches := langAttrRe.FindAllStringSubmatch(attr.Val, -1)
- if len(matches) > 0 {
- for j, match := range matches {
- key := fmt.Sprintf("%s_index_attr_%d_%d_%d", namespace, index, i, j)
- translations = append(translations, Translation{
- Key: fmt.Sprintf("%s_index_attr_%d_%d_%d", namespace, index, i, j),
- Message: match[1],
- Name: attr.Key,
- Type: "attr",
- })
- keys[attr.Key] = append(keys[attr.Key], key)
- has = true
- }
- }
- }
-
- if has {
- raw, err := jsoniter.Marshal(keys)
- if err != nil {
- fmt.Println(color.RedString(err.Error()))
- break
- }
- sel.SetAttr("s:trans-attrs", string(raw))
- sel.RemoveAttr("s:trans")
- }
-
- case html.TextNode:
- if strings.HasPrefix(sel.Text(), "::") {
- key := fmt.Sprintf("%s_index_%d", namespace, index)
- translations = append(translations, Translation{
- Key: fmt.Sprintf("%s_index_%d", namespace, index),
- Message: strings.TrimSpace(sel.Text()[2:]),
- Type: "text",
- })
- sel.SetAttr("s:trans-node", key)
- sel.RemoveAttr("s:trans")
- }
- }
-
- return translations
-
-}
diff --git a/sui/core/compile.go b/sui/core/compile.go
index 6ab387c1..27a67fae 100644
--- a/sui/core/compile.go
+++ b/sui/core/compile.go
@@ -5,15 +5,20 @@ import (
"regexp"
"strings"
+ "github.com/PuerkitoBio/goquery"
"github.com/evanw/esbuild/pkg/api"
jsoniter "github.com/json-iterator/go"
"github.com/yaoapp/gou/runtime/transform"
"github.com/yaoapp/kun/log"
+ "golang.org/x/net/html"
)
var quoteRe = "'\"`"
var importRe = regexp.MustCompile(`import\s*\t*\n*[^;]*;`) // import { foo, bar } from 'hello'; ...
var importAssetsRe = regexp.MustCompile(`import\s*\t*\n*\s*['"]@assets\/([^'"]+)['"];`) // import '@assets/foo.js'; or import "@assets/foo.js";
+var transStmtReSingle = regexp.MustCompile(`'::([^:']+)'`)
+var transStmtReDouble = regexp.MustCompile(`"::([^:"]+)"`)
+var transFuncRe = regexp.MustCompile(`__m\s*\(\s*["'](.*?)["']\s*\)`)
// AssetsRe is the regexp for assets
var AssetsRe = regexp.MustCompile(`[` + quoteRe + `]@assets\/([^` + quoteRe + `]+)[` + quoteRe + `]`) // '@assets/foo.js' or "@assets/foo.js" or `@assets/foo`
@@ -85,6 +90,13 @@ func (page *Page) Compile(ctx *BuildContext, option *BuildOption) (string, []str
)
}
+ // Add the translation marks
+ sequence := 0
+ err = page.TranslateMarks(ctx, option, doc, &sequence)
+ if err != nil {
+ return "", warnings, err
+ }
+
page.ReplaceDocument(doc)
html, err := doc.Html()
if err != nil {
@@ -140,6 +152,13 @@ func (page *Page) CompileAsComponent(ctx *BuildContext, option *BuildOption) (st
body.Children().First().AppendHtml(fmt.Sprintf(``+"\n", rawStyles))
body.Children().First().AppendHtml(fmt.Sprintf(``+"\n", rawOption))
+ // Add the translation marks
+ sequence := 0
+ err = page.TranslateMarks(ctx, option, doc, &sequence)
+ if err != nil {
+ return "", warnings, err
+ }
+
html, err := body.Html()
return html, warnings, err
}
@@ -263,3 +282,146 @@ func (style StyleNode) HTML() string {
return ""
}
+
+// TranslateMarks add the translation marks to the document
+func (page *Page) TranslateMarks(ctx *BuildContext, option *BuildOption, doc *goquery.Document, sequence *int) error {
+
+ if doc.Length() == 0 {
+ return 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{}
+ *sequence = *sequence + 1
+
+ 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:
+
+ // Script
+ if node.Data == "script" {
+ code := goquery.NewDocumentFromNode(node).Text()
+ if code != "" {
+ translations := []Translation{}
+ matches := transFuncRe.FindAllStringSubmatch(code, -1)
+ for _, match := range matches {
+ key := Namespace(page.Route, *sequence)
+ translations = append(translations, Translation{
+ Key: key,
+ Message: match[1],
+ Type: "script",
+ })
+ *sequence = *sequence + 1
+ }
+ }
+ break
+ }
+
+ sel := goquery.NewDocumentFromNode(node)
+ for _, attr := range node.Attr {
+
+ 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 {
+ key := Namespace(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.RemoveAttr("s:trans")
+ }
+
+ 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)
+ parentSel.RemoveAttr("s:trans")
+ translations = append(translations, trans...)
+ }
+ break
+ }
+
+ return translations, nil
+}
+
+func (page *Page) translateText(text string, sequence *int, typ 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 := Namespace(page.Route, *sequence)
+ keys = append(keys, key)
+ translations = append(translations, Translation{
+ Key: key,
+ Message: message,
+ Type: typ,
+ })
+ *sequence = *sequence + 1
+ }
+ }
+ return translations, keys, nil
+}
diff --git a/sui/core/context.go b/sui/core/context.go
index 36233a38..2c3be60e 100644
--- a/sui/core/context.go
+++ b/sui/core/context.go
@@ -36,6 +36,14 @@ func (ctx *BuildContext) GetJitComponents() []string {
return jitComponents
}
+// GetTranslations get the translations
+func (ctx *BuildContext) GetTranslations() []Translation {
+ if ctx.translations == nil {
+ return []Translation{}
+ }
+ return ctx.translations
+}
+
// GetJitComponents get the just in time components
func (globalCtx *GlobalBuildContext) GetJitComponents() []string {
if globalCtx.jitComponents == nil {
diff --git a/sui/core/injections.go b/sui/core/injections.go
index 0b5d73cc..8cd804cd 100644
--- a/sui/core/injections.go
+++ b/sui/core/injections.go
@@ -70,8 +70,8 @@ const initScriptTmpl = `
`
const i118nScriptTmpl = `
- function L(key) {
- return key;
+ function __m(message) {
+ return message;
}
`
diff --git a/sui/core/types.go b/sui/core/types.go
index 5c1b11f8..518270d9 100644
--- a/sui/core/types.go
+++ b/sui/core/types.go
@@ -29,21 +29,20 @@ type Setting struct {
// Page is the struct for the page
type Page struct {
- Route string `json:"route"`
- Name string `json:"name,omitempty"`
- CacheStore string `json:"-"`
- TemplateID string `json:"-"`
- SuiID string `json:"-"`
- Config *PageConfig `json:"-"`
- Path string `json:"-"`
- Codes SourceCodes `json:"-"`
- Document []byte `json:"-"`
- GlobalData []byte `json:"-"`
- Attrs map[string]string `json:"-"`
- Attributes []html.Attribute `json:"-"`
- Translations []Translation `json:"-"` // will be deprecated
- namespace string `json:"-"`
- parent *Page `json:"-"`
+ Route string `json:"route"`
+ Name string `json:"name,omitempty"`
+ CacheStore string `json:"-"`
+ TemplateID string `json:"-"`
+ SuiID string `json:"-"`
+ Config *PageConfig `json:"-"`
+ Path string `json:"-"`
+ Codes SourceCodes `json:"-"`
+ Document []byte `json:"-"`
+ GlobalData []byte `json:"-"`
+ Attrs map[string]string `json:"-"`
+ Attributes []html.Attribute `json:"-"`
+ namespace string `json:"-"`
+ parent *Page `json:"-"`
}
// BuildContext is the struct for the build context
diff --git a/sui/storages/local/build.go b/sui/storages/local/build.go
index abd7d892..2b41d012 100644
--- a/sui/storages/local/build.go
+++ b/sui/storages/local/build.go
@@ -205,7 +205,7 @@ func (page *Page) Build(globalCtx *core.GlobalBuildContext, option *core.BuildOp
}
// Save the locale files
- err = page.writeLocaleFiles(option.Data)
+ err = page.writeLocaleFiles(ctx, option.Data)
if err != nil {
return warnings, err
}
@@ -272,7 +272,7 @@ func (page *Page) BuildAsComponent(globalCtx *core.GlobalBuildContext, option *c
}
// Save the locale files
- err = page.writeLocaleFiles(option.Data)
+ err = page.writeLocaleFiles(ctx, option.Data)
if err != nil {
return warnings, err
}
@@ -415,10 +415,14 @@ func (page *Page) locale(name string) core.Locale {
return locale
}
-func (page *Page) writeLocaleFiles(data map[string]interface{}) error {
+func (page *Page) writeLocaleFiles(ctx *core.BuildContext, data map[string]interface{}) error {
- // No translations
- if len(page.Page.Translations) == 0 {
+ if ctx == nil {
+ return nil
+ }
+
+ translations := ctx.GetTranslations()
+ if len(translations) == 0 {
return nil
}
@@ -428,7 +432,7 @@ func (page *Page) writeLocaleFiles(data map[string]interface{}) error {
// Init Data
keys := map[string]string{}
messages := map[string]string{}
- for _, t := range page.Page.Translations {
+ for _, t := range translations {
keys[t.Key] = t.Message
messages[t.Message] = t.Message
}
diff --git a/sui/storages/local/page.go b/sui/storages/local/page.go
index f94babbb..36b334b0 100644
--- a/sui/storages/local/page.go
+++ b/sui/storages/local/page.go
@@ -279,12 +279,11 @@ func (tmpl *Template) CreateEmptyPage(route string, setting *core.PageSetting) (
page := &Page{
tmpl: tmpl,
Page: &core.Page{
- Route: route,
- TemplateID: tmpl.ID,
- SuiID: tmpl.local.ID,
- Path: filepath.Join(tmpl.Root, route),
- Name: name,
- Translations: []core.Translation{},
+ Route: route,
+ TemplateID: tmpl.ID,
+ SuiID: tmpl.local.ID,
+ Path: filepath.Join(tmpl.Root, route),
+ Name: name,
Codes: core.SourceCodes{
HTML: core.Source{File: fmt.Sprintf("%s.html", name)},
CSS: core.Source{File: fmt.Sprintf("%s.css", name)},
@@ -350,12 +349,11 @@ func (tmpl *Template) getPage(route, file string) (core.IPage, error) {
return &Page{
tmpl: tmpl,
Page: &core.Page{
- Route: route,
- Path: path,
- Name: name,
- TemplateID: tmpl.ID,
- SuiID: tmpl.local.ID,
- Translations: []core.Translation{},
+ Route: route,
+ Path: path,
+ Name: name,
+ TemplateID: tmpl.ID,
+ SuiID: tmpl.local.ID,
Codes: core.SourceCodes{
HTML: core.Source{File: fmt.Sprintf("%s%s", name, filepath.Ext(file))},
CSS: core.Source{File: fmt.Sprintf("%s.css", name)},