Skip to content

Commit f3d2af2

Browse files
feat(yalocales): Default locale derivation
1 parent 414b891 commit f3d2af2

6 files changed

Lines changed: 775 additions & 328 deletions

File tree

yalocales/codegen_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func TestGenerateCode(t *testing.T) {
1818
t.Fatalf("failed to access sub fs: %v", err)
1919
}
2020

21-
loc := yalocales.NewYaLocalizer("en")
21+
loc := yalocales.NewYaLocalizer("en", false)
2222
if yaErr := loc.LoadLocales(sub); yaErr != nil {
2323
t.Fatalf("Failed to load locales: %v", yaErr)
2424
}

yalocales/errors.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,6 @@ var (
1515
ErrDefaultCoverage = errors.New("default language missing keys")
1616
ErrMismatchedPlaceholders = errors.New("mismatched locale placeholders")
1717
ErrMissingFormatArgs = errors.New("missing format arguments")
18+
ErrNoDefaultLanguage = errors.New("no default language set")
19+
ErrConsistencyRequired = errors.New("consistency check is required for this operation")
1820
)

yalocales/locale.go

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
package yalocales
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
"strings"
8+
9+
"github.com/YaCodeDev/GoYaCodeDevUtils/yaerrors"
10+
)
11+
12+
// compiledLocale represents a compiled localization file node or leaf
13+
type compiledLocale struct {
14+
// The key for this node
15+
Key string
16+
// The sub-nodes for this node, if any. This is mutually exclusive with Value
17+
SubMap map[string]*compiledLocale
18+
// This is the actual value if this is a leaf node, might be empty if not a leaf, only useful for native lookups.
19+
// This is mutually exclusive with SubMap
20+
Value string
21+
// This is the JSON representation of this node and all its children, ready to be served as-is
22+
JSON []byte
23+
}
24+
25+
func (c *compiledLocale) retriveJSONByCompositeKey(key string) ([]byte, yaerrors.Error) {
26+
if key == "" {
27+
return c.JSON, nil
28+
}
29+
30+
keyPart := strings.SplitN(key, Separator, keySplitMaxParts)
31+
32+
if c.SubMap == nil {
33+
return nil, yaerrors.FromError(
34+
http.StatusNotFound,
35+
ErrSubMapNotFound,
36+
fmt.Sprintf("No submap for key part '%s'", keyPart[0]),
37+
)
38+
}
39+
40+
if len(keyPart) == 1 {
41+
return c.SubMap[keyPart[0]].JSON, nil
42+
}
43+
44+
subLocale, ok := c.SubMap[keyPart[0]]
45+
46+
if !ok {
47+
return nil, yaerrors.FromError(
48+
http.StatusNotFound,
49+
ErrKeyNotFound,
50+
fmt.Sprintf("Key '%s' not found", keyPart[0]),
51+
)
52+
}
53+
54+
value, err := subLocale.retriveJSONByCompositeKey(keyPart[1])
55+
if err != nil {
56+
return nil, err.Wrap(fmt.Sprintf("Failed to retrieve JSON for key part '%s'", keyPart[0]))
57+
}
58+
59+
return value, nil
60+
}
61+
62+
func (c *compiledLocale) retriveValueByCompositeKey(key string) (string, yaerrors.Error) {
63+
if key == "" {
64+
return c.Value, nil
65+
}
66+
67+
keyPart := strings.SplitN(key, Separator, keySplitMaxParts)
68+
69+
if c.SubMap == nil {
70+
return "", yaerrors.FromError(
71+
http.StatusNotFound,
72+
ErrSubMapNotFound,
73+
fmt.Sprintf("No submap for key part '%s'", keyPart[0]),
74+
)
75+
}
76+
77+
if len(keyPart) == 1 {
78+
return c.SubMap[keyPart[0]].Value, nil
79+
}
80+
81+
subLocale, ok := c.SubMap[keyPart[0]]
82+
83+
if !ok {
84+
return "", yaerrors.FromError(
85+
http.StatusNotFound,
86+
ErrKeyNotFound,
87+
fmt.Sprintf("Key '%s' not found", keyPart[0]),
88+
)
89+
}
90+
91+
value, err := subLocale.retriveValueByCompositeKey(keyPart[1])
92+
if err != nil {
93+
return "", err.Wrap(fmt.Sprintf("Failed to retrieve value for key part '%s'", keyPart[0]))
94+
}
95+
96+
return value, nil
97+
}
98+
99+
func (c *compiledLocale) insertByCompositeKey(key, value string) yaerrors.Error {
100+
if value == "" {
101+
return yaerrors.FromError(
102+
http.StatusTeapot,
103+
ErrInvalidTranslation,
104+
"Empty values are not allowed",
105+
)
106+
}
107+
108+
keyPart := strings.SplitN(key, Separator, keySplitMaxParts)
109+
110+
if c.SubMap == nil {
111+
c.SubMap = make(map[string]*compiledLocale)
112+
}
113+
114+
if len(keyPart) == keySplitMaxParts {
115+
_, ok := c.SubMap[keyPart[0]]
116+
if !ok {
117+
c.SubMap[keyPart[0]] = &compiledLocale{
118+
Key: keyPart[0],
119+
}
120+
}
121+
122+
err := c.SubMap[keyPart[0]].insertByCompositeKey(keyPart[1], value)
123+
if err != nil {
124+
return err.Wrap(fmt.Sprintf("Failed to insert key part '%s'", keyPart[0]))
125+
}
126+
127+
return nil
128+
}
129+
130+
if _, ok := c.SubMap[key]; ok {
131+
return yaerrors.FromError(
132+
http.StatusTeapot,
133+
ErrDuplicateKey,
134+
fmt.Sprintf("Key '%s' already exists", key),
135+
)
136+
}
137+
138+
kvMap := map[string]string{
139+
key: value,
140+
}
141+
142+
jsonData, err := json.Marshal(kvMap)
143+
if err != nil {
144+
return yaerrors.FromError(
145+
http.StatusInternalServerError,
146+
err,
147+
fmt.Sprintf("Failed to marshal JSON for key '%s'", key),
148+
)
149+
}
150+
151+
c.SubMap[key] = &compiledLocale{
152+
Key: key,
153+
Value: value,
154+
JSON: jsonData,
155+
}
156+
157+
return nil
158+
}
159+
160+
func (c *compiledLocale) representSubTreeReconcilingJSON() (map[string]any, yaerrors.Error) {
161+
if c.SubMap != nil {
162+
result := make(map[string]any)
163+
164+
for k, v := range c.SubMap {
165+
if v.SubMap != nil {
166+
subResult, err := v.representSubTreeReconcilingJSON()
167+
if err != nil {
168+
return nil, err.Wrap(
169+
fmt.Sprintf("Failed to represent sub-tree for key '%s'", k),
170+
)
171+
}
172+
173+
result[k] = subResult
174+
} else {
175+
result[k] = v.Value
176+
}
177+
}
178+
179+
jsonData, err := json.Marshal(result)
180+
if err != nil {
181+
return nil, yaerrors.FromError(
182+
http.StatusInternalServerError,
183+
err,
184+
"Failed to marshal JSON for sub-tree",
185+
)
186+
}
187+
188+
c.JSON = jsonData
189+
190+
return result, nil
191+
}
192+
193+
return nil, yaerrors.FromError(
194+
http.StatusInternalServerError,
195+
ErrNilLocale,
196+
"Failed to represent sub-tree",
197+
)
198+
}

0 commit comments

Comments
 (0)