aboutsummaryrefslogtreecommitdiffstats
path: root/internal/web/templates/dashboard/layout.html
diff options
context:
space:
mode:
authorgrm <grm@eyesin.space>2026-09-14 01:32:25 +0300
committergrm <grm@eyesin.space>2026-09-14 01:32:25 +0300
commite14a4250efb828262347f5a4bbc4067cd71d885f (patch)
tree3fcf6512f3b030b06c818ff4a224677a026d3b1b /internal/web/templates/dashboard/layout.html
parent282d2ab3e0fb3a74f32160a231225773001acd14 (diff)
downloadblogspace-e14a4250efb828262347f5a4bbc4067cd71d885f.tar.gz
blogspace-e14a4250efb828262347f5a4bbc4067cd71d885f.tar.bz2
blogspace-e14a4250efb828262347f5a4bbc4067cd71d885f.zip
Add languages: English and Greek, chosen per blog on the Settings tab
Every blog and its dashboard were hard-wired to English. A blogger can now pick the language of their blog; it switches the whole dashboard and the blog's fixed text — post dates, archive months, the RSS link, the pager, the 404 page — while what the blogger wrote is left alone. The new internal/i18n package keys translations by the English string, so an untranslated key renders as English rather than blank, and TestGreekCatalogComplete scans the templates and handlers to fail when the Greek catalog misses a key or keeps a stale one. Templates are compiled once per language with t/tf/date/postdate/month closed over the language, so they need no data plumbing. The language lives in settings.language (blog migration 00002), not in the theme, so "Reset design" does not touch it. Public pages use the blog's language; management pages use the logged-in user's own blog's, so a superadmin editing someone else's blog keeps theirs; the login page follows Accept-Language. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
Diffstat (limited to 'internal/web/templates/dashboard/layout.html')
-rw-r--r--internal/web/templates/dashboard/layout.html102
1 files changed, 51 insertions, 51 deletions
diff --git a/internal/web/templates/dashboard/layout.html b/internal/web/templates/dashboard/layout.html
index 229ca0e..48b6778 100644
--- a/internal/web/templates/dashboard/layout.html
+++ b/internal/web/templates/dashboard/layout.html
@@ -1,86 +1,86 @@
-{{define "title"}}Layout · {{.Blog.Title}}{{end}}
+{{define "title"}}{{t "Layout"}} · {{.Blog.Title}}{{end}}
{{define "content"}}
{{$t := .Data.theme}}
{{$u := print "/b/" .Blog.Subdomain "/layout"}}
<div class="pagehead">
- <h1>Layout</h1>
- <p class="lead muted">Decide what goes where on your blog. Every change here is saved straight away; refresh <a href="{{.BlogURL}}" target="_blank">your blog &#8599;</a> to see it. Colours and fonts are on the <a href="/b/{{.Blog.Subdomain}}/design">Design</a> tab.</p>
+ <h1>{{t "Layout"}}</h1>
+ <p class="lead muted">{{t "Decide what goes where on your blog. Every change here is saved straight away; refresh"}} <a href="{{.BlogURL}}" target="_blank">{{t "your blog"}} &#8599;</a> {{t "to see it."}} {{t "Colours and fonts are on the"}} <a href="/b/{{.Blog.Subdomain}}/design">{{t "Design"}}</a> {{t "tab."}}</p>
</div>
-<p class="jumps"><a href="#columns">Columns</a><a href="#header">Header</a><a href="#left">Left column</a><a href="#main">Main content</a><a href="#right">Right column</a><a href="#footer">Footer</a><a href="#menu">Menu</a></p>
+<p class="jumps"><a href="#columns">{{t "Columns"}}</a><a href="#header">{{t "Header"}}</a><a href="#left">{{t "Left column"}}</a><a href="#main">{{t "Main content"}}</a><a href="#right">{{t "Right column"}}</a><a href="#footer">{{t "Footer"}}</a><a href="#menu">{{t "Menu"}}</a></p>
<div class="card" id="columns">
- <h2>Columns</h2>
+ <h2>{{t "Columns"}}</h2>
<div class="lay">
- <div class="lay-row lay-header{{if not $t.HeaderOn}} off{{end}}">Header{{if not $t.HeaderOn}} (hidden){{end}}</div>
+ <div class="lay-row lay-header{{if not $t.HeaderOn}} off{{end}}">{{t "Header"}}{{if not $t.HeaderOn}} ({{t "hidden"}}){{end}}</div>
<div class="lay-cols">
- <div class="lay-col lay-side{{if not $t.LeftOn}} off{{end}}{{if not (index .Data.areas "left")}} empty{{end}}" style="width:{{$t.LeftWidth}}%">Left {{$t.LeftWidth}}%</div>
- <div class="lay-col lay-main" style="width:{{$t.MainWidth}}%">Main content {{$t.MainWidth}}%</div>
- <div class="lay-col lay-side{{if not $t.RightOn}} off{{end}}{{if not (index .Data.areas "right")}} empty{{end}}" style="width:{{$t.RightWidth}}%">Right {{$t.RightWidth}}%</div>
+ <div class="lay-col lay-side{{if not $t.LeftOn}} off{{end}}{{if not (index .Data.areas "left")}} empty{{end}}" style="width:{{$t.LeftWidth}}%">{{t "Left"}} {{$t.LeftWidth}}%</div>
+ <div class="lay-col lay-main" style="width:{{$t.MainWidth}}%">{{t "Main content"}} {{$t.MainWidth}}%</div>
+ <div class="lay-col lay-side{{if not $t.RightOn}} off{{end}}{{if not (index .Data.areas "right")}} empty{{end}}" style="width:{{$t.RightWidth}}%">{{t "Right"}} {{$t.RightWidth}}%</div>
</div>
- <div class="lay-row lay-footer{{if not $t.FooterOn}} off{{end}}">Footer{{if not $t.FooterOn}} (hidden){{end}}</div>
+ <div class="lay-row lay-footer{{if not $t.FooterOn}} off{{end}}">{{t "Footer"}}{{if not $t.FooterOn}} ({{t "hidden"}}){{end}}</div>
</div>
<form method="post" action="{{$u}}/widths">
<input type="hidden" name="_csrf" value="{{.CSRF}}">
<div class="row">
- <label>Left column width<br><input type="number" name="left_width" value="{{$t.LeftWidth}}" min="15" max="40" step="5"> %</label>
- <label>Right column width<br><input type="number" name="right_width" value="{{$t.RightWidth}}" min="15" max="40" step="5"> %</label>
+ <label>{{t "Left column width"}}<br><input type="number" name="left_width" value="{{$t.LeftWidth}}" min="15" max="40" step="5"> %</label>
+ <label>{{t "Right column width"}}<br><input type="number" name="right_width" value="{{$t.RightWidth}}" min="15" max="40" step="5"> %</label>
</div>
- <p class="muted small">Between 15% and 40% each; the main content takes the rest. A column only appears when it has at least one module; without columns the posts take the whole width. On phones the columns stack: main content first, then the left and right columns, then the footer.</p>
- <label class="check"><input type="checkbox" name="keep_columns"{{if $t.KeepColumns}} checked{{end}}> Always keep the columns' space, even when a column is empty or hidden <span class="muted">(the main content then keeps its width and stays centred)</span></label>
- <p class="actionrow"><button type="submit">Save columns</button></p>
+ <p class="muted small">{{t "Between 15% and 40% each; the main content takes the rest. A column only appears when it has at least one module; without columns the posts take the whole width. On phones the columns stack: main content first, then the left and right columns, then the footer."}}</p>
+ <label class="check"><input type="checkbox" name="keep_columns"{{if $t.KeepColumns}} checked{{end}}> {{t "Always keep the columns' space, even when a column is empty or hidden"}} <span class="muted">({{t "the main content then keeps its width and stays centred"}})</span></label>
+ <p class="actionrow"><button type="submit">{{t "Save columns"}}</button></p>
</form>
</div>
-{{template "area" (dict "area" "header" "root" . "hint" "The top of every page. Stack the title, a logo and the menu in the order you like. A banner image can be chosen on the Design tab.")}}
-{{template "area" (dict "area" "left" "root" . "hint" "A narrow column beside your posts. Good for the menu, an archive or recent posts.")}}
+{{template "area" (dict "area" "header" "root" . "hint" (t "The top of every page. Stack the title, a logo and the menu in the order you like. A banner image can be chosen on the Design tab."))}}
+{{template "area" (dict "area" "left" "root" . "hint" (t "A narrow column beside your posts. Good for the menu, an archive or recent posts."))}}
<div class="card" id="main">
- <div class="cardhead"><h2>Main content</h2></div>
- <p class="muted small">Your posts and pages always show here. You can add your own HTML before and after them — an intro box, a newsletter form, a badge…</p>
+ <div class="cardhead"><h2>{{t "Main content"}}</h2></div>
+ <p class="muted small">{{t "Your posts and pages always show here. You can add your own HTML before and after them — an intro box, a newsletter form, a badge…"}}</p>
<table class="modules">
{{range $area := .Data.areaOrder}}{{$mods := index $.Data.areas $area}}
- <tr><th>{{index $.Data.areaNames $area}}</th>
+ <tr><th>{{t (index $.Data.areaNames $area)}}</th>
{{if $mods}}{{$m := index $mods 0}}
- <td>{{index $.Data.names $m.Kind}}{{with modsummary $m}} <span class="muted">— {{.}}</span>{{end}}</td>
- <td class="nowrap actions"><a href="{{$u}}/modules/{{$m.ID}}/edit">edit</a> <a class="danger" href="{{$u}}/modules/{{$m.ID}}/delete">remove</a></td>
+ <td>{{t (index $.Data.names $m.Kind)}}{{with modsummary $m}} <span class="muted">— {{.}}</span>{{end}}</td>
+ <td class="nowrap actions"><a href="{{$u}}/modules/{{$m.ID}}/edit">{{t "edit"}}</a> <a class="danger" href="{{$u}}/modules/{{$m.ID}}/delete">{{t "remove"}}</a></td>
{{else}}
- <td class="muted">Nothing extra.</td>
- <td class="nowrap actions"><form method="post" action="{{$u}}/areas/{{$area}}/add" class="inline"><input type="hidden" name="_csrf" value="{{$.CSRF}}"><input type="hidden" name="kind" value="html"><button class="mini">+ Add custom HTML</button></form></td>
+ <td class="muted">{{t "Nothing extra."}}</td>
+ <td class="nowrap actions"><form method="post" action="{{$u}}/areas/{{$area}}/add" class="inline"><input type="hidden" name="_csrf" value="{{$.CSRF}}"><input type="hidden" name="kind" value="html"><button class="mini">+ {{t "Add custom HTML"}}</button></form></td>
{{end}}
</tr>{{end}}
</table>
</div>
-{{template "area" (dict "area" "right" "root" . "hint" "A narrow column on the other side of your posts. Same choices as the left one.")}}
-{{template "area" (dict "area" "footer" "root" . "hint" "The bottom of every page: a copyright line, the RSS link, a list of your pages.")}}
+{{template "area" (dict "area" "right" "root" . "hint" (t "A narrow column on the other side of your posts. Same choices as the left one."))}}
+{{template "area" (dict "area" "footer" "root" . "hint" (t "The bottom of every page: a copyright line, the RSS link, a list of your pages."))}}
<div class="card" id="menu">
- <h2>Menu</h2>
- <p class="muted small">The links the Menu module shows, wherever you place it. Pages and outside links can be mixed in any order. Ticking "Show in menu" on a page does the same as adding it here.</p>
+ <h2>{{t "Menu"}}</h2>
+ <p class="muted small">{{t "The links the Menu module shows, wherever you place it. Pages and outside links can be mixed in any order. Ticking “Show in menu” on a page does the same as adding it here."}}</p>
{{if .Data.menu}}<table class="modules">
{{range .Data.menu}}<tr>
<td class="nowrap order">
- <form method="post" action="{{$u}}/menu/{{.ID}}/move" class="inline"><input type="hidden" name="_csrf" value="{{$.CSRF}}"><input type="hidden" name="dir" value="up"><button class="mini" title="Move up">&#9650;</button></form>
- <form method="post" action="{{$u}}/menu/{{.ID}}/move" class="inline"><input type="hidden" name="_csrf" value="{{$.CSRF}}"><input type="hidden" name="dir" value="down"><button class="mini" title="Move down">&#9660;</button></form>
+ <form method="post" action="{{$u}}/menu/{{.ID}}/move" class="inline"><input type="hidden" name="_csrf" value="{{$.CSRF}}"><input type="hidden" name="dir" value="up"><button class="mini" title="{{t "Move up"}}">&#9650;</button></form>
+ <form method="post" action="{{$u}}/menu/{{.ID}}/move" class="inline"><input type="hidden" name="_csrf" value="{{$.CSRF}}"><input type="hidden" name="dir" value="down"><button class="mini" title="{{t "Move down"}}">&#9660;</button></form>
</td>
- <td>{{.Text}} {{if .PageID}}<span class="tag">page</span>{{else}}<span class="tag">link</span> <span class="muted small">{{.URL}}</span>{{end}}</td>
- <td class="nowrap actions"><form method="post" action="{{$u}}/menu/{{.ID}}/delete" class="inline"><input type="hidden" name="_csrf" value="{{$.CSRF}}"><button class="mini danger">Remove</button></form></td>
+ <td>{{.Text}} {{if .PageID}}<span class="tag">{{t "page"}}</span>{{else}}<span class="tag">{{t "link"}}</span> <span class="muted small">{{.URL}}</span>{{end}}</td>
+ <td class="nowrap actions"><form method="post" action="{{$u}}/menu/{{.ID}}/delete" class="inline"><input type="hidden" name="_csrf" value="{{$.CSRF}}"><button class="mini danger">{{t "Remove"}}</button></form></td>
</tr>{{end}}
- </table>{{else}}<p class="muted">The menu is empty.</p>{{end}}
+ </table>{{else}}<p class="muted">{{t "The menu is empty."}}</p>{{end}}
<div class="cols addrow">
<form method="post" action="{{$u}}/menu/add-page" class="add">
<input type="hidden" name="_csrf" value="{{.CSRF}}">
- <h3>Add a page</h3>
- {{if .Data.freePages}}<label>Page<br><select name="page">{{range .Data.freePages}}<option value="{{.ID}}">{{.Title}}</option>{{end}}</select></label>
- <button type="submit" class="secondary">Add page</button>
- {{else}}<p class="muted small">Every page is already in the menu.</p>{{end}}
+ <h3>{{t "Add a page"}}</h3>
+ {{if .Data.freePages}}<label>{{t "Page"}}<br><select name="page">{{range .Data.freePages}}<option value="{{.ID}}">{{.Title}}</option>{{end}}</select></label>
+ <button type="submit" class="secondary">{{t "Add page"}}</button>
+ {{else}}<p class="muted small">{{t "Every page is already in the menu."}}</p>{{end}}
</form>
<form method="post" action="{{$u}}/menu/add-link" class="add">
<input type="hidden" name="_csrf" value="{{.CSRF}}">
- <h3>Add a link</h3>
- <label>Text<br><input name="label" maxlength="60" placeholder="My photos"></label>
- <label>Address<br><input name="url" maxlength="500" placeholder="https://example.org"></label>
- <button type="submit" class="secondary">Add link</button>
+ <h3>{{t "Add a link"}}</h3>
+ <label>{{t "Text"}}<br><input name="label" maxlength="60" placeholder="{{t "My photos"}}"></label>
+ <label>{{t "Address"}}<br><input name="url" maxlength="500" placeholder="https://example.org"></label>
+ <button type="submit" class="secondary">{{t "Add link"}}</button>
</form>
</div>
</div>
@@ -90,24 +90,24 @@
{{define "area"}}{{$r := .root}}{{$area := .area}}{{$u := print "/b/" $r.Blog.Subdomain "/layout"}}{{$on := $r.Data.theme.AreaOn $area}}{{$mods := index $r.Data.areas $area}}
<div class="card{{if not $on}} area-off{{end}}" id="{{$area}}">
<div class="cardhead">
- <h2>{{index $r.Data.areaNames $area}}{{if not $on}} <span class="tag off">hidden</span>{{end}}</h2>
- <form method="post" action="{{$u}}/areas/{{$area}}/toggle" class="inline switch"><input type="hidden" name="_csrf" value="{{$r.CSRF}}"><button class="mini">{{if $on}}Hide this area{{else}}Show this area{{end}}</button></form>
+ <h2>{{t (index $r.Data.areaNames $area)}}{{if not $on}} <span class="tag off">{{t "hidden"}}</span>{{end}}</h2>
+ <form method="post" action="{{$u}}/areas/{{$area}}/toggle" class="inline switch"><input type="hidden" name="_csrf" value="{{$r.CSRF}}"><button class="mini">{{if $on}}{{t "Hide this area"}}{{else}}{{t "Show this area"}}{{end}}</button></form>
</div>
<p class="muted small">{{.hint}}</p>
{{if $mods}}<table class="modules">
{{range $mods}}<tr>
<td class="nowrap order">
- <form method="post" action="{{$u}}/modules/{{.ID}}/move" class="inline"><input type="hidden" name="_csrf" value="{{$r.CSRF}}"><input type="hidden" name="dir" value="up"><button class="mini" title="Move up">&#9650;</button></form>
- <form method="post" action="{{$u}}/modules/{{.ID}}/move" class="inline"><input type="hidden" name="_csrf" value="{{$r.CSRF}}"><input type="hidden" name="dir" value="down"><button class="mini" title="Move down">&#9660;</button></form>
+ <form method="post" action="{{$u}}/modules/{{.ID}}/move" class="inline"><input type="hidden" name="_csrf" value="{{$r.CSRF}}"><input type="hidden" name="dir" value="up"><button class="mini" title="{{t "Move up"}}">&#9650;</button></form>
+ <form method="post" action="{{$u}}/modules/{{.ID}}/move" class="inline"><input type="hidden" name="_csrf" value="{{$r.CSRF}}"><input type="hidden" name="dir" value="down"><button class="mini" title="{{t "Move down"}}">&#9660;</button></form>
</td>
- <td>{{index $r.Data.names .Kind}}{{with modsummary .}} <span class="muted">— {{.}}</span>{{end}}
- {{if and (eq .Kind "logo") (not $r.Data.theme.Logo)}}<span class="tag off">no logo chosen</span> <span class="muted small">pick one on the <a href="/b/{{$r.Blog.Subdomain}}/design#logo">Design</a> tab</span>{{end}}</td>
- <td class="nowrap actions">{{if modsettings .Kind}}<a href="{{$u}}/modules/{{.ID}}/edit">edit</a> {{end}}<a class="danger" href="{{$u}}/modules/{{.ID}}/delete">remove</a></td>
+ <td>{{t (index $r.Data.names .Kind)}}{{with modsummary .}} <span class="muted">— {{.}}</span>{{end}}
+ {{if and (eq .Kind "logo") (not $r.Data.theme.Logo)}}<span class="tag off">{{t "no logo chosen"}}</span> <span class="muted small">{{t "pick one on the"}} <a href="/b/{{$r.Blog.Subdomain}}/design#logo">{{t "Design"}}</a> {{t "tab."}}</span>{{end}}</td>
+ <td class="nowrap actions">{{if modsettings .Kind}}<a href="{{$u}}/modules/{{.ID}}/edit">{{t "edit"}}</a> {{end}}<a class="danger" href="{{$u}}/modules/{{.ID}}/delete">{{t "remove"}}</a></td>
</tr>{{end}}
- </table>{{else}}<p class="muted">Nothing here yet{{if ne $area "header"}} — this area is not shown until you add a module{{end}}.</p>{{end}}
+ </table>{{else}}<p class="muted">{{if ne $area "header"}}{{t "Nothing here yet — this area is not shown until you add a module."}}{{else}}{{t "Nothing here yet."}}{{end}}</p>{{end}}
<form method="post" action="{{$u}}/areas/{{$area}}/add" class="addmod">
<input type="hidden" name="_csrf" value="{{$r.CSRF}}">
- <label>Add a module<br><select name="kind">{{range index $r.Data.kinds $area}}<option value="{{.}}">{{index $r.Data.names .}}</option>{{end}}</select></label>
- <button type="submit" class="secondary">Add</button>
+ <label>{{t "Add a module"}}<br><select name="kind">{{range index $r.Data.kinds $area}}<option value="{{.}}">{{t (index $r.Data.names .)}}</option>{{end}}</select></label>
+ <button type="submit" class="secondary">{{t "Add"}}</button>
</form>
</div>{{end}}