-
Notifications
You must be signed in to change notification settings - Fork 327
/
http.go
132 lines (106 loc) · 3.15 KB
/
http.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
package locale
import (
"net/http"
"go.uber.org/zap"
"golang.org/x/text/language"
)
const AcceptLanguageHeader = "Accept-Language"
const ContentLanguageHeader = "Content-Language"
func DetectLanguage(ll *service) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var ctx = r.Context()
// resolve accept-language header
// Accept-Language specifies the language of the response payload.
ctx = SetAcceptLanguageToContext(ctx, resolveAcceptLanguageHeaders(r, ll))
// resolve content-language header
// Content-Language specifies the language of the request payload.
ctx = SetContentLanguageToContext(ctx, resolveContentLanguageHeaders(r.Header, ll))
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
// reads Content-Language headers from the request and returns
// parsed value as language.Tag
//
// There are 4 valid scenarios for :
// - lang == 'skip': returns und & services will (likely) ignore all translatable content
// - invalid language: (same as skip)
// - valid language: returns valid language; services will treat translatable content from the payload as translations
// - no header: returns default language; (same as valid language)
func resolveContentLanguageHeaders(h http.Header, ll *service) language.Tag {
if ll == nil || ll.Default() == nil {
return language.Und
}
var cLang = h.Get(ContentLanguageHeader)
if cLang == "" {
return ll.Default().Tag
}
if cLang == "skip" {
// more than 1 header or value equal to skip
return language.Und
}
if tag, err := language.Parse(cLang); err != nil {
return language.Und
} else {
return tag
}
}
func resolveAcceptLanguageHeaders(r *http.Request, ll *service) (tag language.Tag) {
if ll == nil || ll.Default() == nil {
// locale service does not have anything loaded...
return language.Und
}
if ll.opt.DevelopmentMode {
if err := ll.ReloadStatic(); err != nil {
// when in development mode, refresh languages for every request
ll.log.Error("failed to load locales", zap.Error(err))
return
}
}
var (
raw string
)
// try to detect the language from the request's query string:
if ll.opt.QueryStringParam != "" {
raw = r.URL.Query().Get(ll.opt.QueryStringParam)
}
// try to detect the language from the request's headers:
if len(raw) == 0 {
raw = r.Header.Get(AcceptLanguageHeader)
}
if len(raw) == 0 {
// no need for lang detection
return ll.Default().Tag
}
// parse & ignore errors
var (
preferred = ll.Default().Tag
supported = ll.Tags()
accepted, _, err = language.ParseAcceptLanguage(raw)
)
if err == nil {
// ignoring index & confidence
preferred, _, _ = language.NewMatcher(supported).Match(accepted...)
var match bool
for _, s := range supported {
if s == preferred {
match = true
break
}
}
if !match {
base, _ := preferred.Base()
preferred = language.MustParse(base.String())
}
}
if ll.opt.DevelopmentMode {
ll.log.Debug(
"language detected",
zap.String("preferred", preferred.String()),
zap.String("raw", raw),
)
}
// new request with new context
return preferred
}