Use “layouts” for the common HTML between pages

Had to call xgettext on Go source files because now the title comes from
there, as i assume i will have titles like "Invoice #INVxxxx" that have
to come from the database that the template does not know.
This commit is contained in:
jordi fita mas 2023-01-22 21:41:50 +01:00
parent fa6ddc70b3
commit 5505fa41c3
13 changed files with 115 additions and 110 deletions

View File

@ -1,8 +1,10 @@
INPUT_FILES := $(shell find web -name *.html)
HTML_FILES := $(shell find web -name *.html)
GO_FILES := $(shell find . -name *.go)
DEFAULT_DOMAIN = numerus
POT_FILE = po/$(DEFAULT_DOMAIN).pot
LINGUAS = ca es
MO_FILES = $(patsubst %,locales/%/LC_MESSAGES/$(DEFAULT_DOMAIN).mo,$(LINGUAS))
XGETTEXTFLAGS = --no-wrap --from-code=UTF-8 --package-name=numerus --msgid-bugs-address=jordi@tandem.blog
locales: $(MO_FILES)
@ -13,8 +15,9 @@ locales/%/LC_MESSAGES/numerus.mo: po/%.po
po/%.po: $(POT_FILE)
msgmerge --no-wrap --update --backup=off $@ $<
$(POT_FILE): $(INPUT_FILES)
xgettext --no-wrap --language=Scheme --from-code=UTF-8 --output=$@ --keyword=pgettext:1,2c --package-name=numerus --msgid-bugs-address=jordi@tandem.blog $^
$(POT_FILE): $(HTML_FILES) $(GO_FILES)
xgettext $(XGETTEXTFLAGS) --language=Scheme --output=$@ --keyword=pgettext:1,2c $(HTML_FILES)
xgettext $(XGETTEXTFLAGS) --language=C --output=$@ --join-existing $(GO_FILES)
test-deploy:
sqitch deploy --db-name $(PGDATABASE)

View File

@ -46,6 +46,10 @@ func getLocale(r *http.Request) *gotext.Locale {
return r.Context().Value(contextLocaleKey).(*gotext.Locale)
}
func pgettext(context string, str string, locale *gotext.Locale) string {
return locale.GetC(str, context)
}
func mustGetAvailableLanguages(db *Db) []language.Tag {
rows, err := db.Query(context.Background(), "select lang_tag from language where selectable")
if err != nil {

View File

@ -52,7 +52,7 @@ func LoginHandler() http.Handler {
} else {
w.WriteHeader(http.StatusOK)
}
mustRenderTemplate(w, r, "login.html", page)
mustRenderWebTemplate(w, r, "login.html", page)
})
}

View File

@ -11,6 +11,7 @@ type LanguageOption struct {
}
type ProfilePage struct {
Title string
Name string
Email string
Password string
@ -27,7 +28,9 @@ func ProfileHandler() http.Handler {
return
}
conn := getConn(r)
locale := getLocale(r)
page := ProfilePage{
Title: pgettext("title", "User Settings", locale),
Email: user.Email,
Languages: mustGetLanguageOptions(r.Context(), conn),
}
@ -44,7 +47,7 @@ func ProfileHandler() http.Handler {
panic(nil)
}
}
mustRenderTemplate(w, r, "profile.html", page)
mustRenderAppTemplate(w, r, "profile.html", page)
})
}

View File

