Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[add] sui.trans.all and sui.trans.page to support automatic translation #681

Merged
merged 1 commit into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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