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())