@ -13,7 +13,7 @@ func NewRouter(db *Db) http.Handler {
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
user := getUser(r)
if user.LoggedIn {
mustRenderTemplate(w, r, "index.html", nil)
mustRenderAppTemplate(w, r, "dashboard.html", nil)
} else {
http.Redirect(w, r, "/login", http.StatusSeeOther)
}

View File

@ -6,17 +6,29 @@ import (
"net/http"
)
func mustRenderTemplate(wr io.Writer, r *http.Request, filename string, data interface{}) {
func templateFile (name string) string {
return "web/template/" + name
}
func mustRenderTemplate(wr io.Writer, r *http.Request, layout string, filename string, data interface{}) {
locale := getLocale(r)
t := template.New(filename)
t.Funcs(template.FuncMap{
"gettext": locale.Get,
"pgettext": locale.GetC,
})
if _, err := t.ParseFiles("web/template/" + filename); err != nil {
if _, err := t.ParseFiles(templateFile(filename), templateFile(layout)); err != nil {
panic(err)
}
if err := t.Execute(wr, data); err != nil {
if err := t.ExecuteTemplate(wr, layout, data); err != nil {
panic(err)
}
}
func mustRenderAppTemplate(w io.Writer, r *http.Request, filename string, data interface{}) {
mustRenderTemplate(w, r, "app.html", filename, data);
}
func mustRenderWebTemplate(w io.Writer, r *http.Request, filename string, data interface{}) {
mustRenderTemplate(w, r, "web.html", filename, data);
}

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: numerus\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2023-01-22 02:20+0100\n"
"POT-Creation-Date: 2023-01-22 21:38+0100\n"
"PO-Revision-Date: 2023-01-18 17:08+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Catalan <ca@dodds.net>\n"
@ -17,75 +17,75 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: web/template/login.html:6 web/template/login.html:19
#: web/template/web.html:6 web/template/login.html:9
msgctxt "title"
msgid "Login"
msgstr "Entrada"
#: web/template/login.html:14
#: web/template/login.html:5
msgid "Invalid user or password"
msgstr "Nom dusuari o contrasenya incorrectes"
#: web/template/login.html:21 web/template/profile.html:29
#: web/template/login.html:11 web/template/profile.html:10
msgctxt "input"
msgid "Email"
msgstr "Correu electrònic"
#: web/template/login.html:24 web/template/profile.html:35
#: web/template/login.html:14 web/template/profile.html:16
msgctxt "input"
msgid "Password"
msgstr "Contrasenya"
#: web/template/login.html:27
#: web/template/login.html:17
msgctxt "action"
msgid "Login"
msgstr "Entra"
#: web/template/profile.html:6 web/template/profile.html:21
#: web/template/profile.html:2 pkg/profile.go:33
msgctxt "title"
msgid "User Settings"
msgstr "Configuració usuari"
#: web/template/profile.html:15 web/template/index.html:15
msgid "Account"
msgstr "Compte"
#: web/template/profile.html:16 web/template/index.html:16
msgctxt "action"
msgid "Logout"
msgstr "Surt"
#: web/template/profile.html:24
#: web/template/profile.html:5
msgctxt "title"
msgid "User Access Data"
msgstr "Dades accés usuari"
#: web/template/profile.html:26
#: web/template/profile.html:7
msgctxt "input"
msgid "User name"
msgstr "Nom dusuari"
#: web/template/profile.html:33
#: web/template/profile.html:14
msgctxt "title"
msgid "Password Change"
msgstr "Canvi contrasenya"
#: web/template/profile.html:38
#: web/template/profile.html:19
msgctxt "input"
msgid "Password Confirmation"
msgstr "Confirmació contrasenya"
#: web/template/profile.html:42
#: web/template/profile.html:23
msgctxt "input"
msgid "Language"
msgstr "Idioma"
#: web/template/profile.html:44
#: web/template/profile.html:25
msgctxt "language option"
msgid "Automatic"
msgstr "Automàtic"
#: web/template/profile.html:49
#: web/template/profile.html:30
msgctxt "action"
msgid "Save changes"
msgstr "Desa canvis"
#: web/template/app.html:15
msgid "Account"
msgstr "Compte"
#: web/template/app.html:16
msgctxt "action"
msgid "Logout"
msgstr "Surt"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: numerus\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2023-01-22 02:20+0100\n"
"POT-Creation-Date: 2023-01-22 21:38+0100\n"
"PO-Revision-Date: 2023-01-18 17:45+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Spanish <es@tp.org.es>\n"
@ -17,75 +17,75 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: web/template/login.html:6 web/template/login.html:19
#: web/template/web.html:6 web/template/login.html:9
msgctxt "title"
msgid "Login"
msgstr "Entrada"
#: web/template/login.html:14
#: web/template/login.html:5
msgid "Invalid user or password"
msgstr "Nombre de usuario o contraseña inválido"
#: web/template/login.html:21 web/template/profile.html:29
#: web/template/login.html:11 web/template/profile.html:10
msgctxt "input"
msgid "Email"
msgstr "Correo electrónico"
#: web/template/login.html:24 web/template/profile.html:35
#: web/template/login.html:14 web/template/profile.html:16
msgctxt "input"
msgid "Password"
msgstr "Contraseña"
#: web/template/login.html:27
#: web/template/login.html:17
msgctxt "action"
msgid "Login"
msgstr "Entrar"
#: web/template/profile.html:6 web/template/profile.html:21
#: web/template/profile.html:2 pkg/profile.go:33
msgctxt "title"
msgid "User Settings"
msgstr "Configuración usuario"
#: web/template/profile.html:15 web/template/index.html:15
msgid "Account"
msgstr "Cuenta"
#: web/template/profile.html:16 web/template/index.html:16
msgctxt "action"
msgid "Logout"
msgstr "Salir"
#: web/template/profile.html:24
#: web/template/profile.html:5
msgctxt "title"
msgid "User Access Data"
msgstr "Datos acceso usuario"
#: web/template/profile.html:26
#: web/template/profile.html:7
msgctxt "input"
msgid "User name"
msgstr "Nombre de usuario"
#: web/template/profile.html:33
#: web/template/profile.html:14
msgctxt "title"
msgid "Password Change"
msgstr "Cambio de contraseña"
#: web/template/profile.html:38
#: web/template/profile.html:19
msgctxt "input"
msgid "Password Confirmation"
msgstr "Confirmación contrasenya"
#: web/template/profile.html:42
#: web/template/profile.html:23
msgctxt "input"
msgid "Language"
msgstr "Idioma"
#: web/template/profile.html:44
#: web/template/profile.html:25
msgctxt "language option"
msgid "Automatic"
msgstr "Automático"
#: web/template/profile.html:49
#: web/template/profile.html:30
msgctxt "action"
msgid "Save changes"
msgstr "Guardar cambios"
#: web/template/app.html:15
msgid "Account"
msgstr "Cuenta"
#: web/template/app.html:16
msgctxt "action"
msgid "Logout"
msgstr "Salir"

View File

@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Numerus</title>
<title>{{ .Title }} — Numerus</title>
<link rel="stylesheet" type="text/css" media="screen" href="/static/numerus.css">
</head>
<body>
@ -18,6 +18,7 @@
</nav>
</header>
<main>
{{- template "content" . }}
</main>
</body>
</html>

View File

@ -0,0 +1,2 @@
{{ define "content" }}
{{- end }}

View File

@ -1,31 +1,20 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{( pgettext "Login" "title" )}} — Numerus</title>
<link rel="stylesheet" type="text/css" media="screen" href="/static/numerus.css">
</head>
<body class="web">
<h1><img src="/static/numerus.svg" alt="Numerus" width="620" height="77"></h1>
{{ define "content" }}
<h1><img src="/static/numerus.svg" alt="Numerus" width="620" height="77"></h1>
{{ if .LoginError -}}
<div class="error" role="alert">
<p>{{( gettext "Invalid user or password" )}}</p>
</div>
{{- end }}
<section id="login">
<h2>{{( pgettext "Login" "title" )}}</h2>
<form method="POST" action="/login">
<label for="user_email">{{( pgettext "Email" "input" )}}</label>
<input id="user_email" type="email" required autofocus name="email" autocapitalize="none" value="{{ .Email }}">
{{ if .LoginError }}
<div class="error" role="alert">
<p>{{( gettext "Invalid user or password" )}}</p>
</div>
{{ end }}
<label for="user_password">{{( pgettext "Password" "input" )}}</label>
<input id="user_password" type="password" required name="password" autocomplete="current-password" value="{{ .Password }}">
<section id="login">
<h2>{{( pgettext "Login" "title" )}}</h2>
<form method="POST" action="/login">
<label for="user_email">{{( pgettext "Email" "input" )}}</label>
<input id="user_email" type="email" required autofocus name="email" autocapitalize="none" value="{{ .Email }}">
<label for="user_password">{{( pgettext "Password" "input" )}}</label>
<input id="user_password" type="password" required name="password" autocomplete="current-password" value="{{ .Password }}">
<button type="submit">{{( pgettext "Login" "action" )}}</button>
</form>
</section>
</body>
</html>
<button type="submit">{{( pgettext "Login" "action" )}}</button>
</form>
</section>
{{- end }}

View File

@ -1,23 +1,4 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{(pgettext "User Settings" "title")}} — Numerus</title>
<link rel="stylesheet" type="text/css" media="screen" href="/static/numerus.css">
</head>
<body>
<header>
<h1><img src="/static/numerus.svg" alt="Numerus" width="261" height="33"></h1>
<nav role="navigation">
<button aria-haspopup="true"><i class="ri-eye-close-line ri-3x"></i></button>
<ul>
<li><a href="/profile"><i class="ri-account-circle-line"></i> {{( gettext "Account" )}}</a></li>
<li><form method="POST" action="/logout"><button type="submit"><i class="ri-logout-circle-line"></i> {{( pgettext "Logout" "action" )}}</button></form></li>
</ul>
</nav>
</header>
<main>
{{ define "content" }}
<h2>{{(pgettext "User Settings" "title")}}</h2>
<form method="POST" action="/profile">
<fieldset>
@ -42,12 +23,10 @@
<label for="language">{{( pgettext "Language" "input" )}}</label>
<select id="language" name="language">
<option value="und">{{( pgettext "Automatic" "language option" )}}</option>
{{ range $language := .Languages }}
{{- range $language := .Languages }}
<option value="{{ .Tag }}" {{ if eq .Tag $.Language }}selected="selected"{{ end }}>{{ .Name }}</option>
{{ end }}
{{- end }}
</select>
<button type="submit">{{( pgettext "Save changes" "action" )}}</button>
</form>
</main>
</body>
</html>
{{- end }}

12
web/template/web.html Normal file
View File

@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{( pgettext "Login" "title" )}} — Numerus</title>
<link rel="stylesheet" type="text/css" media="screen" href="/static/numerus.css">
</head>
<body class="web">
{{- template "content" . }}
</body>
</html>