Skip to content

Commit

Permalink
[add] sui.trans.all and sui.trans.page to support automatic translation
Browse files Browse the repository at this point in the history
  • Loading branch information
trheyi committed Jul 15, 2024
1 parent 1ed1973 commit ef8b802
Show file tree
Hide file tree
Showing 9 changed files with 369 additions and 22 deletions.
86 changes: 86 additions & 0 deletions sui/api/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ func init() {
"build.all": BuildAll,
"build.page": BuildPage,

"trans.all": TransAll,
"trans.page": TransPage,

"sync.assetfile": SyncAssetFile, // Will be deprecated or change in the future

// Will be deprecated or change in the future
Expand Down Expand Up @@ -1018,6 +1021,89 @@ func BuildPage(process *process.Process) interface{} {
return nil
}

// TransAll handle the render page request
func TransAll(process *process.Process) interface{} {

process.ValidateArgNums(3)
sui := get(process)
templateID := process.ArgsString(1)

option := process.ArgsMap(2, map[string]interface{}{})
ssr := true
if v, ok := option["ssr"].(bool); ok {
ssr = v
}

assetRoot := ""
if v, ok := option["asset_root"].(string); ok {
assetRoot = v
}

data := map[string]interface{}{}
if v, ok := option["data"].(map[string]interface{}); ok {
data = v
}

tmpl, err := sui.GetTemplate(templateID)
if err != nil {
exception.New(err.Error(), 500).Throw()
}

warnings, err := tmpl.Trans(&core.BuildOption{SSR: ssr, AssetRoot: assetRoot, Data: data})
if err != nil {
exception.New(err.Error(), 500).Throw()
}

if warnings != nil && len(warnings) > 0 {
return warnings
}
return nil
}

// TransPage handle the render page request
func TransPage(process *process.Process) interface{} {
process.ValidateArgNums(4)
sui := get(process)
templateID := process.ArgsString(1)
route := route(process, 2)
option := process.ArgsMap(3, map[string]interface{}{})
ssr := true
if v, ok := option["ssr"].(bool); ok {
ssr = v
}

assetRoot := ""
if v, ok := option["asset_root"].(string); ok {
assetRoot = v
}

tmpl, err := sui.GetTemplate(templateID)
if err != nil {
exception.New(err.Error(), 500).Throw()
}

page, err := tmpl.Page(route)
if err != nil {
exception.New(err.Error(), 500).Throw()
}

err = page.Load()
if err != nil {
exception.New(err.Error(), 500).Throw()
}

data := process.ArgsMap(5, map[string]interface{}{})
warnings, err := page.Trans(nil, &core.BuildOption{SSR: ssr, AssetRoot: assetRoot, Data: data})
if err != nil {
exception.New(err.Error(), 500).Throw()
}
if warnings != nil && len(warnings) > 0 {
return warnings
}

return nil
}

// get the sui
func get(process *process.Process) core.SUI {
sui, has := core.SUIs[process.ArgsString(0)]
Expand Down
43 changes: 41 additions & 2 deletions sui/api/process_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,13 @@ func TestTemplateLocaleGet(t *testing.T) {
}

assert.IsType(t, []core.SelectOption{}, res)
assert.Equal(t, 3, len(res.([]core.SelectOption)))
assert.Equal(t, "ja-jp", res.([]core.SelectOption)[0].Value)
assert.Equal(t, 4, len(res.([]core.SelectOption)))
assert.Equal(t, "en-us", res.([]core.SelectOption)[0].Value)
assert.True(t, res.([]core.SelectOption)[0].Default)

assert.Equal(t, "zh-cn", res.([]core.SelectOption)[1].Value)
assert.Equal(t, "zh-hk", res.([]core.SelectOption)[2].Value)
assert.Equal(t, "ja-jp", res.([]core.SelectOption)[3].Value)
}

func TestTemplateThemeGet(t *testing.T) {
Expand Down Expand Up @@ -723,6 +726,42 @@ func TestBuildPage(t *testing.T) {
assert.Nil(t, res)
}

func TestTransAll(t *testing.T) {
prepare(t)
defer clean()

// test demo
p, err := process.Of("sui.trans.all", "test", "advanced", map[string]interface{}{"ssr": true})
if err != nil {
t.Fatal(err)
}

res, err := p.Exec()
if err != nil {
t.Fatal(err)
}

assert.Nil(t, res)
}

func TestTransPage(t *testing.T) {
prepare(t)
defer clean()

// test demo
p, err := process.Of("sui.trans.page", "test", "advanced", "/i18n", map[string]interface{}{"ssr": true})
if err != nil {
t.Fatal(err)
}

res, err := p.Exec()
if err != nil {
t.Fatal(err)
}

assert.Nil(t, res)
}

func TestSyncAssetFile(t *testing.T) {
prepare(t)
defer clean()
Expand Down
4 changes: 4 additions & 0 deletions sui/core/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ type ITemplate interface {

ExecBeforeBuildScripts() []TemplateScirptResult
ExecAfterBuildScripts() []TemplateScirptResult

Trans(option *BuildOption) ([]string, error)
}

// IPage is the interface for the page
Expand Down Expand Up @@ -94,6 +96,8 @@ type IPage interface {

Build(globalCtx *GlobalBuildContext, option *BuildOption) ([]string, error)
BuildAsComponent(globalCtx *GlobalBuildContext, option *BuildOption) ([]string, error)

Trans(globalCtx *GlobalBuildContext, option *BuildOption) ([]string, error)
}

// IBlock is the interface for the block
Expand Down
6 changes: 4 additions & 2 deletions sui/core/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ type Template struct {
Document []byte `json:"-"`
GlobalData []byte `json:"-"`
Scripts *TemplateScirpts `json:"scripts,omitempty"`
Translator string `json:"translator,omitempty"`
}

// TemplateScirpts is the struct for the template scripts
Expand Down Expand Up @@ -217,8 +218,9 @@ type Theme struct {

// SelectOption is the struct for the select option
type SelectOption struct {
Label string `json:"label"`
Value string `json:"value"`
Label string `json:"label"`
Value string `json:"value"`
Default bool `json:"default"`
}

// Asset is the struct for the asset
Expand Down
146 changes: 136 additions & 10 deletions sui/storages/local/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/hashicorp/go-multierror"
"github.com/yaoapp/gou/application"
"github.com/yaoapp/gou/process"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/sui/core"
"gopkg.in/yaml.v3"
Expand Down Expand Up @@ -127,6 +128,37 @@ func (tmpl *Template) Build(option *core.BuildOption) ([]string, error) {
return warnings, err
}

// Trans the template
func (tmpl *Template) Trans(option *core.BuildOption) ([]string, error) {
var err error
warnings := []string{}

ctx := core.NewGlobalBuildContext()
pages, err := tmpl.Pages()
if err != nil {
return warnings, err
}

// loaed pages
for _, page := range pages {
err := page.Load()
if err != nil {
return warnings, err
}

messages, err := page.Trans(ctx, option)
if err != nil {
return warnings, err
}

if len(messages) > 0 {
warnings = append(warnings, messages...)
}
}

return warnings, nil
}

// SyncAssetFile sync the assets
func (tmpl *Template) SyncAssetFile(file string, option *core.BuildOption) error {

Expand Down Expand Up @@ -309,6 +341,25 @@ func (page *Page) BuildAsComponent(globalCtx *core.GlobalBuildContext, option *c
return warnings, err
}

// Trans the page
func (page *Page) Trans(globalCtx *core.GlobalBuildContext, option *core.BuildOption) ([]string, error) {
warnings := []string{}
ctx := core.NewBuildContext(globalCtx)

_, messages, err := page.Page.CompileAsComponent(ctx, option)
if err != nil {
return warnings, err
}

if len(messages) > 0 {
warnings = append(warnings, messages...)
}

// Tranlate the locale files
err = page.writeLocaleSource(ctx)
return warnings, err
}

func (page *Page) publicFile(data map[string]interface{}) string {
root, err := page.tmpl.local.DSL.PublicRoot(data)
if err != nil {
Expand All @@ -328,6 +379,9 @@ func (page *Page) localeFiles(data map[string]interface{}) map[string]string {
roots := map[string]string{}
locales := page.tmpl.Locales()
for _, locale := range locales {
if locale.Default {
continue
}
target := filepath.Join("/", "public", root, ".locales", locale.Value, fmt.Sprintf("%s.yml", page.Route))
roots[locale.Value] = target
}
Expand Down Expand Up @@ -365,14 +419,13 @@ func (page *Page) localeGlobal(name string) core.Locale {
return global
}

func (page *Page) locale(name string) core.Locale {
func (page *Page) locale(name string, pageOnly ...bool) core.Locale {
file := filepath.Join(page.tmpl.Root, "__locales", name, fmt.Sprintf("%s.yml", page.Route))
global := page.localeGlobal(name)

// Check the locale file
exist, err := page.tmpl.local.fs.Exists(file)
if err != nil {
log.Error(`[SUI] Check the locale file error: %s`, err.Error())
return global
}

Expand All @@ -399,22 +452,95 @@ func (page *Page) locale(name string) core.Locale {
return global
}

// Merge the global
for key, message := range global.Keys {
if _, ok := locale.Keys[key]; !ok {
locale.Keys[key] = message
if len(pageOnly) == 0 || !pageOnly[0] {

// Merge the global
for key, message := range global.Keys {
if _, ok := locale.Keys[key]; !ok {
locale.Keys[key] = message
}
}
}

for key, message := range global.Messages {
if _, ok := locale.Messages[key]; !ok {
locale.Messages[key] = message
for key, message := range global.Messages {
if _, ok := locale.Messages[key]; !ok {
locale.Messages[key] = message
}
}
}

return locale
}

func (page *Page) writeLocaleSource(ctx *core.BuildContext) error {

locales := page.tmpl.Locales()
translations := ctx.GetTranslations()
for _, lc := range locales {
if lc.Default {
continue
}

locale := page.locale(lc.Value, true)
for _, t := range translations {
message := t.Message
// Match the key
if _, has := locale.Messages[message]; has {
message = locale.Messages[message]
}
locale.Keys[t.Key] = message
msg, has := locale.Messages[t.Message]
if has && msg != t.Message {
continue
}
locale.Messages[t.Message] = t.Message
}

// Call the hook
var keys any = locale.Keys
var messages any = locale.Messages
if page.tmpl.Translator != "" {
p, err := process.Of(page.tmpl.Translator, lc.Value, locale, page.Route, page.TemplateID)
if err != nil {
return err
}

res, err := p.Exec()
if err != nil {
return err
}

pres, ok := res.(map[string]interface{})
if !ok {
return fmt.Errorf("The translator %s should return a locale", page.tmpl.Translator)
}

keys = pres["keys"]
messages = pres["messages"]
}

if keys == nil && messages == nil {
return nil
}

// Save to file
file := filepath.Join(page.tmpl.Root, "__locales", lc.Value, fmt.Sprintf("%s.yml", page.Route))
content, err := yaml.Marshal(map[string]interface{}{
"keys": keys,
"messages": messages,
})
if err != nil {
return err
}

_, err = page.tmpl.local.fs.WriteFile(file, content, 0644)
if err != nil {
return err
}
}

return nil
}

func (page *Page) writeLocaleFiles(ctx *core.BuildContext, data map[string]interface{}) error {

if ctx == nil {
Expand Down
Loading

0 comments on commit ef8b802

Please sign in to comment.