forked from cloudfoundry-attic/jibber_jabber
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjibberjabber.go
321 lines (256 loc) · 10.8 KB
/
jibberjabber.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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
package jibberjabber
import (
"errors"
"fmt"
"sort"
"strings"
"sync"
"golang.org/x/text/language"
"golang.org/x/text/language/display"
)
var (
ErrLangDetectFail = errors.New("could not detect Language")
ErrLangFallbackUndefined = errors.New("no fallback language defined")
ErrLangFallbackUnsupported = errors.New("defined fallback language is not supported")
ErrLangUnsupported = errors.New("language not supported")
ErrLangParse = errors.New("language identifier cannot be parsed")
)
func splitLocale(locale string) (string, string) {
formattedLocale := strings.Split(locale, ".")[0]
formattedLocale = strings.Replace(formattedLocale, "-", "_", -1)
pieces := strings.Split(formattedLocale, "_")
language := pieces[0]
territory := ""
if len(pieces) > 1 {
territory = strings.Split(formattedLocale, "_")[1]
}
return language, territory
}
/**
* languageServer
*/
type languageServer struct {
supportedLanguages map[language.Tag]string // the string can be used to link to a localization file for that language
fallbackLanguage language.Tag
}
var (
languageServerSingletonOnce sync.Once
languageServerInstance *languageServer
languageServerMutex = &sync.Mutex{}
)
func LanguageServer() *languageServer {
languageServerSingletonOnce.Do(func() {
if languageServerInstance == nil {
languageServerInstance = new(languageServer)
}
})
return languageServerInstance
}
// SetSupportedLanguages defines the supported languages checked against in other funcs.
// The values (type `string`) can be used to link to a localization file for that language.
func (server *languageServer) SetSupportedLanguages(supported map[language.Tag]string) {
languageServerMutex.Lock()
defer languageServerMutex.Unlock()
server.supportedLanguages = supported
}
// GetSupportedLanguages returns the supported languages.
func (server *languageServer) GetSupportedLanguages() map[language.Tag]string {
languageServerMutex.Lock()
defer languageServerMutex.Unlock()
return server.supportedLanguages
}
// SetFallbackLanguage defines the language used as a fallback language Tag if any other func returns no valid value.
func (server *languageServer) SetFallbackLanguage(fallback language.Tag) {
languageServerMutex.Lock()
defer languageServerMutex.Unlock()
server.fallbackLanguage = fallback
}
// GetFallbackLanguage returns the language fallback.
func (server *languageServer) GetFallbackLanguage() language.Tag {
languageServerMutex.Lock()
defer languageServerMutex.Unlock()
return server.fallbackLanguage
}
// DetectSupportedLanguage returns the language tag detected from the system.
// If it's not supported, it returns the fallback.
// Returns ErrLangParse, if library cannot detect language or parse value given from your operating system.
// Returns ErrLangFallbackUndefined, if fallback is undefined.
// Returns ErrLangFallbackUnsupported, if fallaback is defined but unsupported.
// If you want to check for jibberjabber errors, call `jibberjabber.IsError()`.
func (server *languageServer) DetectSupportedLanguage() (language.Tag, error) {
tag, err := DetectLanguageTag()
if err != nil {
return language.Und, fmt.Errorf("%v: %w", ErrLangParse.Error(), err)
}
if server.LanguageTagIsSupported(tag) {
return tag, nil
}
languageServerMutex.Lock()
defer languageServerMutex.Unlock()
fallbackTag := server.fallbackLanguage
if fallbackTag == language.Und {
return language.Und, ErrLangFallbackUndefined
} else if _, supported := server.supportedLanguages[fallbackTag]; !supported {
return language.Und, ErrLangFallbackUnsupported
} else {
return fallbackTag, nil
}
}
// ListSupportedLanguages returns the language tags in a language.Tag slice.
func (server *languageServer) ListSupportedLanguages() []language.Tag {
languageServerMutex.Lock()
defer languageServerMutex.Unlock()
supportedLangTags := make([]language.Tag, 0, len(server.supportedLanguages))
for tag := range server.supportedLanguages {
supportedLangTags = append(supportedLangTags, tag)
}
return supportedLangTags
}
// ListSupportedLanguagesAsStrings returns the language tags in a slice of string representation of the language tags.
func (server *languageServer) ListSupportedLanguagesAsStrings() []string {
languageServerMutex.Lock()
defer languageServerMutex.Unlock()
supportedLangs := make([]string, 0, len(server.supportedLanguages))
for tag := range server.supportedLanguages {
supportedLangs = append(supportedLangs, tag.String())
}
return supportedLangs
}
// ListSupportedLanguagesAsStringsSorted returns the language tags in a slice of string representation of the language tags, alphabetically sorted.
func (server *languageServer) ListSupportedLanguagesAsStringsSorted() []string {
supportedLangs := server.ListSupportedLanguagesAsStrings()
sort.Strings(supportedLangs)
return supportedLangs
}
// ListSupportedLanguagesForDisplay returns the language tags in a slice of human readable strings.
func (server *languageServer) ListSupportedLanguagesForDisplay() []string {
languageServerMutex.Lock()
defer languageServerMutex.Unlock()
supportedLangs := make([]string, 0, len(server.supportedLanguages))
for tag := range server.supportedLanguages {
supportedLangs = append(supportedLangs, display.Self.Name(tag))
}
return supportedLangs
}
// ListSupportedLanguagesForDisplaySorted returns the language tags in a string slice, alphabetically sorted.
func (server *languageServer) ListSupportedLanguagesForDisplaySorted() []string {
supportedLangs := server.ListSupportedLanguagesForDisplay()
sort.Strings(supportedLangs)
return supportedLangs
}
// ListSupportedLanguagesSorted returns the language tags + their strings sorted alphabetically by string.
// Use the elements for the first return value as key for the second return value.
func (server *languageServer) ListSupportedLanguagesSorted() ([]string, map[string]language.Tag) {
languageServerMutex.Lock()
defer languageServerMutex.Unlock()
supportedLangs := make([]string, 0, len(server.supportedLanguages))
supportedLangTags := make(map[string]language.Tag)
for tag := range server.supportedLanguages {
name := display.Self.Name(tag)
supportedLangs = append(supportedLangs, name)
supportedLangTags[name] = tag
}
sort.Strings(supportedLangs)
return supportedLangs, supportedLangTags
}
// LanguageIsSupported returns true if the given BCP 47 string is in the list of supported languages.
// Returns ErrLangParse, if any parsing issue occured.
// If you want to check for jibberjabber errors, call `jibberjabber.IsError()`.
func (server *languageServer) LanguageIsSupported(bcp string) (bool, error) {
languageServerMutex.Lock()
defer languageServerMutex.Unlock()
lang, parseErr := language.Parse(bcp)
if parseErr != nil {
return false, fmt.Errorf("%v: %w", ErrLangParse.Error(), parseErr)
}
_, supported := server.supportedLanguages[lang]
return supported, nil
}
// LanguageTagIsSupported returns true if the given language tag is in the list of supported languages.
func (server *languageServer) LanguageTagIsSupported(lang language.Tag) bool {
languageServerMutex.Lock()
defer languageServerMutex.Unlock()
_, supported := server.supportedLanguages[lang]
return supported
}
// StringToLanguageTag returns language tag for given BCP 47 string.
// Returns ErrLangParse, if parsing fails.
// If you want to check for jibberjabber errors, call `jibberjabber.IsError()`.
func (server *languageServer) StringToLanguageTag(bcp string) (language.Tag, error) {
languageServerMutex.Lock()
defer languageServerMutex.Unlock()
lang, parseErr := language.Parse(bcp)
if parseErr != nil {
return language.Und, fmt.Errorf("%v: %w", ErrLangParse.Error(), parseErr)
}
return lang, nil
}
// StringToSupportedLanguageTag returns language tag for given BCP 47 string.
// Returns specified fallback, if language is not supported or parsing to language.Tag fails.
// Returns ErrLangUnsupported, if language could be parsed, but is not supported.
// Returns ErrLangFallbackUndefined, if ErrLangUnsupported and fallback is undefined.
// Returns ErrLangFallbackUnsupported, if ErrLangUnsupported and fallaback is defined but unsupported.
func (server *languageServer) StringToSupportedLanguageTag(bcp string) (language.Tag, error) {
languageServerMutex.Lock()
defer languageServerMutex.Unlock()
var err error
lang, parseErr := language.Parse(bcp)
if parseErr == nil {
if _, supported := server.supportedLanguages[lang]; supported {
return lang, nil
} else {
err = ErrLangUnsupported
}
}
lang = server.fallbackLanguage
if _, supported := server.supportedLanguages[lang]; !supported {
if server.fallbackLanguage == language.Und {
err = ErrLangFallbackUndefined
} else {
err = ErrLangFallbackUnsupported
}
}
return lang, err
}
// GetSupportedLanguageValue returns the value for given BCP 47 string.
// If parsing fails or the language is not supported, it will use the fallback's value.
// Returns ErrLangUnsupported, if language could be parsed, but is not supported.
// Returns ErrLangFallbackUndefined, if ErrLangUnsupported and fallback is undefined.
// Returns ErrLangFallbackUnsupported, if ErrLangUnsupported and fallaback is defined but unsupported.
func (server *languageServer) GetSupportedLanguageValue(bcp string) (string, error) {
tag, err := server.StringToSupportedLanguageTag(bcp)
if errors.Is(err, ErrLangFallbackUndefined) || errors.Is(err, ErrLangFallbackUnsupported) {
return "", err
}
languageServerMutex.Lock()
defer languageServerMutex.Unlock()
return server.supportedLanguages[tag], err
}
// GetSupportedLanguageValueByTag returns the value of the requested language tag.
// If the language is not supported, it will use the fallback's value.
// Returns ErrLangUnsupported, if language could be parsed, but is not supported.
// Returns ErrLangFallbackUndefined, if ErrLangUnsupported and fallback is undefined.
// Returns ErrLangFallbackUnsupported, if ErrLangUnsupported and fallaback is defined but unsupported.
func (server *languageServer) GetSupportedLanguageValueByTag(lang language.Tag) (string, error) {
languageServerMutex.Lock()
defer languageServerMutex.Unlock()
var err error
value, supported := server.supportedLanguages[lang]
if !supported {
err = ErrLangUnsupported
value, supported = server.supportedLanguages[server.fallbackLanguage]
if !supported {
if server.fallbackLanguage == language.Und {
err = ErrLangFallbackUndefined
} else {
err = ErrLangFallbackUnsupported
}
}
}
return value, err
}
// IsError checks an error you received from one of jibberjabber's funcs for a jibberjabber error like `ErrLangDetectFail`.
// Reason you cannot use e.g. `errors.Is()`: currently, golang does not allow native chain-wrapping errors. Therefore, `errors.Unwrap()`, `errors.Is()` & Co. won't return `true` for jibberjabber errors.
func IsError(err error, jjError error) bool {
return strings.HasPrefix(err.Error(), jjError.Error())
}