forked from jdkeke142/lingo-toml
-
Notifications
You must be signed in to change notification settings - Fork 1
/
lingo.go
154 lines (142 loc) · 3.62 KB
/
lingo.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
package lingo
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/BurntSushi/toml"
"github.com/shurcooL/httpfs/vfsutil"
)
func OSFS() http.FileSystem {
return osfs{}
}
type osfs struct{}
func (_ osfs) Open(name string) (http.File, error) {
return os.Open(name)
}
// Lingo is a translation bundle of all translations by locale, as well as
// default locale and list of supported locales
type Lingo struct {
bundle map[string]Translations
deflt string
supported []Locale
}
// New creates the Lingo bundle. `deflt` is the default locale, which is used
// when the requested locale is not found, and when translations are not found
// in the requested locale. `path` is the absolute or relative path to the
// folder of TOML translations. `fs` is either a FileSystem or null, used to
// locate the path and translation files.
func New(deflt, path string, fs http.FileSystem) (*Lingo, error) {
if fs == nil {
fs = OSFS()
}
l := &Lingo{
bundle: make(map[string]Translations),
deflt: deflt,
supported: make([]Locale, 0),
}
err := vfsutil.Walk(fs, path, func(pth string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() { // We skip these
return nil
}
fileName := info.Name()
if !strings.HasSuffix(fileName, ".toml") {
return nil
}
f, err := fs.Open(pth)
if err != nil {
return err
}
dat, err := ioutil.ReadAll(f)
if err != nil {
return err
}
t := Translations{
transl: make(map[string]interface{}),
}
err = toml.Unmarshal(dat, &t.transl)
if err != nil {
return fmt.Errorf("in file %s: %w", fileName, err)
}
locale := strings.TrimSuffix(fileName, filepath.Ext(fileName))
if locale != l.deflt {
t.def = l
}
l.supported = append(l.supported, ParseLocale(locale))
l.bundle[locale] = t
return nil
})
return l, err
}
// TranslationsForRequest will get the best matched T for given
// Request. If no T is found, returns default T
func (l *Lingo) TranslationsForRequest(r *http.Request) Translations {
locales := GetLocales(r)
for _, locale := range locales {
t, exists := l.bundle[locales[0].Name()]
if exists {
return t
}
for _, sup := range l.supported {
if locale.Lang == sup.Lang {
return l.bundle[sup.Name()]
}
}
}
return l.bundle[l.deflt]
}
// TranslationsForLocale will get the T for specific locale.
// If no locale is found, returns default T
func (l *Lingo) TranslationsForLocale(locale string) Translations {
if t, exists := l.bundle[locale]; exists {
return t
}
return l.bundle[l.deflt]
}
// Translations represents translations map for specific locale
type Translations struct {
def *Lingo
transl map[string]interface{}
}
// Value traverses the translations map and finds translation for
// given key. If no translation is found, returns value of given key.
func (t Translations) Value(key string, args ...string) string {
if v, ok := t.transl[key]; ok {
if s, ok := v.(string); ok {
return t.sprintf(s, args)
}
}
ss := strings.Split(key, ".")
cm := t.transl
for _, k := range ss {
if m, ok := cm[k].(map[string]interface{}); ok {
cm = m
continue
}
if v, ok := cm[k]; ok {
if s, ok := v.(string); ok {
return t.sprintf(s, args)
}
break
}
}
if t.def != nil {
return t.def.TranslationsForLocale(t.def.deflt).Value(key, args...)
}
return key
}
// sprintf replaces the argument placeholders with given arguments
func (t Translations) sprintf(value string, args []string) string {
res := value
for i := 0; i < len(args); i++ {
tok := "{" + strconv.Itoa(i) + "}"
res = strings.Replace(res, tok, args[i], -1)
}
return res
}