From 61f297b3dc7b1e10456a3deca2145dffd9e3c215 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 17 Jul 2024 08:25:52 +0800 Subject: [PATCH 1/2] optimize SUI page writeLocaleSource and writeLocaleFiles logic --- sui/core/context.go | 12 +++ sui/core/locale.go | 69 ++++++++++++ sui/core/locale_test.go | 209 ++++++++++++++++++++++++++++++++++++ sui/core/utils.go | 8 +- sui/storages/local/build.go | 47 ++------ 5 files changed, 307 insertions(+), 38 deletions(-) create mode 100644 sui/core/locale.go create mode 100644 sui/core/locale_test.go diff --git a/sui/core/context.go b/sui/core/context.go index e22bf783..e39a3f77 100644 --- a/sui/core/context.go +++ b/sui/core/context.go @@ -36,6 +36,18 @@ func (ctx *BuildContext) GetJitComponents() []string { return jitComponents } +// GetComponents get the components +func (ctx *BuildContext) GetComponents() []string { + if ctx.components == nil { + return []string{} + } + components := []string{} + for name := range ctx.components { + components = append(components, name) + } + return components +} + // GetTranslations get the translations func (ctx *BuildContext) GetTranslations() []Translation { if ctx.translations == nil { diff --git a/sui/core/locale.go b/sui/core/locale.go new file mode 100644 index 00000000..1992540b --- /dev/null +++ b/sui/core/locale.go @@ -0,0 +1,69 @@ +package core + +import ( + "fmt" + "regexp" +) + +// MergeTranslations merge the translations +func (locale *Locale) MergeTranslations(translations []Translation, prefix ...string) { + if locale.Keys == nil { + locale.Keys = map[string]string{} + } + + if locale.Messages == nil { + locale.Messages = map[string]string{} + } + + var reg *regexp.Regexp = nil + if len(prefix) > 0 && prefix[0] != "" { + reg = regexp.MustCompile(fmt.Sprintf(`^%s_([0-9]+)$`, prefix[0])) + } + + for _, t := range translations { + + // Keep only the keys that start with the keyPrefix + if reg != nil && !reg.MatchString(t.Key) { + continue + } + + message := t.Message + 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 + } +} + +// Merge merge the locale +func (locale *Locale) Merge(locale2 Locale) { + + if locale2.Keys != nil { + if locale.Keys == nil { + locale.Keys = map[string]string{} + } + for key, value := range locale2.Keys { + if _, has := locale.Keys[key]; has { + continue + } + locale.Keys[key] = value + } + } + + if locale2.Messages != nil { + if locale.Messages == nil { + locale.Messages = map[string]string{} + } + for key, value := range locale2.Messages { + if _, has := locale.Messages[key]; has { + continue + } + locale.Messages[key] = value + } + } +} diff --git a/sui/core/locale_test.go b/sui/core/locale_test.go new file mode 100644 index 00000000..f4e6dc4d --- /dev/null +++ b/sui/core/locale_test.go @@ -0,0 +1,209 @@ +package core + +import ( + "testing" +) + +func TestLocaleMergeTranslations(t *testing.T) { + tests := []struct { + name string + locale Locale + translations []Translation + prefix string + expectedKeys map[string]string + expectedMsgs map[string]string + }{ + { + name: "Empty translations", + locale: Locale{ + Keys: map[string]string{}, + Messages: map[string]string{}, + }, + translations: []Translation{}, + prefix: "", + expectedKeys: map[string]string{}, + expectedMsgs: map[string]string{}, + }, + { + name: "Nil Keys and Messages", + locale: Locale{ + Keys: nil, + Messages: nil, + }, + translations: []Translation{ + {Key: "greeting", Message: "Hello"}, + }, + prefix: "", + expectedKeys: map[string]string{ + "greeting": "Hello", + }, + expectedMsgs: map[string]string{ + "Hello": "Hello", + }, + }, + { + name: "With prefix", + locale: Locale{ + Keys: map[string]string{}, + Messages: map[string]string{}, + }, + translations: []Translation{ + {Key: "prefix_1", Message: "Hello"}, + {Key: "other_1", Message: "World"}, + }, + prefix: "prefix", + expectedKeys: map[string]string{ + "prefix_1": "Hello", + }, + expectedMsgs: map[string]string{ + "Hello": "Hello", + }, + }, + { + name: "Update existing keys and values", + locale: Locale{ + Keys: map[string]string{ + "greeting": "Hi", + }, + Messages: map[string]string{ + "Hi": "Hi", + }, + }, + translations: []Translation{ + {Key: "greeting", Message: "Hello"}, + }, + prefix: "", + expectedKeys: map[string]string{ + "greeting": "Hello", + }, + expectedMsgs: map[string]string{ + "Hi": "Hi", + "Hello": "Hello", + }, + }, + { + name: "Duplicate messages", + locale: Locale{ + Keys: map[string]string{}, + Messages: map[string]string{}, + }, + translations: []Translation{ + {Key: "welcome", Message: "Hello"}, + {Key: "farewell", Message: "Hello"}, + }, + prefix: "", + expectedKeys: map[string]string{ + "welcome": "Hello", + "farewell": "Hello", + }, + expectedMsgs: map[string]string{ + "Hello": "Hello", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.locale.MergeTranslations(tt.translations, tt.prefix) + if !testCompareMaps(tt.locale.Keys, tt.expectedKeys) { + t.Errorf("expected keys %v, got %v", tt.expectedKeys, tt.locale.Keys) + } + if !testCompareMaps(tt.locale.Messages, tt.expectedMsgs) { + t.Errorf("expected messages %v, got %v", tt.expectedMsgs, tt.locale.Messages) + } + }) + } +} + +func TestLocaleMerge(t *testing.T) { + tests := []struct { + name string + locale Locale + locale2 Locale + expectedKeys map[string]string + expectedMsgs map[string]string + }{ + { + name: "Nil Keys and Messages in locale2", + locale: Locale{ + Keys: map[string]string{"greeting": "Hello"}, + Messages: map[string]string{"Hello": "Hello"}, + }, + locale2: Locale{ + Keys: nil, + Messages: nil, + }, + expectedKeys: map[string]string{"greeting": "Hello"}, + expectedMsgs: map[string]string{"Hello": "Hello"}, + }, + { + name: "Nil Keys and Messages in locale", + locale: Locale{ + Keys: nil, + Messages: nil, + }, + locale2: Locale{ + Keys: map[string]string{"farewell": "Goodbye"}, + Messages: map[string]string{"Goodbye": "Goodbye"}, + }, + expectedKeys: map[string]string{"farewell": "Goodbye"}, + expectedMsgs: map[string]string{"Goodbye": "Goodbye"}, + }, + { + name: "Merge non-existing keys and messages", + locale: Locale{ + Keys: map[string]string{"greeting": "Hello"}, + Messages: map[string]string{"Hello": "Hello"}, + }, + locale2: Locale{ + Keys: map[string]string{"farewell": "Goodbye"}, + Messages: map[string]string{"Goodbye": "Goodbye"}, + }, + expectedKeys: map[string]string{ + "greeting": "Hello", + "farewell": "Goodbye", + }, + expectedMsgs: map[string]string{ + "Hello": "Hello", + "Goodbye": "Goodbye", + }, + }, + { + name: "Merge with existing keys and messages", + locale: Locale{ + Keys: map[string]string{"greeting": "Hello"}, + Messages: map[string]string{"Hello": "Hello"}, + }, + locale2: Locale{ + Keys: map[string]string{"greeting": "Hi"}, + Messages: map[string]string{"Hello": "Hi"}, + }, + expectedKeys: map[string]string{"greeting": "Hello"}, + expectedMsgs: map[string]string{"Hello": "Hello"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.locale.Merge(tt.locale2) + if !testCompareMaps(tt.locale.Keys, tt.expectedKeys) { + t.Errorf("expected keys %v, got %v", tt.expectedKeys, tt.locale.Keys) + } + if !testCompareMaps(tt.locale.Messages, tt.expectedMsgs) { + t.Errorf("expected messages %v, got %v", tt.expectedMsgs, tt.locale.Messages) + } + }) + } +} + +func testCompareMaps(a, b map[string]string) bool { + if len(a) != len(b) { + return false + } + for k, v := range a { + if b[k] != v { + return false + } + } + return true +} diff --git a/sui/core/utils.go b/sui/core/utils.go index 0cb7655f..3c4f0c19 100644 --- a/sui/core/utils.go +++ b/sui/core/utils.go @@ -80,8 +80,14 @@ func ComponentName(name string, hash ...bool) string { // TranslationKey convert the name to translation key func TranslationKey(name string, sequence int) string { + prefix := TranslationKeyPrefix(name) + return fmt.Sprintf("%s_%d", prefix, sequence) +} + +// TranslationKeyPrefix convert the name to translation key prefix +func TranslationKeyPrefix(name string) string { name = strings.ReplaceAll(name, "/", "_") name = strings.ReplaceAll(name, "[", "_") name = strings.ReplaceAll(name, "]", "_") - return fmt.Sprintf("trans_%s_%d", name, sequence) + return fmt.Sprintf("trans_%s", name) } diff --git a/sui/storages/local/build.go b/sui/storages/local/build.go index dee91fd4..18de54a4 100644 --- a/sui/storages/local/build.go +++ b/sui/storages/local/build.go @@ -495,25 +495,14 @@ func (page *Page) writeLocaleSource(ctx *core.BuildContext, option *core.BuildOp } } + prefix := core.TranslationKeyPrefix(page.Route) 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 - } + locale.MergeTranslations(translations, prefix) // Call the hook var keys any = locale.Keys @@ -573,35 +562,19 @@ func (page *Page) writeLocaleFiles(ctx *core.BuildContext, data map[string]inter } files := page.localeFiles(data) + components := ctx.GetComponents() for name, file := range files { - - // Init Data - keys := map[string]string{} - messages := map[string]string{} - for _, t := range translations { - keys[t.Key] = t.Message - messages[t.Message] = t.Message - } - locale := page.locale(name) - for key := range keys { - if _, has := locale.Keys[key]; has { - keys[key] = locale.Keys[key] - } + locale.MergeTranslations(translations) - if msgValue, has := locale.Messages[keys[key]]; has { - keys[key] = msgValue - } + // Merge the components locale + for _, component := range components { + compLocale := page.locale(component, true) + locale.Merge(compLocale) } - for message := range messages { - if _, has := locale.Messages[message]; has { - messages[message] = locale.Messages[message] - } - } - - locale.Keys = keys - locale.Messages = messages + // Remove messages + locale.Messages = map[string]string{} raw, err := yaml.Marshal(locale) if err != nil { log.Error(`[SUI] Marshal the locale file error: %s`, err.Error()) From 1005556c2f64f1449a4e8ee384cbfff552a65f39 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 17 Jul 2024 09:16:11 +0800 Subject: [PATCH 2/2] optimize SUI page writeLocaleSource and writeLocaleFiles logic --- sui/core/locale.go | 18 ++++ sui/storages/local/build.go | 179 ++++++++++++++++++------------------ 2 files changed, 108 insertions(+), 89 deletions(-) diff --git a/sui/core/locale.go b/sui/core/locale.go index 1992540b..9c9b7779 100644 --- a/sui/core/locale.go +++ b/sui/core/locale.go @@ -67,3 +67,21 @@ func (locale *Locale) Merge(locale2 Locale) { } } } + +// ParseKeys match +func (locale *Locale) ParseKeys() { + if locale.Keys == nil { + locale.Keys = map[string]string{} + } + + if locale.Messages == nil { + locale.Messages = map[string]string{} + } + + for key, msgKey := range locale.Keys { + if message, has := locale.Messages[msgKey]; has { + locale.Keys[key] = message + } + } + return +} diff --git a/sui/storages/local/build.go b/sui/storages/local/build.go index 18de54a4..fea32004 100644 --- a/sui/storages/local/build.go +++ b/sui/storages/local/build.go @@ -217,6 +217,89 @@ func (tmpl *Template) SyncAssets(option *core.BuildOption) error { return copyDirectory(sourceRoot, targetRoot) } +func (tmpl *Template) getLocaleGlobal(name string) core.Locale { + global := core.Locale{ + Keys: map[string]string{}, + Messages: map[string]string{}, + } + file := filepath.Join(tmpl.Root, "__locales", name, "__global.yml") + exist, err := tmpl.local.fs.Exists(file) + if err != nil { + log.Error(`[SUI] Check the global locale file error: %s`, err.Error()) + return global + } + + if !exist { + return global + } + + raw, err := tmpl.local.fs.ReadFile(file) + if err != nil { + log.Error(`[SUI] Read the global locale file error: %s`, err.Error()) + return global + } + + err = yaml.Unmarshal(raw, &global) + if err != nil { + log.Error(`[SUI] Parse the global locale file error: %s`, err.Error()) + return global + } + + return global +} + +func (tmpl *Template) getLocale(name string, route string, pageOnly ...bool) core.Locale { + file := filepath.Join(tmpl.Root, "__locales", name, fmt.Sprintf("%s.yml", route)) + global := tmpl.getLocaleGlobal(name) + + // Check the locale file + exist, err := tmpl.local.fs.Exists(file) + if err != nil { + return global + } + + if !exist { + return global + } + + locale := core.Locale{ + Keys: map[string]string{}, + Messages: map[string]string{}, + Date: global.Date, + Currency: global.Currency, + Number: global.Number, + } + raw, err := tmpl.local.fs.ReadFile(file) + if err != nil { + log.Error(`[SUI] Read the locale file error: %s`, err.Error()) + return global + } + + err = yaml.Unmarshal(raw, &locale) + if err != nil { + log.Error(`[SUI] Parse the locale file error: %s`, err.Error()) + return global + } + + 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 + } + } + } + + return locale +} + // Build is the struct for the public func (page *Page) Build(globalCtx *core.GlobalBuildContext, option *core.BuildOption) ([]string, error) { @@ -399,89 +482,6 @@ func (page *Page) localeFiles(data map[string]interface{}) map[string]string { return roots } -func (page *Page) localeGlobal(name string) core.Locale { - global := core.Locale{ - Keys: map[string]string{}, - Messages: map[string]string{}, - } - file := filepath.Join(page.tmpl.Root, "__locales", name, "__global.yml") - exist, err := page.tmpl.local.fs.Exists(file) - if err != nil { - log.Error(`[SUI] Check the global locale file error: %s`, err.Error()) - return global - } - - if !exist { - return global - } - - raw, err := page.tmpl.local.fs.ReadFile(file) - if err != nil { - log.Error(`[SUI] Read the global locale file error: %s`, err.Error()) - return global - } - - err = yaml.Unmarshal(raw, &global) - if err != nil { - log.Error(`[SUI] Parse the global locale file error: %s`, err.Error()) - return global - } - - return global -} - -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 { - return global - } - - if !exist { - return global - } - - locale := core.Locale{ - Keys: map[string]string{}, - Messages: map[string]string{}, - Date: global.Date, - Currency: global.Currency, - Number: global.Number, - } - raw, err := page.tmpl.local.fs.ReadFile(file) - if err != nil { - log.Error(`[SUI] Read the locale file error: %s`, err.Error()) - return global - } - - err = yaml.Unmarshal(raw, &locale) - if err != nil { - log.Error(`[SUI] Parse the locale file error: %s`, err.Error()) - return global - } - - 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 - } - } - } - - return locale -} - func (page *Page) writeLocaleSource(ctx *core.BuildContext, option *core.BuildOption) error { locales := page.tmpl.Locales() @@ -501,7 +501,7 @@ func (page *Page) writeLocaleSource(ctx *core.BuildContext, option *core.BuildOp continue } - locale := page.locale(lc.Value, true) + locale := page.tmpl.getLocale(lc.Value, page.Route, true) locale.MergeTranslations(translations, prefix) // Call the hook @@ -560,16 +560,17 @@ func (page *Page) writeLocaleFiles(ctx *core.BuildContext, data map[string]inter if len(translations) == 0 { return nil } - + prefix := core.TranslationKeyPrefix(page.Route) files := page.localeFiles(data) components := ctx.GetComponents() for name, file := range files { - locale := page.locale(name) - locale.MergeTranslations(translations) + locale := page.tmpl.getLocale(name, page.Route) + locale.MergeTranslations(translations, prefix) // Merge the components locale - for _, component := range components { - compLocale := page.locale(component, true) + for _, route := range components { + compLocale := page.tmpl.getLocale(name, route, true) + compLocale.ParseKeys() locale.Merge(compLocale) }