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:
parent
fa6ddc70b3
commit
5505fa41c3
9
Makefile
9
Makefile
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
46
po/ca.po
46
po/ca.po
|
@ -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 d’usuari 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 d’usuari"
|
||||
|
||||
#: 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"
|
||||
|
|
46
po/es.po
46
po/es.po
|
@ -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"
|
||||
|
|
|
@ -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>
|
|
@ -0,0 +1,2 @@
|
|||
{{ define "content" }}
|
||||
{{- end }}
|
|
@ -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 }}
|
||||
|
|
|
@ -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 }}
|
||||
|
|
|
@ -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>
|
Loading…
Reference in New Issue