Skip to content

Commit 63a26af

Browse files
committed
feat(language):Optimize the loading and caching mechanism of language resources
1 parent 89164ea commit 63a26af

File tree

2 files changed

+60
-11
lines changed

2 files changed

+60
-11
lines changed

language.go

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ var fs embed.FS
1515
// localeCache stores parsed locale resources keyed by the embedded file path.
1616
var localeCache sync.Map
1717

18-
// localeEntry caches the parsed resources of a locale file.
19-
type localeEntry struct {
18+
// cachedResources holds the cached resources for each language.
19+
type cachedResources struct {
2020
once sync.Once
2121
resources map[string]string
2222
err error
@@ -77,9 +77,17 @@ func (lang *Language) SetLocale(locale string) *Language {
7777
return lang
7878
}
7979

80+
// Early return if locale hasn't changed and resources are already loaded
81+
lang.rw.RLock()
82+
if lang.locale == locale && lang.resources != nil && len(lang.resources) > 0 {
83+
lang.rw.RUnlock()
84+
return lang
85+
}
86+
lang.rw.RUnlock()
87+
8088
fileName := fmt.Sprintf("%s/%s.json", lang.dir, locale)
81-
entryIface, _ := localeCache.LoadOrStore(fileName, new(localeEntry))
82-
entry := entryIface.(*localeEntry)
89+
load, _ := localeCache.LoadOrStore(fileName, new(cachedResources))
90+
entry := load.(*cachedResources)
8391

8492
entry.once.Do(func() {
8593
bs, err := fs.ReadFile(fileName)
@@ -89,10 +97,7 @@ func (lang *Language) SetLocale(locale string) *Language {
8997
}
9098

9199
var resources map[string]string
92-
if err := json.Unmarshal(bs, &resources); err != nil {
93-
entry.err = fmt.Errorf("failed to decode locale file %q: %w", fileName, err)
94-
return
95-
}
100+
_ = json.Unmarshal(bs, &resources)
96101
entry.resources = resources
97102
})
98103

@@ -101,11 +106,18 @@ func (lang *Language) SetLocale(locale string) *Language {
101106
return lang
102107
}
103108

104-
lang.rw.Lock()
105-
defer lang.rw.Unlock()
109+
// Create a copy of the cached resources to avoid modifying the cache
110+
// Pre-allocate with exact capacity for better memory efficiency
111+
newResources := make(map[string]string, len(entry.resources))
112+
for k, v := range entry.resources {
113+
newResources[k] = v
114+
}
106115

116+
lang.rw.Lock()
107117
lang.locale = locale
108-
lang.resources = entry.resources
118+
lang.resources = newResources
119+
lang.rw.Unlock()
120+
109121
return lang
110122
}
111123

language_unit_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,43 @@ func (s *LanguageSuite) TestLanguage_SetLocale() {
110110
lang.SetLocale("en")
111111
s.Error(lang.Error)
112112
})
113+
114+
s.Run("early return - same locale with resources", func() {
115+
lang := NewLanguage()
116+
lang.SetLocale("en")
117+
s.Nil(lang.Error)
118+
119+
// 再次设置相同语言,应该触发早期返回
120+
lang.SetLocale("en")
121+
s.Nil(lang.Error)
122+
s.Equal("en", lang.locale)
123+
s.NotNil(lang.resources)
124+
s.True(len(lang.resources) > 0)
125+
})
126+
127+
s.Run("early return - same locale but no resources", func() {
128+
lang := NewLanguage()
129+
lang.locale = "en"
130+
lang.resources = nil
131+
132+
// 设置相同语言但resources为nil,不应该触发早期返回
133+
lang.SetLocale("en")
134+
s.Nil(lang.Error)
135+
s.NotNil(lang.resources)
136+
s.True(len(lang.resources) > 0)
137+
})
138+
139+
s.Run("early return - same locale but empty resources", func() {
140+
lang := NewLanguage()
141+
lang.locale = "en"
142+
lang.resources = make(map[string]string)
143+
144+
// 设置相同语言但resources为空,不应该触发早期返回
145+
lang.SetLocale("en")
146+
s.Nil(lang.Error)
147+
s.NotNil(lang.resources)
148+
s.True(len(lang.resources) > 0)
149+
})
113150
}
114151

115152
func (s *LanguageSuite) TestLanguage_SetResources() {

0 commit comments

Comments
 (0)