From 034e6bdf6639c5494b17baab6f6e768a820e926b Mon Sep 17 00:00:00 2001 From: bryan newbold Date: Wed, 22 Nov 2023 14:33:25 -0800 Subject: [PATCH] bskyweb: basic i18n setup Uses Cookies, then HTTP Accept-Language, then default, to negotiate language setting. The cookie name is 'lang', and the value can be any string which would be valid in Accept-Language. This includes a trivial 2-char ISO code. Language setting is returned as 2-char ISO code (though could be more flexible in the future if needed). HTML language is configured (attr), and a secondary per-language scripts.html file is included. As a hack Makefile generates the scripts_*.html files; typescript build system will need to populate those appropriately as part of 'make build-web' to finish integration. --- bskyweb/.gitignore | 1 + bskyweb/Makefile | 2 +- bskyweb/cmd/bskyweb/i18n.go | 40 +++++++++++++++++++++++++++++++++++ bskyweb/cmd/bskyweb/server.go | 4 ++++ bskyweb/templates/base.html | 9 ++++++-- 5 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 bskyweb/cmd/bskyweb/i18n.go diff --git a/bskyweb/.gitignore b/bskyweb/.gitignore index 1d945e1dab..9f9c561844 100644 --- a/bskyweb/.gitignore +++ b/bskyweb/.gitignore @@ -9,6 +9,7 @@ static/js/*.js static/js/*.map static/js/*.js.LICENSE.txt templates/scripts.html +templates/scripts_*.html # Don't ignore this file !.gitignore diff --git a/bskyweb/Makefile b/bskyweb/Makefile index e0ba8aec06..6f979fa849 100644 --- a/bskyweb/Makefile +++ b/bskyweb/Makefile @@ -42,4 +42,4 @@ check: ## Compile everything, checking syntax (does not output binaries) .PHONY: run-dev-bskyweb run-dev-bskyweb: .env ## Runs 'bskyweb' for local dev - GOLOG_LOG_LEVEL=info go run ./cmd/bskyweb serve --debug + GOLOG_LOG_LEVEL=info go run ./cmd/bskyweb serve diff --git a/bskyweb/cmd/bskyweb/i18n.go b/bskyweb/cmd/bskyweb/i18n.go new file mode 100644 index 0000000000..dd35ec2e30 --- /dev/null +++ b/bskyweb/cmd/bskyweb/i18n.go @@ -0,0 +1,40 @@ +package main + +import ( + "github.com/labstack/echo/v4" + "golang.org/x/text/language" +) + +// this constant determines the supported languages. the first in the array is used as a fallback. +// +// NOTE: intentionally using 2-char codes here instead of the named constants +// https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes +var langMatcher = language.NewMatcher([]language.Tag{ + language.MustParse("en"), // English: default/fallback + language.MustParse("ja"), // Japanese + language.MustParse("pt"), // Portuguese + language.MustParse("de"), // German + language.MustParse("hi"), // Hindi +}) + +// cookie value should have same syntax as an Accept-Language HTTP header +var langCookieName = "lang" + +// returns an ISO 639 2-char language code to use for this request. +// +// tries a cookie first, then Accept-Language HTTP header, then a default +func determineLangCode(c echo.Context) string { + + cookieVal := "" + cookie, err := c.Cookie(langCookieName) + if err != nil { + c.Logger().Warn(err) + } else { + cookieVal = cookie.Value + } + + hdr := c.Request().Header.Get("Accept-Language") + langTag, _ := language.MatchStrings(langMatcher, cookieVal, hdr) + base, _ := langTag.Base() + return base.String() +} diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go index 46848a82da..d253305849 100644 --- a/bskyweb/cmd/bskyweb/server.go +++ b/bskyweb/cmd/bskyweb/server.go @@ -276,16 +276,19 @@ func (srv *Server) errorHandler(err error, c echo.Context) { // handler for endpoint that have no specific server-side handling func (srv *Server) WebGeneric(c echo.Context) error { data := pongo2.Context{} + data["lang"] = determineLangCode(c) return c.Render(http.StatusOK, "base.html", data) } func (srv *Server) WebHome(c echo.Context) error { data := pongo2.Context{} + data["lang"] = determineLangCode(c) return c.Render(http.StatusOK, "home.html", data) } func (srv *Server) WebPost(c echo.Context) error { data := pongo2.Context{} + data["lang"] = determineLangCode(c) handle := c.Param("handle") rkey := c.Param("rkey") // sanity check argument @@ -321,6 +324,7 @@ func (srv *Server) WebPost(c echo.Context) error { func (srv *Server) WebProfile(c echo.Context) error { data := pongo2.Context{} + data["lang"] = determineLangCode(c) handle := c.Param("handle") // sanity check argument if len(handle) > 4 && len(handle) < 128 { diff --git a/bskyweb/templates/base.html b/bskyweb/templates/base.html index b03fbbee4e..eeefb4abbf 100644 --- a/bskyweb/templates/base.html +++ b/bskyweb/templates/base.html @@ -1,5 +1,5 @@ - - + + @@ -172,6 +172,11 @@ } {% include "scripts.html" %} + {% if lang == "en" %}{% include "scripts_en.html" %}{% endif %} + {% if lang == "ja" %}{% include "scripts_ja.html" %}{% endif %} + {% if lang == "pt" %}{% include "scripts_pt.html" %}{% endif %} + {% if lang == "de" %}{% include "scripts_de.html" %}{% endif %} + {% if lang == "hi" %}{% include "scripts_hi.html" %}{% endif %}