Merge pull request #692 from trheyi/main
optimize SUI page writeLocaleSource and writeLocaleFiles logic
This commit is contained in:
commit
a2a60d87cc
5 changed files with 412 additions and 124 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
87
sui/core/locale.go
Normal file
87
sui/core/locale.go
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
209
sui/core/locale_test.go
Normal file
209
sui/core/locale_test.go
Normal file
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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 := page.tmpl.getLocale(lc.Value, page.Route, true)
|
||||
locale.MergeTranslations(translations, prefix)
|
||||
|
||||
// Call the hook
|
||||
var keys any = locale.Keys
|
||||
|
|
@ -571,37 +560,22 @@ 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.tmpl.getLocale(name, page.Route)
|
||||
locale.MergeTranslations(translations, prefix)
|
||||
|
||||
// Init Data
|
||||
keys := map[string]string{}
|
||||
messages := map[string]string{}
|
||||
for _, t := range translations {
|
||||
keys[t.Key] = t.Message
|
||||
messages[t.Message] = t.Message
|
||||
// Merge the components locale
|
||||
for _, route := range components {
|
||||
compLocale := page.tmpl.getLocale(name, route, true)
|
||||
compLocale.ParseKeys()
|
||||
locale.Merge(compLocale)
|
||||
}
|
||||
|
||||
locale := page.locale(name)
|
||||
for key := range keys {
|
||||
if _, has := locale.Keys[key]; has {
|
||||
keys[key] = locale.Keys[key]
|
||||
}
|
||||
|
||||
if msgValue, has := locale.Messages[keys[key]]; has {
|
||||
keys[key] = msgValue
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue