Skip to content

Commit dfd3da1

Browse files
Merge pull request #73 from m-horky/multilang
Add support for multiple languages
2 parents 5680cf2 + 9a5081d commit dfd3da1

File tree

3 files changed

+210
-76
lines changed

3 files changed

+210
-76
lines changed

gotext.go

+158-74
Original file line numberDiff line numberDiff line change
@@ -24,54 +24,62 @@ package gotext
2424

2525
import (
2626
"encoding/gob"
27+
"strings"
2728
"sync"
2829
)
2930

3031
// Global environment variables
3132
type config struct {
3233
sync.RWMutex
3334

35+
// Path to library directory where all locale directories and Translation files are.
36+
library string
37+
3438
// Default domain to look at when no domain is specified. Used by package level functions.
3539
domain string
3640

3741
// Language set.
38-
language string
39-
40-
// Path to library directory where all locale directories and Translation files are.
41-
library string
42+
languages []string
4243

4344
// Storage for package level methods
44-
storage *Locale
45+
locales []*Locale
4546
}
4647

4748
var globalConfig *config
4849

4950
func init() {
5051
// Init default configuration
5152
globalConfig = &config{
52-
domain: "default",
53-
language: "en_US",
54-
library: "/usr/local/share/locale",
55-
storage: nil,
53+
domain: "default",
54+
languages: []string{"en_US"},
55+
library: "/usr/local/share/locale",
56+
locales: nil,
5657
}
5758

5859
// Register Translator types for gob encoding
5960
gob.Register(TranslatorEncoding{})
6061
}
6162

