/
client.go
192 lines (146 loc) · 5.15 KB
/
client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
// Copyright © 2020. All rights reserved.
// Author: Ilya Stroy.
// Contacts: qioalice@gmail.com, https://github.com/qioalice
// License: https://opensource.org/licenses/MIT
package privet
import (
"bytes"
"sync/atomic"
"unsafe"
"github.com/qioalice/ekago/v2/ekaerr"
)
type (
/*
TODO: comment
*/
Client struct {
/*
state is a current state of the whole package.
The package provides to you some promises (contracts),
to maintain which this global variable exists.
You may read more about promises (contracts) in the README.md file.
Here is a some "under the hood" description of how is it works.
- You cannot call any Locale's getters during locale files loading and parsing.
- TODO:
*/
state uint32
config struct {
// C-like bool variables. 1 - true, 0 - false.
// Protected by atomic operations.
OverwriteExistingKey uint32
LCEmptyLocaleNameAsNil uint32
LCNotFoundLocaleAsNil uint32
SkipParseFilepath uint32
}
defaultLocale unsafe.Pointer
storage,
storageTmp map[string]*Locale
sources,
sourcesTmp []SourceItem
buf bytes.Buffer
phrasesTotal uint64
localesTotal uint32
}
)
/*
Source allows you to add locale content or path to it.
Multiple calls are allowed or you can pass all arguments into one call.
Source(<content1>) // (1)
Source(<content2>) // (2)
is the same as
Source(<content1>, <content2>) // (3)
You can even combine the arrays of content and single data.
Source(<content1>, []{<content2>, <content3>}) // (4)
As you noticed, the examples above written at the near Golang pseudocode.
That's because there is different entities may be used as locale source
depends on type of argument.
First thing first, all arguments are passed and combined into one single array.
Passed arrays also too.
Then the formed array's items are analysed independently,
the whole set of sources is generating, step by step,
moving forward to being ready to loaded by Load() call.
Argument's types and their meanings:
(Keep in mind, that ALL OTHER ARGUMENT TYPES which not listed below ARE PROHIBITED
and will lead to fast return error).
As we know from the above, there is only some "base" types and arrays are allowed.
Base types are:
- string (treated as path to either locale's directory or locale's one file),
- []byte (treated as the content of locale's file).
Adding arrays to the list above and we've also get:
- []string (treated as the array of either locale's directories, files or even mixed),
- [][]byte (treated as the array of locale's files content).
The passed paths are analyzed as soon as possible during this call
(thus you will get an error if path is not exist, access denied or something else)
but the content will not be loaded until Load() call.
Also for passed []byte (or [][]byte).
Keep in mind, Load() call flushes all pending sources.
Thus you will need to re-register sources you want to use as source again
after Load() call,
if you need to call Load() more than one times (dunno why you even need that).
Path to directory or file might be absolute or relative.
If relative it will converted to absolutely starting from the current work directory.
You may provide path to directory that contains sub-directories.
In that case all these sub-directories will be scanned too recursively,
till locale files found.
*/
func (c *Client) Source(args ...interface{}) *ekaerr.Error {
return c.source(args).Throw()
}
/*
TODO: comment
*/
func (c *Client) Load() *ekaerr.Error {
return c.load().Throw()
}
/*
LC returns the requested Locale by its name.
If the Locale with the specified name doesn't exists (or if name is empty):
- Default Locale is returned if any locale marked as default;
- nil is returned if no locale is marked as default.
You may change that behaviour using:
1. Config.LCEmptyLocaleNameAsNil set to true (false by default)
if you want to get nil Locale if name is empty
(even if any Locale is marked as default).
2. Config.LCNotFoundLocaleAsNil set to true (false by default)
if you want to get nil Locale if Locale with requested name not found
(even if any Locale is marked as default).
*/
func (c *Client) LC(name string) *Locale {
if !c.isValid() {
return nil
}
if name == "" {
if atomic.LoadUint32(&c.config.LCEmptyLocaleNameAsNil) == 1 {
return nil
} else {
return c.getDefaultLocale()
}
}
if loc := c.getLocale(name); loc != nil {
return loc
} else if atomic.LoadUint32(&c.config.LCNotFoundLocaleAsNil) == 0 {
return c.getDefaultLocale()
} else {
return nil
}
}
/*
Default returns a Locale object that is marked as default Locale.
If no Locale marked as default, nil is returned.
Reminder:
It's safe to call Locale.Tr() of nil Locale.
You just will get an error string message. Not panic, not UB.
*/
func (c *Client) Default() *Locale {
if !c.isValid() {
return nil
}
return c.getDefaultLocale()
}
/*
Tr is an alias for Client.LC(localeName).Tr(key, args).
See LC() function and Locale.Tr() method for more details.
*/
func (c *Client) Tr(localeName, key string, args Args) string {
return c.LC(localeName).Tr(key, args)
}