-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f7473aa
commit 4ba9cbe
Showing
9 changed files
with
448 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
name: CI | ||
|
||
on: | ||
push: | ||
branches: [ '*' ] | ||
pull_request: | ||
branches: [ '*' ] | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Set up Go | ||
uses: actions/setup-go@v2 | ||
with: | ||
go-version: ^1.15 | ||
id: go | ||
|
||
- name: Checkout code | ||
uses: actions/checkout@v2 | ||
|
||
- name: Set up dependencies | ||
run: go mod download | ||
|
||
- name: Run golangci-lint | ||
uses: golangci/golangci-lint-action@v2 | ||
with: | ||
version: v1.36 | ||
|
||
- name: Run tests | ||
run: go test -v $(go list ./... | grep -v vendor) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
linters: | ||
enable: | ||
- asciicheck | ||
- bodyclose | ||
- deadcode | ||
- depguard | ||
- dogsled | ||
- dupl | ||
- errcheck | ||
- exhaustive | ||
- forbidigo | ||
- funlen | ||
- gochecknoinits | ||
- gocognit | ||
- goconst | ||
- gocritic | ||
- gocyclo | ||
- godot | ||
- godox | ||
- goerr113 | ||
- gofmt | ||
- goimports | ||
- golint | ||
- gomodguard | ||
- goprintffuncname | ||
- gosec | ||
- gosimple | ||
- govet | ||
- ineffassign | ||
- interfacer | ||
- lll | ||
- makezero | ||
- maligned | ||
- misspell | ||
- nakedret | ||
- nestif | ||
- noctx | ||
- nolintlint | ||
- prealloc | ||
- rowserrcheck | ||
- scopelint | ||
- staticcheck | ||
- structcheck | ||
- stylecheck | ||
- thelper | ||
- typecheck | ||
- unconvert | ||
- unparam | ||
- unused | ||
- varcheck | ||
- whitespace | ||
|
||
issues: | ||
exclude-rules: | ||
# Exclude some linters from running on tests files. | ||
- path: _test\.go | ||
linters: | ||
- dogsled | ||
- dupl | ||
- errcheck | ||
- forbidigo | ||
- funlen | ||
- gochecknoglobals | ||
- gocyclo | ||
- goerr113 | ||
- gosec | ||
- lll | ||
- scopelint |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,69 @@ | ||
# language | ||
Language HTTP Middleware | ||
# Language package for Golang | ||
|
||
Package language provides HTTP middleware for parsing language from HTTP request and passing it via context. | ||
|
||
## Example of reading language from Accept-Language header | ||
|
||
```golang | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"net/http/httptest" | ||
|
||
"golang.org/x/text/language" | ||
|
||
languagepkg "github.com/muonsoft/language" | ||
) | ||
|
||
func main() { | ||
h := http.HandlerFunc(func (writer http.ResponseWriter, request *http.Request) { | ||
tag := languagepkg.FromContext(request.Context()) | ||
fmt.Println("language:", tag) | ||
}) | ||
m := languagepkg.NewMiddleware(h, languagepkg.SupportedLanguages(language.English, language.Russian)) | ||
|
||
r := httptest.NewRequest(http.MethodGet, "/", nil) | ||
r.Header.Set("Accept-Language", "ru") | ||
w := httptest.NewRecorder() | ||
|
||
m.ServeHTTP(w, r) | ||
// Output: language: ru | ||
} | ||
``` | ||
|
||
## Example of reading language from Cookie | ||
|
||
```golang | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"net/http/httptest" | ||
|
||
"golang.org/x/text/language" | ||
|
||
languagepkg "github.com/muonsoft/language" | ||
) | ||
|
||
func main() { | ||
h := http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { | ||
tag := languagepkg.FromContext(request.Context()) | ||
fmt.Println("language:", tag) | ||
}) | ||
m := language.NewMiddleware( | ||
h, | ||
languagepkg.SupportedLanguages(language.English, language.Russian), | ||
languagepkg.ReadFromCookie("lang"), | ||
) | ||
|
||
r := httptest.NewRequest(http.MethodGet, "/", nil) | ||
r.AddCookie(&http.Cookie{Name: "lang", Value: "ru"}) | ||
w := httptest.NewRecorder() | ||
|
||
m.ServeHTTP(w, r) | ||
// Output: language: ru | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package language | ||
|
||
import ( | ||
"context" | ||
|
||
"golang.org/x/text/language" | ||
) | ||
|
||
type contextKey string | ||
|
||
const languageKey contextKey = "language" | ||
|
||
// FromContext returns language tag. If language tag does not exist it returns language.Und value. | ||
func FromContext(ctx context.Context) language.Tag { | ||
tag, _ := ctx.Value(languageKey).(language.Tag) | ||
|
||
return tag | ||
} | ||
|
||
// WithContext adds language tag to context. | ||
func WithContext(ctx context.Context, tag language.Tag) context.Context { | ||
return context.WithValue(ctx, languageKey, tag) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// Package language provides HTTP middleware for parsing language from HTTP request and | ||
// passing it via context. | ||
package language |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module github.com/muonsoft/language | ||
|
||
go 1.13 | ||
|
||
require golang.org/x/text v0.3.5 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= | ||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package language | ||
|
||
import ( | ||
"net/http" | ||
|
||
"golang.org/x/text/language" | ||
) | ||
|
||
// Middleware is used to parse language from Accept-Language header or custom cookie from | ||
// from current request and pass best matching language via context to next http.Handler. | ||
type Middleware struct { | ||
matcher language.Matcher | ||
readers []func(r *http.Request) string | ||
next http.Handler | ||
} | ||
|
||
type MiddlewareOption func(middleware *Middleware) | ||
|
||
// ReadFromCookie can be used to set up middleware to read language value from cookie with given name. | ||
func ReadFromCookie(name string) MiddlewareOption { | ||
return func(middleware *Middleware) { | ||
middleware.readers = append(middleware.readers, func(r *http.Request) string { | ||
cookie, _ := r.Cookie(name) | ||
return cookie.Value | ||
}) | ||
} | ||
} | ||
|
||
// ReadFromAcceptHeader can be used to set up middleware to read language value from Accept-Language header. | ||
func ReadFromAcceptHeader() MiddlewareOption { | ||
return func(middleware *Middleware) { | ||
middleware.readers = append(middleware.readers, readFromAcceptLanguageHeader) | ||
} | ||
} | ||
|
||
// SupportedLanguages is used to set up list of supported languages. See language.NewMatcher() for details. | ||
func SupportedLanguages(tags ...language.Tag) MiddlewareOption { | ||
return func(middleware *Middleware) { | ||
middleware.matcher = language.NewMatcher(tags) | ||
} | ||
} | ||
|
||
// NewMiddleware creates middleware for parsing language from Accept-Language header or a cookie | ||
// and passing its value via context. | ||
// | ||
// By default Middleware uses only English language and Accept-Language header as source. | ||
// | ||
// To set up supported languages list use SupportedLanguages option. | ||
// | ||
// To set up sources of language value use ReadFromCookie and ReadFromAcceptHeader options. Order of | ||
// sources should be preserved. | ||
func NewMiddleware(next http.Handler, options ...MiddlewareOption) *Middleware { | ||
middleware := &Middleware{next: next} | ||
|
||
for _, setOption := range options { | ||
setOption(middleware) | ||
} | ||
if middleware.matcher == nil { | ||
middleware.matcher = language.NewMatcher([]language.Tag{language.English}) | ||
} | ||
if len(middleware.readers) == 0 { | ||
middleware.readers = append(middleware.readers, readFromAcceptLanguageHeader) | ||
} | ||
|
||
return middleware | ||
} | ||
|
||
func (middleware *Middleware) ServeHTTP(writer http.ResponseWriter, request *http.Request) { | ||
sources := make([]string, 0, len(middleware.readers)) | ||
for _, readLanguage := range middleware.readers { | ||
sources = append(sources, readLanguage(request)) | ||
} | ||
|
||
tag, _ := language.MatchStrings(middleware.matcher, sources...) | ||
ctx := WithContext(request.Context(), tag) | ||
|
||
middleware.next.ServeHTTP(writer, request.WithContext(ctx)) | ||
} | ||
|
||
func readFromAcceptLanguageHeader(r *http.Request) string { | ||
return r.Header.Get("Accept-Language") | ||
} |
Oops, something went wrong.