Simplify locale package usage (refactoring)
This commit is contained in:
parent
aae9b4eb83
commit
b1e8f534ef
26 changed files with 443 additions and 299 deletions
|
@ -13,7 +13,6 @@ import (
|
|||
"time"
|
||||
|
||||
"miniflux.app/config"
|
||||
"miniflux.app/locale"
|
||||
"miniflux.app/logger"
|
||||
"miniflux.app/reader/feed"
|
||||
"miniflux.app/scheduler"
|
||||
|
@ -39,10 +38,9 @@ func Run(cfg *config.Config, store *storage.Storage) {
|
|||
}
|
||||
}()
|
||||
|
||||
translator := locale.Load()
|
||||
feedHandler := feed.NewFeedHandler(store, translator)
|
||||
feedHandler := feed.NewFeedHandler(store)
|
||||
pool := scheduler.NewWorkerPool(feedHandler, cfg.WorkerPoolSize())
|
||||
server := newServer(cfg, store, pool, feedHandler, translator)
|
||||
server := newServer(cfg, store, pool, feedHandler)
|
||||
|
||||
scheduler.NewFeedScheduler(
|
||||
store,
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"miniflux.app/api"
|
||||
"miniflux.app/config"
|
||||
"miniflux.app/fever"
|
||||
"miniflux.app/locale"
|
||||
"miniflux.app/middleware"
|
||||
"miniflux.app/reader/feed"
|
||||
"miniflux.app/scheduler"
|
||||
|
@ -21,12 +20,12 @@ import (
|
|||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func routes(cfg *config.Config, store *storage.Storage, feedHandler *feed.Handler, pool *scheduler.WorkerPool, translator *locale.Translator) *mux.Router {
|
||||
func routes(cfg *config.Config, store *storage.Storage, feedHandler *feed.Handler, pool *scheduler.WorkerPool) *mux.Router {
|
||||
router := mux.NewRouter()
|
||||
templateEngine := template.NewEngine(cfg, router, translator)
|
||||
templateEngine := template.NewEngine(cfg, router)
|
||||
apiController := api.NewController(store, feedHandler)
|
||||
feverController := fever.NewController(cfg, store)
|
||||
uiController := ui.NewController(cfg, store, pool, feedHandler, templateEngine, translator, router)
|
||||
uiController := ui.NewController(cfg, store, pool, feedHandler, templateEngine, router)
|
||||
middleware := middleware.New(cfg, store, router)
|
||||
|
||||
if cfg.BasePath() != "" {
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"time"
|
||||
|
||||
"miniflux.app/config"
|
||||
"miniflux.app/locale"
|
||||
"miniflux.app/logger"
|
||||
"miniflux.app/reader/feed"
|
||||
"miniflux.app/scheduler"
|
||||
|
@ -19,7 +18,7 @@ import (
|
|||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
func newServer(cfg *config.Config, store *storage.Storage, pool *scheduler.WorkerPool, feedHandler *feed.Handler, translator *locale.Translator) *http.Server {
|
||||
func newServer(cfg *config.Config, store *storage.Storage, pool *scheduler.WorkerPool, feedHandler *feed.Handler) *http.Server {
|
||||
certFile := cfg.CertFile()
|
||||
keyFile := cfg.KeyFile()
|
||||
certDomain := cfg.CertDomain()
|
||||
|
@ -29,7 +28,7 @@ func newServer(cfg *config.Config, store *storage.Storage, pool *scheduler.Worke
|
|||
WriteTimeout: 30 * time.Second,
|
||||
IdleTimeout: 60 * time.Second,
|
||||
Addr: cfg.ListenAddr(),
|
||||
Handler: routes(cfg, store, feedHandler, pool, translator),
|
||||
Handler: routes(cfg, store, feedHandler, pool),
|
||||
}
|
||||
|
||||
if certDomain != "" && certCache != "" {
|
||||
|
|
|
@ -22,8 +22,8 @@ func (l LocalizedError) Error() string {
|
|||
}
|
||||
|
||||
// Localize returns the translated error message.
|
||||
func (l LocalizedError) Localize(translation *locale.Language) string {
|
||||
return translation.Get(l.message, l.args...)
|
||||
func (l LocalizedError) Localize(printer *locale.Printer) string {
|
||||
return printer.Printf(l.message, l.args...)
|
||||
}
|
||||
|
||||
// NewLocalizedError returns a new LocalizedError.
|
||||
|
|
36
locale/catalog.go
Normal file
36
locale/catalog.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2018 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package locale // import "miniflux.app/locale"
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type translationDict map[string]interface{}
|
||||
type catalog map[string]translationDict
|
||||
|
||||
var defaultCatalog catalog
|
||||
|
||||
func init() {
|
||||
defaultCatalog = make(catalog)
|
||||
|
||||
for language, data := range translations {
|
||||
messages, err := parseTranslationDict(data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defaultCatalog[language] = messages
|
||||
}
|
||||
}
|
||||
|
||||
func parseTranslationDict(data string) (translationDict, error) {
|
||||
var translations translationDict
|
||||
if err := json.Unmarshal([]byte(data), &translations); err != nil {
|
||||
return nil, fmt.Errorf("invalid translation file: %v", err)
|
||||
}
|
||||
return translations, nil
|
||||
}
|
|
@ -7,14 +7,14 @@ package locale // import "miniflux.app/locale"
|
|||
import "testing"
|
||||
|
||||
func TestParserWithInvalidData(t *testing.T) {
|
||||
_, err := parseCatalogMessages(`{`)
|
||||
_, err := parseTranslationDict(`{`)
|
||||
if err == nil {
|
||||
t.Fatal(`An error should be returned when parsing invalid data`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParser(t *testing.T) {
|
||||
translations, err := parseCatalogMessages(`{"k": "v"}`)
|
||||
translations, err := parseTranslationDict(`{"k": "v"}`)
|
||||
if err != nil {
|
||||
t.Fatalf(`Unexpected parsing error: %v`, err)
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
// Copyright 2017 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package locale // import "miniflux.app/locale"
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Language represents a language in the system.
|
||||
type Language struct {
|
||||
language string
|
||||
translations catalogMessages
|
||||
}
|
||||
|
||||
// Get fetch the translation for the given key.
|
||||
func (l *Language) Get(key string, args ...interface{}) string {
|
||||
var translation string
|
||||
|
||||
str, found := l.translations[key]
|
||||
if !found {
|
||||
translation = key
|
||||
} else {
|
||||
translation = str.(string)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(translation, args...)
|
||||
}
|
||||
|
||||
// Plural returns the translation of the given key by using the language plural form.
|
||||
func (l *Language) Plural(key string, n int, args ...interface{}) string {
|
||||
translation := key
|
||||
slices, found := l.translations[key]
|
||||
|
||||
if found {
|
||||
pluralForm, found := pluralForms[l.language]
|
||||
if !found {
|
||||
pluralForm = pluralForms["default"]
|
||||
}
|
||||
|
||||
index := pluralForm(n)
|
||||
translations := slices.([]interface{})
|
||||
translation = key
|
||||
|
||||
if len(translations) > index {
|
||||
translation = translations[index].(string)
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf(translation, args...)
|
||||
}
|
|
@ -1,23 +1,9 @@
|
|||
// Copyright 2017 Frédéric Guillot. All rights reserved.
|
||||
// Copyright 2018 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package locale // import "miniflux.app/locale"
|
||||
|
||||
import "miniflux.app/logger"
|
||||
|
||||
// Load loads all translations.
|
||||
func Load() *Translator {
|
||||
translator := NewTranslator()
|
||||
|
||||
for language, tr := range translations {
|
||||
logger.Debug("Loading translation: %s", language)
|
||||
translator.AddLanguage(language, tr)
|
||||
}
|
||||
|
||||
return translator
|
||||
}
|
||||
|
||||
// AvailableLanguages returns the list of available languages.
|
||||
func AvailableLanguages() map[string]string {
|
||||
return map[string]string{
|
||||
|
|
|
@ -1,103 +1,24 @@
|
|||
// Copyright 2017 Frédéric Guillot. All rights reserved.
|
||||
// Copyright 2018 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package locale // import "miniflux.app/locale"
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestTranslateWithMissingLanguage(t *testing.T) {
|
||||
translator := NewTranslator()
|
||||
translation := translator.GetLanguage("en_US").Get("auth.username")
|
||||
func TestAvailableLanguages(t *testing.T) {
|
||||
results := AvailableLanguages()
|
||||
for k, v := range results {
|
||||
if k == "" {
|
||||
t.Errorf(`Empty language key detected`)
|
||||
}
|
||||
|
||||
if translation != "auth.username" {
|
||||
t.Errorf("Wrong translation, got %s", translation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslateWithExistingKey(t *testing.T) {
|
||||
data := `{"auth.username": "Username"}`
|
||||
translator := NewTranslator()
|
||||
translator.AddLanguage("en_US", data)
|
||||
translation := translator.GetLanguage("en_US").Get("auth.username")
|
||||
|
||||
if translation != "Username" {
|
||||
t.Errorf("Wrong translation, got %s", translation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslateWithMissingKey(t *testing.T) {
|
||||
data := `{"auth.username": "Username"}`
|
||||
translator := NewTranslator()
|
||||
translator.AddLanguage("en_US", data)
|
||||
translation := translator.GetLanguage("en_US").Get("auth.password")
|
||||
|
||||
if translation != "auth.password" {
|
||||
t.Errorf("Wrong translation, got %s", translation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslateWithMissingKeyAndPlaceholder(t *testing.T) {
|
||||
translator := NewTranslator()
|
||||
translator.AddLanguage("fr_FR", "")
|
||||
translation := translator.GetLanguage("fr_FR").Get("Status: %s", "ok")
|
||||
|
||||
if translation != "Status: ok" {
|
||||
t.Errorf("Wrong translation, got %s", translation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslatePluralWithDefaultRule(t *testing.T) {
|
||||
data := `{"number_of_users": ["Il y a %d utilisateur (%s)", "Il y a %d utilisateurs (%s)"]}`
|
||||
translator := NewTranslator()
|
||||
translator.AddLanguage("fr_FR", data)
|
||||
language := translator.GetLanguage("fr_FR")
|
||||
|
||||
translation := language.Plural("number_of_users", 1, 1, "some text")
|
||||
expected := "Il y a 1 utilisateur (some text)"
|
||||
if translation != expected {
|
||||
t.Errorf(`Wrong translation, got "%s" instead of "%s"`, translation, expected)
|
||||
}
|
||||
|
||||
translation = language.Plural("number_of_users", 2, 2, "some text")
|
||||
expected = "Il y a 2 utilisateurs (some text)"
|
||||
if translation != expected {
|
||||
t.Errorf(`Wrong translation, got "%s" instead of "%s"`, translation, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslatePluralWithRussianRule(t *testing.T) {
|
||||
data := `{"key": ["из %d книги за %d день", "из %d книг за %d дня", "из %d книг за %d дней"]}`
|
||||
translator := NewTranslator()
|
||||
translator.AddLanguage("ru_RU", data)
|
||||
language := translator.GetLanguage("ru_RU")
|
||||
|
||||
translation := language.Plural("key", 1, 1, 1)
|
||||
expected := "из 1 книги за 1 день"
|
||||
if translation != expected {
|
||||
t.Errorf(`Wrong translation, got "%s" instead of "%s"`, translation, expected)
|
||||
}
|
||||
|
||||
translation = language.Plural("key", 2, 2, 2)
|
||||
expected = "из 2 книг за 2 дня"
|
||||
if translation != expected {
|
||||
t.Errorf(`Wrong translation, got "%s" instead of "%s"`, translation, expected)
|
||||
}
|
||||
|
||||
translation = language.Plural("key", 5, 5, 5)
|
||||
expected = "из 5 книг за 5 дней"
|
||||
if translation != expected {
|
||||
t.Errorf(`Wrong translation, got "%s" instead of "%s"`, translation, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslatePluralWithMissingTranslation(t *testing.T) {
|
||||
translator := NewTranslator()
|
||||
translator.AddLanguage("fr_FR", "")
|
||||
language := translator.GetLanguage("fr_FR")
|
||||
|
||||
translation := language.Plural("number_of_users", 2)
|
||||
expected := "number_of_users"
|
||||
if translation != expected {
|
||||
t.Errorf(`Wrong translation, got "%s" instead of "%s"`, translation, expected)
|
||||
if v == "" {
|
||||
t.Errorf(`Empty language value detected`)
|
||||
}
|
||||
}
|
||||
|
||||
if _, found := results["en_US"]; !found {
|
||||
t.Errorf(`We must have at least the default language (en_US)`)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright 2018 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package locale // import "miniflux.app/locale"
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type catalogMessages map[string]interface{}
|
||||
type catalog map[string]catalogMessages
|
||||
|
||||
func parseCatalogMessages(data string) (catalogMessages, error) {
|
||||
var translations catalogMessages
|
||||
if err := json.Unmarshal([]byte(data), &translations); err != nil {
|
||||
return nil, fmt.Errorf("invalid translation file: %v", err)
|
||||
}
|
||||
return translations, nil
|
||||
}
|
|
@ -1,12 +1,14 @@
|
|||
// Copyright 2017 Frédéric Guillot. All rights reserved.
|
||||
// Copyright 2018 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package locale // import "miniflux.app/locale"
|
||||
|
||||
type pluralFormFunc func(n int) int
|
||||
|
||||
// See https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
|
||||
// And http://www.unicode.org/cldr/charts/29/supplemental/language_plural_rules.html
|
||||
var pluralForms = map[string]func(n int) int{
|
||||
var pluralForms = map[string]pluralFormFunc{
|
||||
// nplurals=2; plural=(n != 1);
|
||||
"default": func(n int) int {
|
||||
if n != 1 {
|
63
locale/plural_test.go
Normal file
63
locale/plural_test.go
Normal file
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2018 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package locale // import "miniflux.app/locale"
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPluralRules(t *testing.T) {
|
||||
scenarios := map[string]map[int]int{
|
||||
"default": map[int]int{
|
||||
1: 0,
|
||||
2: 1,
|
||||
5: 1,
|
||||
},
|
||||
"ar_AR": map[int]int{
|
||||
0: 0,
|
||||
1: 1,
|
||||
2: 2,
|
||||
5: 3,
|
||||
11: 4,
|
||||
200: 5,
|
||||
},
|
||||
"cs_CZ": map[int]int{
|
||||
1: 0,
|
||||
2: 1,
|
||||
5: 2,
|
||||
},
|
||||
"pl_PL": map[int]int{
|
||||
1: 0,
|
||||
2: 1,
|
||||
5: 2,
|
||||
},
|
||||
"pt_BR": map[int]int{
|
||||
1: 0,
|
||||
2: 1,
|
||||
5: 1,
|
||||
},
|
||||
"ru_RU": map[int]int{
|
||||
1: 0,
|
||||
2: 1,
|
||||
5: 2,
|
||||
},
|
||||
"sr_RS": map[int]int{
|
||||
1: 0,
|
||||
2: 1,
|
||||
5: 2,
|
||||
},
|
||||
"zh_CN": map[int]int{
|
||||
1: 0,
|
||||
5: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for rule, values := range scenarios {
|
||||
for input, expected := range values {
|
||||
result := pluralForms[rule](input)
|
||||
if result != expected {
|
||||
t.Errorf(`Unexpected result for %q rule, got %d instead of %d for %d as input`, rule, result, expected, input)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
67
locale/printer.go
Normal file
67
locale/printer.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2018 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package locale // import "miniflux.app/locale"
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Printer converts translation keys to language-specific strings.
|
||||
type Printer struct {
|
||||
language string
|
||||
}
|
||||
|
||||
// Printf is like fmt.Printf, but using language-specific formatting.
|
||||
func (p *Printer) Printf(key string, args ...interface{}) string {
|
||||
var translation string
|
||||
|
||||
str, found := defaultCatalog[p.language][key]
|
||||
if !found {
|
||||
translation = key
|
||||
} else {
|
||||
var valid bool
|
||||
translation, valid = str.(string)
|
||||
if !valid {
|
||||
translation = key
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf(translation, args...)
|
||||
}
|
||||
|
||||
// Plural returns the translation of the given key by using the language plural form.
|
||||
func (p *Printer) Plural(key string, n int, args ...interface{}) string {
|
||||
choices, found := defaultCatalog[p.language][key]
|
||||
|
||||
if found {
|
||||
var plurals []string
|
||||
|
||||
switch v := choices.(type) {
|
||||
case []interface{}:
|
||||
for _, v := range v {
|
||||
plurals = append(plurals, fmt.Sprint(v))
|
||||
}
|
||||
case []string:
|
||||
plurals = v
|
||||
default:
|
||||
return key
|
||||
}
|
||||
|
||||
pluralForm, found := pluralForms[p.language]
|
||||
if !found {
|
||||
pluralForm = pluralForms["default"]
|
||||
}
|
||||
|
||||
index := pluralForm(n)
|
||||
if len(plurals) > index {
|
||||
return fmt.Sprintf(plurals[index], args...)
|
||||
}
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
// NewPrinter creates a new Printer.
|
||||
func NewPrinter(language string) *Printer {
|
||||
return &Printer{language}
|
||||
}
|
174
locale/printer_test.go
Normal file
174
locale/printer_test.go
Normal file
|
@ -0,0 +1,174 @@
|
|||
// Copyright 2018 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
package locale // import "miniflux.app/locale"
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestTranslateWithMissingLanguage(t *testing.T) {
|
||||
defaultCatalog = catalog{}
|
||||
translation := NewPrinter("invalid").Printf("missing.key")
|
||||
|
||||
if translation != "missing.key" {
|
||||
t.Errorf(`Wrong translation, got %q`, translation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslateWithMissingKey(t *testing.T) {
|
||||
defaultCatalog = catalog{
|
||||
"en_US": translationDict{
|
||||
"k": "v",
|
||||
},
|
||||
}
|
||||
|
||||
translation := NewPrinter("en_US").Printf("missing.key")
|
||||
if translation != "missing.key" {
|
||||
t.Errorf(`Wrong translation, got %q`, translation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslateWithExistingKey(t *testing.T) {
|
||||
defaultCatalog = catalog{
|
||||
"en_US": translationDict{
|
||||
"auth.username": "Login",
|
||||
},
|
||||
}
|
||||
|
||||
translation := NewPrinter("en_US").Printf("auth.username")
|
||||
if translation != "Login" {
|
||||
t.Errorf(`Wrong translation, got %q`, translation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslateWithExistingKeyAndPlaceholder(t *testing.T) {
|
||||
defaultCatalog = catalog{
|
||||
"en_US": translationDict{
|
||||
"key": "Test: %s",
|
||||
},
|
||||
"fr_FR": translationDict{
|
||||
"key": "Test : %s",
|
||||
},
|
||||
}
|
||||
|
||||
translation := NewPrinter("fr_FR").Printf("key", "ok")
|
||||
if translation != "Test : ok" {
|
||||
t.Errorf(`Wrong translation, got %q`, translation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslateWithMissingKeyAndPlaceholder(t *testing.T) {
|
||||
defaultCatalog = catalog{
|
||||
"en_US": translationDict{
|
||||
"auth.username": "Login",
|
||||
},
|
||||
"fr_FR": translationDict{
|
||||
"auth.username": "Identifiant",
|
||||
},
|
||||
}
|
||||
|
||||
translation := NewPrinter("fr_FR").Printf("Status: %s", "ok")
|
||||
if translation != "Status: ok" {
|
||||
t.Errorf(`Wrong translation, got %q`, translation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslateWithInvalidValue(t *testing.T) {
|
||||
defaultCatalog = catalog{
|
||||
"en_US": translationDict{
|
||||
"auth.username": "Login",
|
||||
},
|
||||
"fr_FR": translationDict{
|
||||
"auth.username": true,
|
||||
},
|
||||
}
|
||||
|
||||
translation := NewPrinter("fr_FR").Printf("auth.username")
|
||||
if translation != "auth.username" {
|
||||
t.Errorf(`Wrong translation, got %q`, translation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslatePluralWithDefaultRule(t *testing.T) {
|
||||
defaultCatalog = catalog{
|
||||
"en_US": translationDict{
|
||||
"number_of_users": []string{"%d user (%s)", "%d users (%s)"},
|
||||
},
|
||||
"fr_FR": translationDict{
|
||||
"number_of_users": []string{"%d utilisateur (%s)", "%d utilisateurs (%s)"},
|
||||
},
|
||||
}
|
||||
|
||||
printer := NewPrinter("fr_FR")
|
||||
translation := printer.Plural("number_of_users", 1, 1, "some text")
|
||||
expected := "1 utilisateur (some text)"
|
||||
if translation != expected {
|
||||
t.Errorf(`Wrong translation, got %q instead of %q`, translation, expected)
|
||||
}
|
||||
|
||||
translation = printer.Plural("number_of_users", 2, 2, "some text")
|
||||
expected = "2 utilisateurs (some text)"
|
||||
if translation != expected {
|
||||
t.Errorf(`Wrong translation, got %q instead of %q`, translation, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslatePluralWithRussianRule(t *testing.T) {
|
||||
defaultCatalog = catalog{
|
||||
"en_US": translationDict{
|
||||
"time_elapsed.years": []string{"%d year", "%d years"},
|
||||
},
|
||||
"ru_RU": translationDict{
|
||||
"time_elapsed.years": []string{"%d год назад", "%d года назад", "%d лет назад"},
|
||||
},
|
||||
}
|
||||
|
||||
printer := NewPrinter("ru_RU")
|
||||
|
||||
translation := printer.Plural("time_elapsed.years", 1, 1)
|
||||
expected := "1 год назад"
|
||||
if translation != expected {
|
||||
t.Errorf(`Wrong translation, got %q instead of %q`, translation, expected)
|
||||
}
|
||||
|
||||
translation = printer.Plural("time_elapsed.years", 2, 2)
|
||||
expected = "2 года назад"
|
||||
if translation != expected {
|
||||
t.Errorf(`Wrong translation, got %q instead of %q`, translation, expected)
|
||||
}
|
||||
|
||||
translation = printer.Plural("time_elapsed.years", 5, 5)
|
||||
expected = "5 лет назад"
|
||||
if translation != expected {
|
||||
t.Errorf(`Wrong translation, got %q instead of %q`, translation, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslatePluralWithMissingTranslation(t *testing.T) {
|
||||
defaultCatalog = catalog{
|
||||
"en_US": translationDict{
|
||||
"number_of_users": []string{"%d user (%s)", "%d users (%s)"},
|
||||
},
|
||||
"fr_FR": translationDict{},
|
||||
}
|
||||
translation := NewPrinter("fr_FR").Plural("number_of_users", 2)
|
||||
expected := "number_of_users"
|
||||
if translation != expected {
|
||||
t.Errorf(`Wrong translation, got %q instead of %q`, translation, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslatePluralWithInvalidValues(t *testing.T) {
|
||||
defaultCatalog = catalog{
|
||||
"en_US": translationDict{
|
||||
"number_of_users": []string{"%d user (%s)", "%d users (%s)"},
|
||||
},
|
||||
"fr_FR": translationDict{
|
||||
"number_of_users": "must be a slice",
|
||||
},
|
||||
}
|
||||
translation := NewPrinter("fr_FR").Plural("number_of_users", 2)
|
||||
expected := "number_of_users"
|
||||
if translation != expected {
|
||||
t.Errorf(`Wrong translation, got %q instead of %q`, translation, expected)
|
||||
}
|
||||
}
|
|
@ -9,16 +9,16 @@ import "testing"
|
|||
func TestAllLanguagesHaveCatalog(t *testing.T) {
|
||||
for language := range AvailableLanguages() {
|
||||
if _, found := translations[language]; !found {
|
||||
t.Fatalf(`This language do not have a catalog: %s`, language)
|
||||
t.Fatalf(`This language do not have a catalog: %q`, language)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllKeysHaveValue(t *testing.T) {
|
||||
for language := range AvailableLanguages() {
|
||||
messages, err := parseCatalogMessages(translations[language])
|
||||
messages, err := parseTranslationDict(translations[language])
|
||||
if err != nil {
|
||||
t.Fatalf(`Parsing error language %s`, language)
|
||||
t.Fatalf(`Parsing error for language %q`, language)
|
||||
}
|
||||
|
||||
if len(messages) == 0 {
|
||||
|
@ -42,7 +42,7 @@ func TestAllKeysHaveValue(t *testing.T) {
|
|||
|
||||
func TestMissingTranslations(t *testing.T) {
|
||||
refLang := "en_US"
|
||||
references, err := parseCatalogMessages(translations[refLang])
|
||||
references, err := parseTranslationDict(translations[refLang])
|
||||
if err != nil {
|
||||
t.Fatal(`Unable to parse reference language`)
|
||||
}
|
||||
|
@ -52,9 +52,9 @@ func TestMissingTranslations(t *testing.T) {
|
|||
continue
|
||||
}
|
||||
|
||||
messages, err := parseCatalogMessages(translations[language])
|
||||
messages, err := parseTranslationDict(translations[language])
|
||||
if err != nil {
|
||||
t.Fatalf(`Parsing error language %s`, language)
|
||||
t.Fatalf(`Parsing error for language %q`, language)
|
||||
}
|
||||
|
||||
for key := range references {
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright 2017 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package locale // import "miniflux.app/locale"
|
||||
|
||||
// Translator manage supported locales.
|
||||
type Translator struct {
|
||||
locales catalog
|
||||
}
|
||||
|
||||
// AddLanguage loads a new language into the system.
|
||||
func (t *Translator) AddLanguage(language, data string) (err error) {
|
||||
t.locales[language], err = parseCatalogMessages(data)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetLanguage returns the given language handler.
|
||||
func (t *Translator) GetLanguage(language string) *Language {
|
||||
translations, found := t.locales[language]
|
||||
if !found {
|
||||
return &Language{language: language}
|
||||
}
|
||||
|
||||
return &Language{language: language, translations: translations}
|
||||
}
|
||||
|
||||
// NewTranslator creates a new Translator.
|
||||
func NewTranslator() *Translator {
|
||||
return &Translator{locales: make(catalog)}
|
||||
}
|
|
@ -33,7 +33,6 @@ var (
|
|||
// Handler contains all the logic to create and refresh feeds.
|
||||
type Handler struct {
|
||||
store *storage.Storage
|
||||
translator *locale.Translator
|
||||
}
|
||||
|
||||
// CreateFeed fetch, parse and store a new feed.
|
||||
|
@ -124,7 +123,7 @@ func (h *Handler) RefreshFeed(userID, feedID int64) error {
|
|||
userLanguage = "en_US"
|
||||
}
|
||||
|
||||
currentLanguage := h.translator.GetLanguage(userLanguage)
|
||||
printer := locale.NewPrinter(userLanguage)
|
||||
|
||||
originalFeed, err := h.store.FeedByID(userID, feedID)
|
||||
if err != nil {
|
||||
|
@ -149,7 +148,7 @@ func (h *Handler) RefreshFeed(userID, feedID int64) error {
|
|||
}
|
||||
|
||||
originalFeed.ParsingErrorCount++
|
||||
originalFeed.ParsingErrorMsg = customErr.Localize(currentLanguage)
|
||||
originalFeed.ParsingErrorMsg = customErr.Localize(printer)
|
||||
h.store.UpdateFeed(originalFeed)
|
||||
return customErr
|
||||
}
|
||||
|
@ -159,7 +158,7 @@ func (h *Handler) RefreshFeed(userID, feedID int64) error {
|
|||
if response.IsNotFound() {
|
||||
err := errors.NewLocalizedError(errResourceNotFound)
|
||||
originalFeed.ParsingErrorCount++
|
||||
originalFeed.ParsingErrorMsg = err.Localize(currentLanguage)
|
||||
originalFeed.ParsingErrorMsg = err.Localize(printer)
|
||||
h.store.UpdateFeed(originalFeed)
|
||||
return err
|
||||
}
|
||||
|
@ -167,7 +166,7 @@ func (h *Handler) RefreshFeed(userID, feedID int64) error {
|
|||
if response.HasServerFailure() {
|
||||
err := errors.NewLocalizedError(errServerFailure, response.StatusCode)
|
||||
originalFeed.ParsingErrorCount++
|
||||
originalFeed.ParsingErrorMsg = err.Localize(currentLanguage)
|
||||
originalFeed.ParsingErrorMsg = err.Localize(printer)
|
||||
h.store.UpdateFeed(originalFeed)
|
||||
return err
|
||||
}
|
||||
|
@ -179,7 +178,7 @@ func (h *Handler) RefreshFeed(userID, feedID int64) error {
|
|||
if response.ContentLength == 0 {
|
||||
err := errors.NewLocalizedError(errEmptyFeed)
|
||||
originalFeed.ParsingErrorCount++
|
||||
originalFeed.ParsingErrorMsg = err.Localize(currentLanguage)
|
||||
originalFeed.ParsingErrorMsg = err.Localize(printer)
|
||||
h.store.UpdateFeed(originalFeed)
|
||||
return err
|
||||
}
|
||||
|
@ -192,7 +191,7 @@ func (h *Handler) RefreshFeed(userID, feedID int64) error {
|
|||
subscription, parseErr := parseFeed(body)
|
||||
if parseErr != nil {
|
||||
originalFeed.ParsingErrorCount++
|
||||
originalFeed.ParsingErrorMsg = parseErr.Localize(currentLanguage)
|
||||
originalFeed.ParsingErrorMsg = parseErr.Localize(printer)
|
||||
h.store.UpdateFeed(originalFeed)
|
||||
return err
|
||||
}
|
||||
|
@ -236,6 +235,6 @@ func (h *Handler) RefreshFeed(userID, feedID int64) error {
|
|||
}
|
||||
|
||||
// NewFeedHandler returns a feed handler.
|
||||
func NewFeedHandler(store *storage.Storage, translator *locale.Translator) *Handler {
|
||||
return &Handler{store, translator}
|
||||
func NewFeedHandler(store *storage.Storage) *Handler {
|
||||
return &Handler{store}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,8 @@ import (
|
|||
|
||||
// Engine handles the templating system.
|
||||
type Engine struct {
|
||||
templates map[string]*template.Template
|
||||
translator *locale.Translator
|
||||
funcMap *funcMap
|
||||
templates map[string]*template.Template
|
||||
funcMap *funcMap
|
||||
}
|
||||
|
||||
func (e *Engine) parseAll() {
|
||||
|
@ -43,29 +42,29 @@ func (e *Engine) Render(name, language string, data interface{}) []byte {
|
|||
logger.Fatal("[Template] The template %s does not exists", name)
|
||||
}
|
||||
|
||||
lang := e.translator.GetLanguage(language)
|
||||
printer := locale.NewPrinter(language)
|
||||
|
||||
// Functions that need to be declared at runtime.
|
||||
tpl.Funcs(template.FuncMap{
|
||||
"elapsed": func(timezone string, t time.Time) string {
|
||||
return elapsedTime(lang, timezone, t)
|
||||
return elapsedTime(printer, timezone, t)
|
||||
},
|
||||
"t": func(key interface{}, args ...interface{}) string {
|
||||
switch key.(type) {
|
||||
switch k := key.(type) {
|
||||
case string:
|
||||
return lang.Get(key.(string), args...)
|
||||
return printer.Printf(k, args...)
|
||||
case errors.LocalizedError:
|
||||
return key.(errors.LocalizedError).Localize(lang)
|
||||
return k.Localize(printer)
|
||||
case *errors.LocalizedError:
|
||||
return key.(*errors.LocalizedError).Localize(lang)
|
||||
return k.Localize(printer)
|
||||
case error:
|
||||
return key.(error).Error()
|
||||
return k.Error()
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
},
|
||||
"plural": func(key string, n int, args ...interface{}) string {
|
||||
return lang.Plural(key, n, args...)
|
||||
return printer.Plural(key, n, args...)
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -79,11 +78,10 @@ func (e *Engine) Render(name, language string, data interface{}) []byte {
|
|||
}
|
||||
|
||||
// NewEngine returns a new template engine.
|
||||
func NewEngine(cfg *config.Config, router *mux.Router, translator *locale.Translator) *Engine {
|
||||
func NewEngine(cfg *config.Config, router *mux.Router) *Engine {
|
||||
tpl := &Engine{
|
||||
templates: make(map[string]*template.Template),
|
||||
translator: translator,
|
||||
funcMap: newFuncMap(cfg, router),
|
||||
templates: make(map[string]*template.Template),
|
||||
funcMap: newFuncMap(cfg, router),
|
||||
}
|
||||
|
||||
tpl.parseAll()
|
||||
|
|
|
@ -135,15 +135,15 @@ func isEmail(str string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func elapsedTime(language *locale.Language, tz string, t time.Time) string {
|
||||
func elapsedTime(printer *locale.Printer, tz string, t time.Time) string {
|
||||
if t.IsZero() {
|
||||
return language.Get("time_elapsed.not_yet")
|
||||
return printer.Printf("time_elapsed.not_yet")
|
||||
}
|
||||
|
||||
now := timezone.Now(tz)
|
||||
t = timezone.Convert(tz, t)
|
||||
if now.Before(t) {
|
||||
return language.Get("time_elapsed.not_yet")
|
||||
return printer.Printf("time_elapsed.not_yet")
|
||||
}
|
||||
|
||||
diff := now.Sub(t)
|
||||
|
@ -153,25 +153,25 @@ func elapsedTime(language *locale.Language, tz string, t time.Time) string {
|
|||
d := int(s / 86400)
|
||||
switch {
|
||||
case s < 60:
|
||||
return language.Get("time_elapsed.now")
|
||||
return printer.Printf("time_elapsed.now")
|
||||
case s < 3600:
|
||||
minutes := int(diff.Minutes())
|
||||
return language.Plural("time_elapsed.minutes", minutes, minutes)
|
||||
return printer.Plural("time_elapsed.minutes", minutes, minutes)
|
||||
case s < 86400:
|
||||
hours := int(diff.Hours())
|
||||
return language.Plural("time_elapsed.hours", hours, hours)
|
||||
return printer.Plural("time_elapsed.hours", hours, hours)
|
||||
case d == 1:
|
||||
return language.Get("time_elapsed.yesterday")
|
||||
return printer.Printf("time_elapsed.yesterday")
|
||||
case d < 7:
|
||||
return language.Plural("time_elapsed.days", d, d)
|
||||
return printer.Plural("time_elapsed.days", d, d)
|
||||
case d < 31:
|
||||
weeks := int(math.Ceil(float64(d) / 7))
|
||||
return language.Plural("time_elapsed.weeks", weeks, weeks)
|
||||
return printer.Plural("time_elapsed.weeks", weeks, weeks)
|
||||
case d < 365:
|
||||
months := int(math.Ceil(float64(d) / 30))
|
||||
return language.Plural("time_elapsed.months", months, months)
|
||||
return printer.Plural("time_elapsed.months", months, months)
|
||||
default:
|
||||
years := int(math.Ceil(float64(d) / 365))
|
||||
return language.Plural("time_elapsed.years", years, years)
|
||||
return printer.Plural("time_elapsed.years", years, years)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,28 +97,26 @@ func TestIsEmail(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestElapsedTime(t *testing.T) {
|
||||
translator := locale.Load()
|
||||
language := translator.GetLanguage("fr_FR")
|
||||
|
||||
printer := locale.NewPrinter("en_US")
|
||||
var dt = []struct {
|
||||
in time.Time
|
||||
out string
|
||||
}{
|
||||
{time.Time{}, language.Get("time_elapsed.not_yet")},
|
||||
{time.Now().Add(time.Hour), language.Get("time_elapsed.not_yet")},
|
||||
{time.Now(), language.Get("time_elapsed.now")},
|
||||
{time.Now().Add(-time.Minute), language.Plural("time_elapsed.minutes", 1, 1)},
|
||||
{time.Now().Add(-time.Minute * 40), language.Plural("time_elapsed.minutes", 40, 40)},
|
||||
{time.Now().Add(-time.Hour), language.Plural("time_elapsed.hours", 1, 1)},
|
||||
{time.Now().Add(-time.Hour * 3), language.Plural("time_elapsed.hours", 3, 3)},
|
||||
{time.Now().Add(-time.Hour * 32), language.Get("time_elapsed.yesterday")},
|
||||
{time.Now().Add(-time.Hour * 24 * 3), language.Plural("time_elapsed.days", 3, 3)},
|
||||
{time.Now().Add(-time.Hour * 24 * 14), language.Plural("time_elapsed.weeks", 2, 2)},
|
||||
{time.Now().Add(-time.Hour * 24 * 60), language.Plural("time_elapsed.months", 2, 2)},
|
||||
{time.Now().Add(-time.Hour * 24 * 365 * 3), language.Plural("time_elapsed.years", 3, 3)},
|
||||
{time.Time{}, printer.Printf("time_elapsed.not_yet")},
|
||||
{time.Now().Add(time.Hour), printer.Printf("time_elapsed.not_yet")},
|
||||
{time.Now(), printer.Printf("time_elapsed.now")},
|
||||
{time.Now().Add(-time.Minute), printer.Plural("time_elapsed.minutes", 1, 1)},
|
||||
{time.Now().Add(-time.Minute * 40), printer.Plural("time_elapsed.minutes", 40, 40)},
|
||||
{time.Now().Add(-time.Hour), printer.Plural("time_elapsed.hours", 1, 1)},
|
||||
{time.Now().Add(-time.Hour * 3), printer.Plural("time_elapsed.hours", 3, 3)},
|
||||
{time.Now().Add(-time.Hour * 32), printer.Printf("time_elapsed.yesterday")},
|
||||
{time.Now().Add(-time.Hour * 24 * 3), printer.Plural("time_elapsed.days", 3, 3)},
|
||||
{time.Now().Add(-time.Hour * 24 * 14), printer.Plural("time_elapsed.weeks", 2, 2)},
|
||||
{time.Now().Add(-time.Hour * 24 * 60), printer.Plural("time_elapsed.months", 2, 2)},
|
||||
{time.Now().Add(-time.Hour * 24 * 365 * 3), printer.Plural("time_elapsed.years", 3, 3)},
|
||||
}
|
||||
for i, tt := range dt {
|
||||
if out := elapsedTime(language, "Local", tt.in); out != tt.out {
|
||||
if out := elapsedTime(printer, "Local", tt.in); out != tt.out {
|
||||
t.Errorf(`%d. content mismatch for "%v": expected=%q got=%q`, i, tt.in, tt.out, out)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ package ui // import "miniflux.app/ui"
|
|||
|
||||
import (
|
||||
"miniflux.app/config"
|
||||
"miniflux.app/locale"
|
||||
"miniflux.app/reader/feed"
|
||||
"miniflux.app/scheduler"
|
||||
"miniflux.app/storage"
|
||||
|
@ -23,18 +22,16 @@ type Controller struct {
|
|||
feedHandler *feed.Handler
|
||||
tpl *template.Engine
|
||||
router *mux.Router
|
||||
translator *locale.Translator
|
||||
}
|
||||
|
||||
// NewController returns a new Controller.
|
||||
func NewController(cfg *config.Config, store *storage.Storage, pool *scheduler.WorkerPool, feedHandler *feed.Handler, tpl *template.Engine, translator *locale.Translator, router *mux.Router) *Controller {
|
||||
func NewController(cfg *config.Config, store *storage.Storage, pool *scheduler.WorkerPool, feedHandler *feed.Handler, tpl *template.Engine, router *mux.Router) *Controller {
|
||||
return &Controller{
|
||||
cfg: cfg,
|
||||
store: store,
|
||||
pool: pool,
|
||||
feedHandler: feedHandler,
|
||||
tpl: tpl,
|
||||
translator: translator,
|
||||
router: router,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,12 +12,14 @@ import (
|
|||
"miniflux.app/http/response/html"
|
||||
"miniflux.app/http/route"
|
||||
"miniflux.app/integration/pocket"
|
||||
"miniflux.app/locale"
|
||||
"miniflux.app/logger"
|
||||
"miniflux.app/ui/session"
|
||||
)
|
||||
|
||||
// PocketAuthorize redirects the end-user to Pocket website to authorize the application.
|
||||
func (c *Controller) PocketAuthorize(w http.ResponseWriter, r *http.Request) {
|
||||
printer := locale.NewPrinter(request.UserLanguage(r))
|
||||
user, err := c.store.UserByID(request.UserID(r))
|
||||
if err != nil {
|
||||
html.ServerError(w, err)
|
||||
|
@ -36,7 +38,7 @@ func (c *Controller) PocketAuthorize(w http.ResponseWriter, r *http.Request) {
|
|||
requestToken, err := connector.RequestToken(redirectURL)
|
||||
if err != nil {
|
||||
logger.Error("[Pocket:Authorize] %v", err)
|
||||
sess.NewFlashErrorMessage(c.translator.GetLanguage(request.UserLanguage(r)).Get("error.pocket_request_token"))
|
||||
sess.NewFlashErrorMessage(printer.Printf("error.pocket_request_token"))
|
||||
response.Redirect(w, r, route.Path(c.router, "integrations"))
|
||||
return
|
||||
}
|
||||
|
@ -47,6 +49,7 @@ func (c *Controller) PocketAuthorize(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// PocketCallback saves the personal access token after the authorization step.
|
||||
func (c *Controller) PocketCallback(w http.ResponseWriter, r *http.Request) {
|
||||
printer := locale.NewPrinter(request.UserLanguage(r))
|
||||
sess := session.New(c.store, request.SessionID(r))
|
||||
|
||||
user, err := c.store.UserByID(request.UserID(r))
|
||||
|
@ -65,7 +68,7 @@ func (c *Controller) PocketCallback(w http.ResponseWriter, r *http.Request) {
|
|||
accessToken, err := connector.AccessToken(request.PocketRequestToken(r))
|
||||
if err != nil {
|
||||
logger.Error("[Pocket:Callback] %v", err)
|
||||
sess.NewFlashErrorMessage(c.translator.GetLanguage(request.UserLanguage(r)).Get("error.pocket_access_token"))
|
||||
sess.NewFlashErrorMessage(printer.Printf("error.pocket_access_token"))
|
||||
response.Redirect(w, r, route.Path(c.router, "integrations"))
|
||||
return
|
||||
}
|
||||
|
@ -79,6 +82,6 @@ func (c *Controller) PocketCallback(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
sess.NewFlashMessage(c.translator.GetLanguage(request.UserLanguage(r)).Get("alert.pocket_linked"))
|
||||
sess.NewFlashMessage(printer.Printf("alert.pocket_linked"))
|
||||
response.Redirect(w, r, route.Path(c.router, "integrations"))
|
||||
}
|
||||
|
|
|
@ -13,12 +13,14 @@ import (
|
|||
"miniflux.app/http/request"
|
||||
"miniflux.app/http/response/html"
|
||||
"miniflux.app/http/route"
|
||||
"miniflux.app/locale"
|
||||
"miniflux.app/ui/form"
|
||||
"miniflux.app/ui/session"
|
||||
)
|
||||
|
||||
// UpdateIntegration updates integration settings.
|
||||
func (c *Controller) UpdateIntegration(w http.ResponseWriter, r *http.Request) {
|
||||
printer := locale.NewPrinter(request.UserLanguage(r))
|
||||
sess := session.New(c.store, request.SessionID(r))
|
||||
user, err := c.store.UserByID(request.UserID(r))
|
||||
if err != nil {
|
||||
|
@ -36,7 +38,7 @@ func (c *Controller) UpdateIntegration(w http.ResponseWriter, r *http.Request) {
|
|||
integrationForm.Merge(integration)
|
||||
|
||||
if integration.FeverUsername != "" && c.store.HasDuplicateFeverUsername(user.ID, integration.FeverUsername) {
|
||||
sess.NewFlashErrorMessage(c.translator.GetLanguage(request.UserLanguage(r)).Get("error.duplicate_fever_username"))
|
||||
sess.NewFlashErrorMessage(printer.Printf("error.duplicate_fever_username"))
|
||||
response.Redirect(w, r, route.Path(c.router, "integrations"))
|
||||
return
|
||||
}
|
||||
|
@ -53,6 +55,6 @@ func (c *Controller) UpdateIntegration(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
sess.NewFlashMessage(c.translator.GetLanguage(request.UserLanguage(r)).Get("alert.prefs_saved"))
|
||||
sess.NewFlashMessage(printer.Printf("alert.prefs_saved"))
|
||||
response.Redirect(w, r, route.Path(c.router, "integrations"))
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"miniflux.app/http/response"
|
||||
"miniflux.app/http/response/html"
|
||||
"miniflux.app/http/route"
|
||||
"miniflux.app/locale"
|
||||
"miniflux.app/logger"
|
||||
"miniflux.app/model"
|
||||
"miniflux.app/ui/session"
|
||||
|
@ -19,6 +20,7 @@ import (
|
|||
|
||||
// OAuth2Callback receives the authorization code and create a new session.
|
||||
func (c *Controller) OAuth2Callback(w http.ResponseWriter, r *http.Request) {
|
||||
printer := locale.NewPrinter(request.UserLanguage(r))
|
||||
sess := session.New(c.store, request.SessionID(r))
|
||||
|
||||
provider := request.Param(r, "provider", "")
|
||||
|
@ -65,7 +67,7 @@ func (c *Controller) OAuth2Callback(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
if user != nil {
|
||||
logger.Error("[OAuth2] User #%d cannot be associated because %s is already associated", request.UserID(r), user.Username)
|
||||
sess.NewFlashErrorMessage(c.translator.GetLanguage(request.UserLanguage(r)).Get("error.duplicate_linked_account"))
|
||||
sess.NewFlashErrorMessage(printer.Printf("error.duplicate_linked_account"))
|
||||
response.Redirect(w, r, route.Path(c.router, "settings"))
|
||||
return
|
||||
}
|
||||
|
@ -75,7 +77,7 @@ func (c *Controller) OAuth2Callback(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
sess.NewFlashMessage(c.translator.GetLanguage(request.UserLanguage(r)).Get("alert.account_linked"))
|
||||
sess.NewFlashMessage(printer.Printf("alert.account_linked"))
|
||||
response.Redirect(w, r, route.Path(c.router, "settings"))
|
||||
return
|
||||
}
|
||||
|
|
|
@ -11,12 +11,14 @@ import (
|
|||
"miniflux.app/http/response"
|
||||
"miniflux.app/http/response/html"
|
||||
"miniflux.app/http/route"
|
||||
"miniflux.app/locale"
|
||||
"miniflux.app/logger"
|
||||
"miniflux.app/ui/session"
|
||||
)
|
||||
|
||||
// OAuth2Unlink unlink an account from the external provider.
|
||||
func (c *Controller) OAuth2Unlink(w http.ResponseWriter, r *http.Request) {
|
||||
printer := locale.NewPrinter(request.UserLanguage(r))
|
||||
provider := request.Param(r, "provider", "")
|
||||
if provider == "" {
|
||||
logger.Info("[OAuth2] Invalid or missing provider")
|
||||
|
@ -40,7 +42,7 @@ func (c *Controller) OAuth2Unlink(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if !hasPassword {
|
||||
sess.NewFlashErrorMessage(c.translator.GetLanguage(request.UserLanguage(r)).Get("error.unlink_account_without_password"))
|
||||
sess.NewFlashErrorMessage(printer.Printf("error.unlink_account_without_password"))
|
||||
response.Redirect(w, r, route.Path(c.router, "settings"))
|
||||
return
|
||||
}
|
||||
|
@ -50,6 +52,6 @@ func (c *Controller) OAuth2Unlink(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
sess.NewFlashMessage(c.translator.GetLanguage(request.UserLanguage(r)).Get("alert.account_unlinked"))
|
||||
sess.NewFlashMessage(printer.Printf("alert.account_unlinked"))
|
||||
response.Redirect(w, r, route.Path(c.router, "settings"))
|
||||
}
|
||||
|
|
|
@ -69,6 +69,6 @@ func (c *Controller) UpdateSettings(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
sess.SetLanguage(user.Language)
|
||||
sess.SetTheme(user.Theme)
|
||||
sess.NewFlashMessage(c.translator.GetLanguage(request.UserLanguage(r)).Get("alert.prefs_saved"))
|
||||
sess.NewFlashMessage(locale.NewPrinter(request.UserLanguage(r)).Printf("alert.prefs_saved"))
|
||||
response.Redirect(w, r, route.Path(c.router, "settings"))
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue