mirror of
https://github.com/retailcrm/mg-transport-core.git
synced 2024-11-24 22:26:04 +03:00
Add localizer interfaces (#46)
* add localizer interfaces * add 1.18 to the supported versions * use go 1.17 for lint for now * use localizer interfaces in the library * fix incorrect naming of the HTTPResponseLocalizer interface * fix coverage collection
This commit is contained in:
parent
b260c8fb50
commit
e7d06fa208
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
@ -22,6 +22,7 @@ jobs:
|
||||
- name: Set up Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
# TODO: Should migrate to 1.18 later
|
||||
go-version: '1.17'
|
||||
- name: Get dependencies
|
||||
run: go mod tidy
|
||||
@ -35,7 +36,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: ['1.16', '1.17']
|
||||
go-version: ['1.16', '1.17', '1.18']
|
||||
steps:
|
||||
- name: Set up Go ${{ matrix.go-version }}
|
||||
uses: actions/setup-go@v2
|
||||
@ -49,6 +50,6 @@ jobs:
|
||||
run: go test ./... -v -cpu 2 -timeout 10s -race -cover -coverprofile=coverage.txt -covermode=atomic
|
||||
- name: Coverage
|
||||
run: |
|
||||
go get -v -u github.com/axw/gocov/gocov
|
||||
go install github.com/axw/gocov/gocov@latest
|
||||
gocov convert ./coverage.txt | gocov report
|
||||
bash <(curl -s https://codecov.io/bash)
|
@ -137,7 +137,7 @@ func (e *Engine) Prepare() *Engine {
|
||||
e.LocaleMatcher = DefaultLocalizerMatcher()
|
||||
}
|
||||
|
||||
if e.isUnd(e.Localizer.LanguageTag) {
|
||||
if e.isUnd(e.Localizer.Language()) {
|
||||
e.Localizer.LanguageTag = DefaultLanguage
|
||||
}
|
||||
|
||||
|
@ -38,16 +38,60 @@ type Localizer struct {
|
||||
TranslationsPath string
|
||||
}
|
||||
|
||||
// LocalizerInterface contains entire public interface of the localizer component.
|
||||
type LocalizerInterface interface {
|
||||
GetLocalizedMessage(messageID string) string
|
||||
GetLocalizedTemplateMessage(messageID string, templateData map[string]interface{}) string
|
||||
Localize(messageID string) (string, error)
|
||||
MessageLocalizer
|
||||
LocalizerWithMiddleware
|
||||
LocalizerWithFuncs
|
||||
LocaleControls
|
||||
HTTPResponseLocalizer
|
||||
CloneableLocalizer
|
||||
}
|
||||
|
||||
// MessageLocalizer can localize regular strings and strings with template parameters.
|
||||
type MessageLocalizer interface {
|
||||
GetLocalizedMessage(string) string
|
||||
GetLocalizedTemplateMessage(string, map[string]interface{}) string
|
||||
Localize(string) (string, error)
|
||||
LocalizeTemplateMessage(string, map[string]interface{}) (string, error)
|
||||
}
|
||||
|
||||
// LocalizerWithMiddleware can provide middlewares for usage in the app.
|
||||
type LocalizerWithMiddleware interface {
|
||||
LocalizationMiddleware() gin.HandlerFunc
|
||||
}
|
||||
|
||||
// LocalizerWithFuncs can provide template functions.
|
||||
type LocalizerWithFuncs interface {
|
||||
LocalizationFuncMap() template.FuncMap
|
||||
}
|
||||
|
||||
// LocaleControls is an instance of localizer with exposed locale controls.
|
||||
type LocaleControls interface {
|
||||
Preload([]language.Tag)
|
||||
SetLocale(string)
|
||||
SetLanguage(language.Tag)
|
||||
Language() language.Tag
|
||||
LoadTranslations()
|
||||
}
|
||||
|
||||
// HTTPResponseLocalizer can localize strings and return them with HTTP error codes.
|
||||
type HTTPResponseLocalizer interface {
|
||||
BadRequestLocalized(string) (int, interface{})
|
||||
UnauthorizedLocalized(string) (int, interface{})
|
||||
ForbiddenLocalized(string) (int, interface{})
|
||||
InternalServerErrorLocalized(string) (int, interface{})
|
||||
}
|
||||
|
||||
// CloneableLocalizer is a localizer which can clone itself.
|
||||
type CloneableLocalizer interface {
|
||||
Clone() CloneableLocalizer
|
||||
}
|
||||
|
||||
// NewLocalizer returns localizer instance with specified parameters.
|
||||
// Usage:
|
||||
// NewLocalizer(language.English, DefaultLocalizerMatcher(), "translations")
|
||||
func NewLocalizer(locale language.Tag, matcher language.Matcher, translationsPath string) *Localizer {
|
||||
func NewLocalizer(locale language.Tag, matcher language.Matcher, translationsPath string) LocalizerInterface {
|
||||
localizer := &Localizer{
|
||||
i18nStorage: &sync.Map{},
|
||||
LocaleMatcher: matcher,
|
||||
@ -66,7 +110,7 @@ func NewLocalizer(locale language.Tag, matcher language.Matcher, translationsPat
|
||||
// TODO This code should be covered with tests.
|
||||
func NewLocalizerFS(
|
||||
locale language.Tag, matcher language.Matcher, translationsFS fs.FS,
|
||||
) *Localizer {
|
||||
) LocalizerInterface {
|
||||
localizer := &Localizer{
|
||||
i18nStorage: &sync.Map{},
|
||||
LocaleMatcher: matcher,
|
||||
@ -97,7 +141,7 @@ func DefaultLocalizerMatcher() language.Matcher {
|
||||
// Clone *core.Localizer. Clone shares it's translations with the parent localizer. Language tag will not be shared.
|
||||
// Because of that you can change clone's language without affecting parent localizer.
|
||||
// This method should be used when LocalizationMiddleware is not feasible (outside of *gin.HandlerFunc).
|
||||
func (l *Localizer) Clone() *Localizer {
|
||||
func (l *Localizer) Clone() CloneableLocalizer {
|
||||
clone := &Localizer{
|
||||
i18nStorage: l.i18nStorage,
|
||||
TranslationsFS: l.TranslationsFS,
|
||||
@ -122,7 +166,7 @@ func (l *Localizer) Clone() *Localizer {
|
||||
// engine.Use(localizer.LocalizationMiddleware())
|
||||
func (l *Localizer) LocalizationMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
clone := l.Clone()
|
||||
clone := l.Clone().(LocaleControls)
|
||||
clone.SetLocale(c.GetHeader("Accept-Language"))
|
||||
c.Set(LocalizerContextKey, clone)
|
||||
}
|
||||
@ -262,7 +306,7 @@ func (l *Localizer) isUnd(tag language.Tag) bool {
|
||||
|
||||
// getCurrentLocalizer returns *i18n.Localizer with current language tag.
|
||||
func (l *Localizer) getCurrentLocalizer() *i18n.Localizer {
|
||||
return l.getLocalizer(l.LanguageTag)
|
||||
return l.getLocalizer(l.Language())
|
||||
}
|
||||
|
||||
// SetLocale will change language for current localizer.
|
||||
@ -287,6 +331,11 @@ func (l *Localizer) SetLanguage(tag language.Tag) {
|
||||
l.LoadTranslations()
|
||||
}
|
||||
|
||||
// Language returns current language tag.
|
||||
func (l *Localizer) Language() language.Tag {
|
||||
return l.LanguageTag
|
||||
}
|
||||
|
||||
// FetchLanguage will load language from tag
|
||||
//
|
||||
// Deprecated: Use `(*core.Localizer).LoadTranslations()` instead.
|
||||
@ -344,13 +393,13 @@ func (l *Localizer) InternalServerErrorLocalized(err string) (int, interface{})
|
||||
|
||||
// GetContextLocalizer returns localizer from context if it is present there.
|
||||
// Language will be set using Accept-Language header and root language tag.
|
||||
func GetContextLocalizer(c *gin.Context) (loc *Localizer, ok bool) {
|
||||
func GetContextLocalizer(c *gin.Context) (loc LocalizerInterface, ok bool) {
|
||||
loc, ok = extractLocalizerFromContext(c)
|
||||
if loc != nil {
|
||||
loc.SetLocale(c.GetHeader("Accept-Language"))
|
||||
|
||||
lang := GetRootLanguageTag(loc.LanguageTag)
|
||||
if lang != loc.LanguageTag {
|
||||
lang := GetRootLanguageTag(loc.Language())
|
||||
if lang != loc.Language() {
|
||||
loc.SetLanguage(lang)
|
||||
loc.LoadTranslations()
|
||||
}
|
||||
@ -359,7 +408,7 @@ func GetContextLocalizer(c *gin.Context) (loc *Localizer, ok bool) {
|
||||
}
|
||||
|
||||
// MustGetContextLocalizer returns Localizer instance if it exists in provided context. Panics otherwise.
|
||||
func MustGetContextLocalizer(c *gin.Context) *Localizer {
|
||||
func MustGetContextLocalizer(c *gin.Context) LocalizerInterface {
|
||||
if localizer, ok := GetContextLocalizer(c); ok {
|
||||
return localizer
|
||||
}
|
||||
@ -367,13 +416,13 @@ func MustGetContextLocalizer(c *gin.Context) *Localizer {
|
||||
}
|
||||
|
||||
// extractLocalizerFromContext returns localizer from context if it exist there.
|
||||
func extractLocalizerFromContext(c *gin.Context) (*Localizer, bool) {
|
||||
func extractLocalizerFromContext(c *gin.Context) (LocalizerInterface, bool) {
|
||||
if c == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if item, ok := c.Get(LocalizerContextKey); ok {
|
||||
if localizer, ok := item.(*Localizer); ok {
|
||||
if localizer, ok := item.(LocalizerInterface); ok {
|
||||
return localizer, true
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ func createTestLangFiles(t *testing.T) {
|
||||
|
||||
type LocalizerTest struct {
|
||||
suite.Suite
|
||||
localizer *Localizer
|
||||
localizer LocalizerInterface
|
||||
}
|
||||
|
||||
func (l *LocalizerTest) SetupSuite() {
|
||||
@ -92,9 +92,9 @@ func (l *LocalizerTest) Test_LocalizationMiddleware_Context() {
|
||||
assert.NotNil(l.T(), esLocalizer)
|
||||
assert.NotNil(l.T(), ruLocalizer)
|
||||
|
||||
assert.Equal(l.T(), language.English, enLocalizer.LanguageTag)
|
||||
assert.Equal(l.T(), language.Spanish, esLocalizer.LanguageTag)
|
||||
assert.Equal(l.T(), language.Russian, ruLocalizer.LanguageTag)
|
||||
assert.Equal(l.T(), language.English, enLocalizer.Language())
|
||||
assert.Equal(l.T(), language.Spanish, esLocalizer.Language())
|
||||
assert.Equal(l.T(), language.Russian, ruLocalizer.Language())
|
||||
|
||||
assert.Equal(l.T(), "Test message", enLocalizer.GetLocalizedMessage("message"))
|
||||
assert.Equal(l.T(), "Mensaje de prueba", esLocalizer.GetLocalizedMessage("message"))
|
||||
@ -164,10 +164,10 @@ func (l *LocalizerTest) Test_Clone() {
|
||||
require.Nil(l.T(), recover())
|
||||
}()
|
||||
|
||||
localizer := l.localizer.Clone()
|
||||
localizer := l.localizer.Clone().(LocalizerInterface)
|
||||
localizer.SetLanguage(language.Russian)
|
||||
|
||||
assert.NotEqual(l.T(), l.localizer.LanguageTag, localizer.LanguageTag)
|
||||
assert.NotEqual(l.T(), l.localizer.Language(), localizer.Language())
|
||||
assert.Equal(l.T(), "Test message", l.localizer.GetLocalizedMessage("message"))
|
||||
assert.Equal(l.T(), "Тестовое сообщение", localizer.GetLocalizedMessage("message"))
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ type SentryTagged interface {
|
||||
type Sentry struct {
|
||||
SentryConfig sentry.ClientOptions
|
||||
Logger logger.Logger
|
||||
Localizer *Localizer
|
||||
Localizer MessageLocalizer
|
||||
AppInfo AppInfo
|
||||
SentryLoggerConfig SentryLoggerConfig
|
||||
ServerName string
|
||||
|
Loading…
Reference in New Issue
Block a user