diff --git a/web/handlers/http.go b/web/handlers/http.go index 3f35bd6..2325204 100644 --- a/web/handlers/http.go +++ b/web/handlers/http.go @@ -124,35 +124,37 @@ func New( }), render.InjectTemplateFunc("listLanguages", func(r *http.Request) interface{} { - urlTo := web.NewURLTo(m) - route := urlTo(router.CompleteSetLanguage).String() - csrfElement := csrf.TemplateField(r) + urlTo := web.NewURLTo(m) + route := urlTo(router.CompleteSetLanguage).String() + // seem to get an error when changing languages on pages that already embed a csrf token + csrfElement := csrf.TemplateField(r) - createFormElement := func (tag, translation string) string { - return fmt.Sprintf(` + createFormElement := func(tag, translation string) string { + return fmt.Sprintf(`
%s +
- `, route, csrfElement, tag, translation) - } - return func () template.HTML { - languages := locHelper.ListLanguages() - languageOptions := make([]string, len(languages)) - for tag, translation := range languages { - languageOptions = append(languageOptions, createFormElement(tag, translation)) - } - return (template.HTML)(strings.Join(languageOptions, "\n")) - } - }), + `, route, csrfElement, tag, r.RequestURI, translation) + } + return func() template.HTML { + languages := locHelper.ListLanguages() + languageOptions := make([]string, len(languages)) + for tag, translation := range languages { + languageOptions = append(languageOptions, createFormElement(tag, translation)) + } + return (template.HTML)(strings.Join(languageOptions, "\n")) + } + }), render.InjectTemplateFunc("urlToNotice", func(r *http.Request) interface{} { return func(name string) *url.URL { @@ -269,10 +271,29 @@ func New( ) mainMux.Handle("/admin/", members.AuthenticateFromContext(r)(adminHandler)) - // handle setting language - m.Get(router.CompleteSetLanguage).HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - fmt.Println("set das language jaaa") - }) + // handle setting language + m.Get(router.CompleteSetLanguage).HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + lang := req.FormValue("lang") + previousRoute := req.FormValue("page") + fmt.Println(lang, previousRoute) + + session, err := cookieStore.Get(req, i18n.LanguageCookieName) + if err != nil { + fmt.Printf("cookie error? %w\n", err) + return + } + + prevCookie := session.Values["lang"] + fmt.Println("previous cookie", prevCookie) + + session.Values["lang"] = lang + err = session.Save(req, w) + if err != nil { + fmt.Printf("we failed to save the language session cookie %w\n", err) + } + + http.Redirect(w, req, previousRoute, http.StatusTemporaryRedirect) + }) // landing page m.Get(router.CompleteIndex).Handler(r.HTML("landing/index.tmpl", func(w http.ResponseWriter, req *http.Request) (interface{}, error) { diff --git a/web/i18n/defaults/active.en.toml b/web/i18n/defaults/active.en.toml index 8a60bf1..d8b1f75 100644 --- a/web/i18n/defaults/active.en.toml +++ b/web/i18n/defaults/active.en.toml @@ -1,5 +1,7 @@ # default localiztion file for english -MetaLanguage = "English" + +# the name of this language, in its language. Used for language picking +LanguageName = "English" # generic terms GenericConfirm = "Yes" diff --git a/web/i18n/helper.go b/web/i18n/helper.go index 44bc1e4..3853d4c 100644 --- a/web/i18n/helper.go +++ b/web/i18n/helper.go @@ -14,22 +14,40 @@ import ( "strings" "github.com/BurntSushi/toml" + "github.com/gorilla/sessions" "github.com/nicksnyder/go-i18n/v2/i18n" + "github.com/ssb-ngi-pointer/go-ssb-room/web" "go.mindeco.de/http/render" "golang.org/x/text/language" "github.com/ssb-ngi-pointer/go-ssb-room/internal/repo" ) +const LanguageCookieName = "gossbroom-language" + type Helper struct { - bundle *i18n.Bundle - languages map[string]string + bundle *i18n.Bundle + languages map[string]string + cookieStore *sessions.CookieStore } func New(r repo.Interface) (*Helper, error) { bundle := i18n.NewBundle(language.English) bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal) + cookieCodec, err := web.LoadOrCreateCookieSecrets(r) + if err != nil { + return nil, err + } + + cookieStore := &sessions.CookieStore{ + Codecs: cookieCodec, + Options: &sessions.Options{ + Path: "/", + MaxAge: 2 * 60 * 60, // two hours in seconds // TODO: configure + }, + } + // parse toml files and add them to the bundle walkFn := func(path string, info os.FileInfo, rs io.Reader, err error) error { if err != nil { @@ -57,7 +75,7 @@ func New(r repo.Interface) (*Helper, error) { } // walk the embedded defaults - err := fs.WalkDir(Defaults, ".", func(path string, d fs.DirEntry, err error) error { + err = fs.WalkDir(Defaults, ".", func(path string, d fs.DirEntry, err error) error { if err != nil { return err } @@ -116,7 +134,7 @@ func New(r repo.Interface) (*Helper, error) { // create a mapping of language tags to the translated language names langmap := listLanguages(bundle) - return &Helper{bundle: bundle, languages: langmap}, nil + return &Helper{bundle: bundle, languages: langmap, cookieStore: cookieStore}, nil } func listLanguages(bundle *i18n.Bundle) map[string]string { @@ -127,7 +145,7 @@ func listLanguages(bundle *i18n.Bundle) map[string]string { l.loc = i18n.NewLocalizer(bundle, langTag.String()) msg, err := l.loc.Localize(&i18n.LocalizeConfig{ - MessageID: "MetaLanguage", + MessageID: "LanguageName", }) if err != nil { msg = langTag.String() @@ -160,10 +178,22 @@ func (h Helper) newLocalizer(lang string, accept ...string) *Localizer { // FromRequest returns a new Localizer for the passed helper, // using form value 'lang' and Accept-Language http header from the passed request. -// TODO: user settings/cookie values? +// If a language cookie is detected, then it takes precedence over the form value & Accept-Lanuage header. func (h Helper) FromRequest(r *http.Request) *Localizer { lang := r.FormValue("lang") accept := r.Header.Get("Accept-Language") + + session, err := h.cookieStore.Get(r, LanguageCookieName) + if err != nil { + fmt.Printf("cookie error? %w\n", err) + return h.newLocalizer(lang, accept) + } + + prevCookie := session.Values["lang"] + if prevCookie != nil { + return h.newLocalizer(prevCookie.(string), lang, accept) + } + return h.newLocalizer(lang, accept) } diff --git a/web/i18n/i18ntesting/i18n_helper_test.go b/web/i18n/i18ntesting/i18n_helper_test.go new file mode 100644 index 0000000..745054b --- /dev/null +++ b/web/i18n/i18ntesting/i18n_helper_test.go @@ -0,0 +1,20 @@ +package i18ntesting + +import ( + "github.com/stretchr/testify/assert" + "path/filepath" + "testing" + + "github.com/ssb-ngi-pointer/go-ssb-room/internal/repo" + "github.com/ssb-ngi-pointer/go-ssb-room/web/i18n" +) + +func TestListAllLanguages(t *testing.T) { + r := repo.New(filepath.Join("testrun", t.Name())) + a := assert.New(t) + helper, err := i18n.New(r) + a.NoError(err) + t.Log(helper) + langmap := helper.ListLanguages() + a.Equal(langmap["en"], "English") +} diff --git a/web/i18n/i18ntesting/i18n_test.go b/web/i18n/i18ntesting/testing.go similarity index 85% rename from web/i18n/i18ntesting/i18n_test.go rename to web/i18n/i18ntesting/testing.go index cedecb2..30a7819 100644 --- a/web/i18n/i18ntesting/i18n_test.go +++ b/web/i18n/i18ntesting/testing.go @@ -3,7 +3,6 @@ package i18ntesting import ( "bytes" "fmt" - "github.com/stretchr/testify/assert" "io/ioutil" "os" "path/filepath" @@ -67,17 +66,7 @@ func justTheKeys(t *testing.T) []byte { return buf.Bytes() } -func TestListAllLanguages(t *testing.T) { - r := repo.New(filepath.Join("testrun", t.Name())) - a := assert.New(t) - helper, err := i18n.New(r) - a.NoError(err) - t.Log(helper) - langmap := helper.ListLanguages() - a.Equal(langmap["en"], "English") -} - -func TestWriteReplacement(t *testing.T) { +func WriteReplacement(t *testing.T) { r := repo.New(filepath.Join("testrun", t.Name())) testOverride := filepath.Join(r.GetPath("i18n"), "active.en.toml") diff --git a/web/router/complete.go b/web/router/complete.go index 754953a..206bb2e 100644 --- a/web/router/complete.go +++ b/web/router/complete.go @@ -14,7 +14,7 @@ const ( CompleteNoticeShow = "complete:notice:show" CompleteNoticeList = "complete:notice:list" - CompleteSetLanguage = "complete:set-language" + CompleteSetLanguage = "complete:set-language" CompleteAliasResolve = "complete:alias:resolve"