- 新增 internal/i18n 包:以原始中文字符串为 key 的翻译目录(zh 恒等, en 兜底,ja 缺译回退 en),t/tf 模板函数与 Accept-Language 解析 (按 q 值排序,兜底英语) - User 模型新增 language 字段(auto|en|zh|ja,默认 auto), AutoMigrate 自动加列;UserStore 新增 UpdateLanguage - 语言解析:登录用户按偏好(auto 时取浏览器 Accept-Language), 登录/封禁页按浏览器语言;所有 c.HTML 渲染注入 Lang 模板数据 - 全部页面模板(用户页 + 管理后台 12 页)接入翻译:导航、文件夹、 邮件列表/阅读/写信、设置、登录、封禁页、表单、表格、JS 确认框等; html lang 属性随语言切换;日期函数(shortDate/time12/time12m) 与 folderLabel 改为语言感知(上午/下午 vs AM/PM vs 午前/午後) - 设置页新增「界面语言」选择(自动/English/中文/日本語), /settings 表单支持单独提交语言偏好;管理后台用户表单可预设语言 - 封禁原因等入库中文文案在展示时经 t 翻译(未知 key 原样回退) - 测试:i18n 单测(解析/回退/译文)、render 测试补 Lang 数据、 时间格式化测试适配新签名;全量 go test 通过
120 lines
7.2 KiB
HTML
120 lines
7.2 KiB
HTML
{{define "folder"}}
|
||
<!DOCTYPE html>
|
||
<html lang="{{htmlLang .Lang}}">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||
<title>{{folderLabel .Lang .folder}} - MailGo</title>
|
||
{{template "styles" .}}
|
||
</head>
|
||
<body class="page-list">
|
||
{{template "navbar" .}}
|
||
<div class="app-body">
|
||
{{template "sidebar" .}}
|
||
<main class="mail-main">
|
||
<div class="list-toolbar">
|
||
<label class="check-all" title="{{t .Lang "全选/取消全选"}}">
|
||
<input type="checkbox" id="select-all">
|
||
{{t .Lang "全选"}}
|
||
</label>
|
||
<button type="button" class="tb-btn" id="btn-refresh" title="{{t .Lang "刷新"}}">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></svg>
|
||
{{t .Lang "刷新"}}
|
||
</button>
|
||
<button type="button" class="tb-btn danger" id="btn-delete" disabled>
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
|
||
{{if .isTrash}}{{t $.Lang "彻底删除"}}{{else}}{{t $.Lang "删除"}}{{end}}
|
||
</button>
|
||
{{if .isTrash}}
|
||
<form method="POST" action="/folder/Trash/empty" style="display:inline;"
|
||
onsubmit="return confirm('{{t $.Lang "确定要清空「已删除」文件夹吗?此操作不可恢复。"}}');">
|
||
<button type="submit" class="tb-btn danger">{{t $.Lang "清空文件夹"}}</button>
|
||
</form>
|
||
{{end}}
|
||
<div class="toolbar-spacer"></div>
|
||
<span class="page-info">{{tf .Lang "共 %d 封" .total}}</span>
|
||
</div>
|
||
|
||
{{if not .messages}}
|
||
<div class="mail-list">
|
||
<div class="empty-tip"><span class="empty-icon">{{if .isTrash}}🗑{{else}}📭{{end}}</span>{{folderLabel $.Lang $.folder}} · {{t $.Lang "暂无邮件"}}</div>
|
||
</div>
|
||
{{else}}
|
||
<ul class="mail-list">
|
||
{{range .messages}}
|
||
<li class="mail-row {{if not .IsRead}}unread{{end}}" data-id="{{.ID}}">
|
||
<label class="cell-check" onclick="event.stopPropagation()">
|
||
<input type="checkbox" class="row-check" data-id="{{.ID}}">
|
||
</label>
|
||
<span class="cell-avatar">
|
||
{{if or (eq $.folder "Sent") (eq $.folder "Drafts")}}
|
||
<span class="avatar" style="{{avatarStyle .ToAddr}}">{{initial (mailName .ToAddr)}}</span>
|
||
{{else}}
|
||
<span class="avatar" style="{{avatarStyle .FromAddr}}">{{initial (mailName (decodeHeader .FromAddr))}}</span>
|
||
{{end}}
|
||
</span>
|
||
<span class="cell-from" {{if or (eq $.folder "Sent") (eq $.folder "Drafts")}}title="{{tf $.Lang "收件人:%s" .ToAddr}}"{{else}}title="{{decodeHeader .FromAddr}}"{{end}}>
|
||
{{if or (eq $.folder "Sent") (eq $.folder "Drafts")}}
|
||
{{if eq $.folder "Drafts"}}{{t $.Lang "致:"}}{{end}}{{mailName .ToAddr}}
|
||
{{else}}
|
||
{{mailName (decodeHeader .FromAddr)}}
|
||
{{end}}
|
||
</span>
|
||
<span class="cell-subject-wrap">
|
||
{{if not .IsRead}}<span class="unread-dot"></span>{{end}}
|
||
<a class="cell-subject" href="/folder/{{urlPath $.folder}}/{{.ID}}">
|
||
{{if .Subject}}{{.Subject}}{{else}}{{t $.Lang "(无主题)"}}{{end}}
|
||
</a>
|
||
</span>
|
||
<span class="cell-snippet">
|
||
{{if .TextBody}}{{truncate .TextBody 80}}{{else if .HtmlBody}}{{t $.Lang "[HTML 邮件]"}}{{end}}
|
||
</span>
|
||
<span class="cell-date">{{shortDate $.Lang .Date}}</span>
|
||
{{if $.isTrash}}
|
||
<form method="POST" action="/mail/restore/{{.ID}}" class="row-del"
|
||
onsubmit="event.stopPropagation(); return confirm('{{t $.Lang "确定要恢复这封邮件吗?"}}');">
|
||
<button type="submit" class="icon-btn" title="{{t $.Lang "恢复到收件箱"}}">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/></svg>
|
||
</button>
|
||
</form>
|
||
{{else}}
|
||
<form method="POST" action="/mail/delete/{{.ID}}" class="row-del"
|
||
onsubmit="event.stopPropagation(); return confirm('{{t $.Lang "确定要删除这封邮件吗?"}}');">
|
||
<button type="submit" class="icon-btn" title="{{t $.Lang "删除"}}">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
|
||
</button>
|
||
</form>
|
||
{{end}}
|
||
</li>
|
||
{{end}}
|
||
</ul>
|
||
{{end}}
|
||
|
||
<div class="list-footer">
|
||
<span class="page-num">{{tf .Lang "第 %d / %d 页" .page (or .totalPages 1)}}</span>
|
||
<div class="pager">
|
||
{{if gt .page 1}}
|
||
<a class="page-btn" href="/folder/{{urlPath .folder}}?page={{sub .page 1}}">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"/></svg>
|
||
{{t $.Lang "上一页"}}
|
||
</a>
|
||
{{else}}
|
||
<span class="page-btn disabled">{{t $.Lang "上一页"}}</span>
|
||
{{end}}
|
||
{{if lt .page .totalPages}}
|
||
<a class="page-btn" href="/folder/{{urlPath .folder}}?page={{add .page 1}}">
|
||
{{t $.Lang "下一页"}}
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>
|
||
</a>
|
||
{{else}}
|
||
<span class="page-btn disabled">{{t $.Lang "下一页"}}</span>
|
||
{{end}}
|
||
</div>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
{{template "listjs" .}}
|
||
</body>
|
||
</html>
|
||
{{end}}
|