62-
// loadStorage creates a new Locale object at package level based on the Global variables settings.
63+
// loadStorage creates a new Locale object at package level based on the Global variables settings
64+
// for every language specified using Configure.
6365
// It's called automatically when trying to use Get or GetD methods.
6466
func loadStorage(force bool) {
6567
globalConfig.Lock()
6668

67-
if globalConfig.storage == nil || force {
68-
globalConfig.storage = NewLocale(globalConfig.library, globalConfig.language)
69+
if globalConfig.locales == nil || force {
70+
var locales []*Locale
71+
for _, language := range globalConfig.languages {
72+
locales = append(locales, NewLocale(globalConfig.library, language))
73+
}
74+
globalConfig.locales = locales
6975
}
7076

71-
if _, ok := globalConfig.storage.Domains[globalConfig.domain]; !ok || force {
72-
globalConfig.storage.AddDomain(globalConfig.domain)
77+
for _, locale := range globalConfig.locales {
78+
if _, ok := locale.Domains[globalConfig.domain]; !ok || force {
79+
locale.AddDomain(globalConfig.domain)
80+
}
81+
locale.SetDomain(globalConfig.domain)
7382
}
74-
globalConfig.storage.SetDomain(globalConfig.domain)
7583

7684
globalConfig.Unlock()
7785
}
@@ -80,8 +88,9 @@ func loadStorage(force bool) {
8088
func GetDomain() string {
8189
var dom string
8290
globalConfig.RLock()
83-
if globalConfig.storage != nil {
84-
dom = globalConfig.storage.GetDomain()
91+
if globalConfig.locales != nil {
92+
// All locales have the same domain
93+
dom = globalConfig.locales[0].GetDomain()
8594
}
8695
if dom == "" {
8796
dom = globalConfig.domain
@@ -96,28 +105,42 @@ func GetDomain() string {
96105
func SetDomain(dom string) {
97106
globalConfig.Lock()
98107
globalConfig.domain = dom
99-
if globalConfig.storage != nil {
100-
globalConfig.storage.SetDomain(dom)
108+
if globalConfig.locales != nil {
109+
for _, locale := range globalConfig.locales {
110+
locale.SetDomain(dom)
111+
}
101112
}
102113
globalConfig.Unlock()
103114

104115
loadStorage(true)
105116
}
106117

107-
// GetLanguage is the language getter for the package configuration
118+
// GetLanguage returns the language gotext will translate into.
119+
// If multiple languages have been supplied, the first one will be returned.
108120
func GetLanguage() string {
109-
globalConfig.RLock()
110-
lang := globalConfig.language
111-
globalConfig.RUnlock()
121+
return GetLanguages()[0]
122+
}
112123

113-
return lang
124+
// GetLanguages returns all languages that have been supplied.
125+
func GetLanguages() []string {
126+
globalConfig.RLock()
127+
defer globalConfig.RUnlock()
128+
return globalConfig.languages
114129
}
115130

116-
// SetLanguage sets the language code to be used at package level.
131+
// SetLanguage sets the language code (or colon separated language codes) to be used at package level.
117132
// It reloads the corresponding Translation file.
118133
func SetLanguage(lang string) {
119134
globalConfig.Lock()
120-
globalConfig.language = SimplifiedLocale(lang)
135+
var languages []string
136+
for _, language := range strings.Split(lang, ":") {
137+
language = SimplifiedLocale(language)
138+
languages = append(languages, language)
139+
if language == "C" {
140+
break
141+
}
142+
}
143+
globalConfig.languages = languages
121144
globalConfig.Unlock()
122145

123146
loadStorage(true)
@@ -132,7 +155,7 @@ func GetLibrary() string {
132155
return lib
133156
}
134157

135-
// SetLibrary sets the root path for the loale directories and files to be used at package level.
158+
// SetLibrary sets the root path for the locale directories and files to be used at package level.
136159
// It reloads the corresponding Translation file.
137160
func SetLibrary(lib string) {
138161
globalConfig.Lock()
@@ -173,7 +196,15 @@ func SetStorage(storage *Locale) {
173196
func Configure(lib, lang, dom string) {
174197
globalConfig.Lock()
175198
globalConfig.library = lib
176-
globalConfig.language = SimplifiedLocale(lang)
199+
var languages []string
200+
for _, language := range strings.Split(lang, ":") {
201+
language = SimplifiedLocale(language)
202+
languages = append(languages, language)
203+
if language == "C" {
204+
break
205+
}
206+
}
207+
globalConfig.languages = languages
177208
globalConfig.domain = dom
178209
globalConfig.Unlock()
179210

@@ -198,16 +229,20 @@ func GetD(dom, str string, vars ...interface{}) string {
198229
// Try to load default package Locale storage
199230
loadStorage(false)
200231

201-
// Return Translation
202232
globalConfig.RLock()
233+
defer globalConfig.RUnlock()
203234

204-
if _, ok := globalConfig.storage.Domains[dom]; !ok {
205-
globalConfig.storage.AddDomain(dom)
235+
var tr string
236+
for i, locale := range globalConfig.locales {
237+
if _, ok := locale.Domains[dom]; !ok {
238+
locale.AddDomain(dom)
239+
}
240+
if !locale.IsTranslatedD(dom, str) && i < (len(globalConfig.locales)-1) {
241+
continue
242+
}
243+
tr = locale.GetD(dom, str, vars...)
244+
break
206245
}
207-
208-
tr := globalConfig.storage.GetD(dom, str, vars...)
209-
globalConfig.RUnlock()
210-
211246
return tr
212247
}
213248

@@ -217,16 +252,20 @@ func GetND(dom, str, plural string, n int, vars ...interface{}) string {
217252
// Try to load default package Locale storage
218253
loadStorage(false)
219254

220-
// Return Translation
221255
globalConfig.RLock()
256+
defer globalConfig.RUnlock()
222257

223-
if _, ok := globalConfig.storage.Domains[dom]; !ok {
224-
globalConfig.storage.AddDomain(dom)
258+
var tr string
259+
for i, locale := range globalConfig.locales {
260+
if _, ok := locale.Domains[dom]; !ok {
261+
locale.AddDomain(dom)
262+
}
263+
if !locale.IsTranslatedND(dom, str, n) && i < (len(globalConfig.locales)-1) {
264+
continue
265+
}
266+
tr = locale.GetND(dom, str, plural, n, vars...)
267+
break
225268
}
226-
227-
tr := globalConfig.storage.GetND(dom, str, plural, n, vars...)
228-
globalConfig.RUnlock()
229-
230269
return tr
231270
}
232271

@@ -248,11 +287,17 @@ func GetDC(dom, str, ctx string, vars ...interface{}) string {
248287
// Try to load default package Locale storage
249288
loadStorage(false)
250289

251-
// Return Translation
252290
globalConfig.RLock()
253-
tr := globalConfig.storage.GetDC(dom, str, ctx, vars...)
254-
globalConfig.RUnlock()
291+
defer globalConfig.RUnlock()
255292

293+
var tr string
294+
for i, locale := range globalConfig.locales {
295+
if !locale.IsTranslatedDC(dom, str, ctx) && i < (len(globalConfig.locales)-1) {
296+
continue
297+
}
298+
tr = locale.GetDC(dom, str, ctx, vars...)
299+
break
300+
}
256301
return tr
257302
}
258303

@@ -264,62 +309,101 @@ func GetNDC(dom, str, plural string, n int, ctx string, vars ...interface{}) str
264309

265310
// Return Translation
266311
globalConfig.RLock()
267-
tr := globalConfig.storage.GetNDC(dom, str, plural, n, ctx, vars...)
268-
globalConfig.RUnlock()
312+
defer globalConfig.RUnlock()
269313

314+
var tr string
315+
for i, locale := range globalConfig.locales {
316+
if !locale.IsTranslatedNDC(dom, str, n, ctx) && i < (len(globalConfig.locales)-1) {
317+
continue
318+
}
319+
tr = locale.GetNDC(dom, str, plural, n, ctx, vars...)
320+
break
321+
}
270322
return tr
271323
}
272324

273-
// IsTranslated reports whether a string is translated
274-
func IsTranslated(str string) bool {
275-
return IsTranslatedND(GetDomain(), str, 0)
325+
// IsTranslated reports whether a string is translated in given languages.
326+
// When the langs argument is omitted, the output of GetLanguages is used.
327+
func IsTranslated(str string, langs ...string) bool {
328+
return IsTranslatedND(GetDomain(), str, 0, langs...)
276329
}
277330

278-
// IsTranslatedN reports whether a plural string is translated
279-
func IsTranslatedN(str string, n int) bool {
280-
return IsTranslatedND(GetDomain(), str, n)
331+
// IsTranslatedN reports whether a plural string is translated in given languages.
332+
// When the langs argument is omitted, the output of GetLanguages is used.
333+
func IsTranslatedN(str string, n int, langs ...string) bool {
334+
return IsTranslatedND(GetDomain(), str, n, langs...)
281335
}
282336

283-
// IsTranslatedD reports whether a domain string is translated
284-
func IsTranslatedD(dom, str string) bool {
285-
return IsTranslatedND(dom, str, 0)
337+
// IsTranslatedD reports whether a domain string is translated in given languages.
338+
// When the langs argument is omitted, the output of GetLanguages is used.
339+
func IsTranslatedD(dom, str string, langs ...string) bool {
340+
return IsTranslatedND(dom, str, 0, langs...)
286341
}
287342

288-
// IsTranslatedND reports whether a plural domain string is translated
289-
func IsTranslatedND(dom, str string, n int) bool {
343+
// IsTranslatedND reports whether a plural domain string is translated in any of given languages.
344+
// When the langs argument is omitted, the output of GetLanguages is used.
345+
func IsTranslatedND(dom, str string, n int, langs ...string) bool {
346+
if len(langs) == 0 {
347+
langs = GetLanguages()
348+
}
349+
290350
loadStorage(false)
291351

292352
globalConfig.RLock()
293353
defer globalConfig.RUnlock()
294354

295-
if _, ok := globalConfig.storage.Domains[dom]; !ok {
296-
globalConfig.storage.AddDomain(dom)
297-
}
355+
for _, lang := range langs {
356+
lang = SimplifiedLocale(lang)
298357

299-
return globalConfig.storage.IsTranslatedND(dom, str, n)
358+
for _, supportedLocale := range globalConfig.locales {
359+
if lang != supportedLocale.GetActualLanguage(dom) {
360+
continue
361+
}
362+
return supportedLocale.IsTranslatedND(dom, str, n)
363+
}
364+
}
365+
return false
300366
}
301367

302-
// IsTranslatedC reports whether a context string is translated
303-
func IsTranslatedC(str, ctx string) bool {
304-
return IsTranslatedNDC(GetDomain(), str, 0, ctx)
368+
// IsTranslatedC reports whether a context string is translated in given languages.
369+
// When the langs argument is omitted, the output of GetLanguages is used.
370+
func IsTranslatedC(str, ctx string, langs ...string) bool {
371+
return IsTranslatedNDC(GetDomain(), str, 0, ctx, langs...)
305372
}
306373

307-
// IsTranslatedNC reports whether a plural context string is translated
308-
func IsTranslatedNC(str string, n int, ctx string) bool {
309-
return IsTranslatedNDC(GetDomain(), str, n, ctx)
374+
// IsTranslatedNC reports whether a plural context string is translated in given languages.
375+
// When the langs argument is omitted, the output of GetLanguages is used.
376+
func IsTranslatedNC(str string, n int, ctx string, langs ...string) bool {
377+
return IsTranslatedNDC(GetDomain(), str, n, ctx, langs...)
310378
}
311379

312-
// IsTranslatedDC reports whether a domain context string is translated
313-
func IsTranslatedDC(dom, str, ctx string) bool {
314-
return IsTranslatedNDC(dom, str, 0, ctx)
380+
// IsTranslatedDC reports whether a domain context string is translated in given languages.
381+
// When the langs argument is omitted, the output of GetLanguages is used.
382+
func IsTranslatedDC(dom, str, ctx string, langs ...string) bool {
383+
return IsTranslatedNDC(dom, str, 0, ctx, langs...)
315384
}
316385

317-
// IsTranslatedNDC reports whether a plural domain context string is translated
318-
func IsTranslatedNDC(dom, str string, n int, ctx string) bool {
386+
// IsTranslatedNDC reports whether a plural domain context string is translated in any of given languages.
387+
// When the langs argument is omitted, the output of GetLanguages is used.
388+
func IsTranslatedNDC(dom, str string, n int, ctx string, langs ...string) bool {
389+
if len(langs) == 0 {
390+
langs = GetLanguages()
391+
}
392+
319393
loadStorage(false)
320394

321395
globalConfig.RLock()
322396
defer globalConfig.RUnlock()
323397

324-
return globalConfig.storage.IsTranslatedNDC(dom, str, n, ctx)
398+
for _, lang := range langs {
399+
lang = SimplifiedLocale(lang)
400+
401+
for _, locale := range globalConfig.locales {
402+
if lang != locale.GetActualLanguage(dom) {
403+
continue
404+
}
405+
return locale.IsTranslatedNDC(dom, str, n, ctx)
406+
}
407+
}
408+
return false
325409
}

0 commit comments

Comments
 (0)