fix(stats): 删掉「没有推测」这类对站长说的话,文案统一以访客为读者

「数据速览」下面那句「下面几句是从上面的数字直接读出来的,没有推测」,
是在向读者解释「我没有编」—— 那是作者视角的免责,不是访客需要的信息。
读者看到「没有推测」只会想:那我该怀疑什么?删掉,卡片留标题 + 结论列表
即可(`stats.insight.title` 已经说明这组是什么)。

顺着把整页 40 余条文案过了一遍,同类的还有页脚:

  「累计数字与图表是当天重算一次的快照(当天第一次访问时算,服务重启也会重算)」
  → 「累计数字与图表每天更新一次」

访客不需要知道缓存是懒重算、更不需要知道重启会触发重算;真要精确的新鲜度,
上一行「快照时间」已经给了到秒的值。这条注的职责只是说清「累计按天、今日实时」。

保留的两处,判断过不属于这个问题:
- `stats.foot.privacy` 与顶部 `stats.lead` 后半句近乎同义 —— 页脚隐私声明是惯例,
  重复一次不算噪音,删了反而有人以为底部没声明。
- 「(实时)」在 60 秒缓存下仍成立。

删掉说明行后标题与列表之间只剩 4px,给 `.tb-insights` 补 `margin: 8px 0 0`
(原来靠 `tb-card-note` 的 16px 撑开)。顺手修掉模板里一条过期注释 ——
它还写着「四条时间」「哪个是基准日」,上一轮已改成三行「快照时间」。

i18n 四语言同步(有词条数量对等测试兜底);build + i18n/stats/template/templates
测试全绿,中英页脚与数据速览卡片截图确认过间距。
This commit is contained in:
tamakyi
2026-10-25 09:18:00 +08:00
parent af39651734
commit 79199ac3cd
7 changed files with 47 additions and 13 deletions
+29
View File
@@ -0,0 +1,29 @@
# 2026-10-25
## /stats 文案清理:把「写给运营者看」的话删掉
用户指出「数据速览」下面那句「下面几句是从上面的数字直接读出来的,没有推测」怪 ——
那是对他自己说的(作者视角的免责),不是对访客说的。顺着扫了页面全部 40 余条文案。
改动:
- **删 `stats.insight.note`**(四语言 + 模板那行 `<p class="tb-card-note">`)。
卡片留「数据速览」+ 列表即可;`stats.insight.empty` 保留。
删掉说明行后标题与列表之间只剩 4px,给 `.tb-insights` 补 `margin: 8px 0 0`。
- **`stats.foot.cacheNote` 去掉实现细节**:「当天重算一次的快照(当天第一次访问时算,
服务重启也会重算)」→「累计数字与图表每天更新一次」。访客不需要知道缓存是懒重算、
也不需要知道重启会触发重算;准确的新鲜度由「快照时间」那行给出。
- 顺手修了 `templates/stats.html` 里一条过期注释(还写着「四条时间」「基准日」,
上一轮已改成三行「快照时间」)。
保留未动(判断过,不属于这个问题):
- `stats.foot.privacy` 与顶部 `stats.lead` 的后半句近乎同义(隐私声明重复一次)——
页脚隐私声明是惯例,重复不算噪音,故保留。
- `stats.today.tag` 的「(实时)」在 60s TTL 下仍成立,不改。
## 约定(已写进 topics/stats.md)
页面文案一律以**访客**为第二人称对象:不写实现细节(缓存、重启、索引),
不写作者视角的免责(「没有推测」「这里只有」「本文」这类把读者当审稿人的话)。
数据来源/隐私/新鲜度这些确实要对访客交代的事,放 `stats.lead` 与页脚注。
+10
View File
@@ -118,3 +118,13 @@ totals + 字数分布 + 回答耗时合并成一条 SQL(都是同一张表的
公开页只给聚合数字。不显示 IP、不显示单条内容、不做箱主排行(会招骚扰)。 公开页只给聚合数字。不显示 IP、不显示单条内容、不做箱主排行(会招骚扰)。
「收到过提问的箱子」这类计数可以,具体是谁不行。 「收到过提问的箱子」这类计数可以,具体是谁不行。
## 文案(读者是访客,不是站长)
- 不写实现细节:缓存策略、懒重算、"服务重启也会重算"、索引、TTL —— 访客要的只是
「多久更新一次」。准确的新鲜度交给页脚「快照时间」那行。
- 不写作者视角的免责:「下面几句…没有推测」「本文」「这里只有」这类,读起来像在跟审稿人
解释,而不是在给访客看。已删 `stats.insight.note`。
- 确实要对访客交代的只有三件:数据来源/隐私(`stats.lead` + `stats.foot.privacy`)、
新鲜度(`stats.foot.cacheNote`)、口径例外(`stats.trend.monthlyNote` 的"当月还没过完")。
- 删说明行会改变间距:`.tb-insights` 自己补 `margin-top`,别指望 `tb-card-note` 的 16px。
+1 -2
View File
@@ -970,7 +970,6 @@ var enDict = map[string]string{
"stats.kpi.messages": "Direct messages", "stats.kpi.messages": "Direct messages",
"stats.insight.title": "At a glance", "stats.insight.title": "At a glance",
"stats.insight.note": "Each line below is read straight from the numbers above — no guesswork.",
"stats.insight.empty": "Not enough question data yet.", "stats.insight.empty": "Not enough question data yet.",
"stats.insight.daily30": "%s questions in the last 30 days, about %.1f per day.", "stats.insight.daily30": "%s questions in the last 30 days, about %.1f per day.",
"stats.insight.peak": "Busiest day was %s with %s questions.", "stats.insight.peak": "Busiest day was %s with %s questions.",
@@ -1033,5 +1032,5 @@ var enDict = map[string]string{
"stats.foot.prevMonth": "Last question:", "stats.foot.prevMonth": "Last question:",
"stats.foot.prevAnswer": "Last answer:", "stats.foot.prevAnswer": "Last answer:",
"stats.foot.privacy": "All figures are aggregates; no individual question, asker, or source address is shown.", "stats.foot.privacy": "All figures are aggregates; no individual question, asker, or source address is shown.",
"stats.foot.cacheNote": "Totals and charts are recomputed once a day, on the first visit after midnight (and again after a restart); the two \"today\" figures are live.", "stats.foot.cacheNote": "Totals and charts are updated once a day; the two \"today\" figures are live.",
} }
+1 -2
View File
@@ -969,7 +969,6 @@ var jaDict = map[string]string{
"stats.kpi.messages": "DM の総数", "stats.kpi.messages": "DM の総数",
"stats.insight.title": "ひと目でわかる数字", "stats.insight.title": "ひと目でわかる数字",
"stats.insight.note": "以下の文はすべて上の数値から直接読み取ったもので、推測は含みません。",
"stats.insight.empty": "まだ分析できるほどの質問データがありません。", "stats.insight.empty": "まだ分析できるほどの質問データがありません。",
"stats.insight.daily30": "直近 30 日で %s 件の質問、1 日あたり約 %.1f 件。", "stats.insight.daily30": "直近 30 日で %s 件の質問、1 日あたり約 %.1f 件。",
"stats.insight.peak": "最も多かった日は %s で %s 件でした。", "stats.insight.peak": "最も多かった日は %s で %s 件でした。",
@@ -1032,5 +1031,5 @@ var jaDict = map[string]string{
"stats.foot.prevMonth": "最後の質問:", "stats.foot.prevMonth": "最後の質問:",
"stats.foot.prevAnswer": "最後の回答:", "stats.foot.prevAnswer": "最後の回答:",
"stats.foot.privacy": "数値はすべて集計値です。個々の質問内容・質問者の身元・送信元アドレスは表示しません。", "stats.foot.privacy": "数値はすべて集計値です。個々の質問内容・質問者の身元・送信元アドレスは表示しません。",
"stats.foot.cacheNote": "累計値とグラフはその日の最初のアクセス時に 1 回だけ再計算されます(再起動時にも再計算)。「今日」の 2 項目はリアルタイムです。", "stats.foot.cacheNote": "累計値とグラフは 1 日 1 回更新されます。「今日」の 2 項目はリアルタイムです。",
} }
+1 -2
View File
@@ -970,7 +970,6 @@ var zhCNDict = map[string]string{
"stats.kpi.messages": "累计私聊消息", "stats.kpi.messages": "累计私聊消息",
"stats.insight.title": "数据速览", "stats.insight.title": "数据速览",
"stats.insight.note": "下面几句是从上面的数字直接读出来的,没有推测。",
"stats.insight.empty": "提问数据还太少,暂时看不出什么。", "stats.insight.empty": "提问数据还太少,暂时看不出什么。",
"stats.insight.daily30": "近 30 天共收到 %s 条提问,日均 %.1f 条。", "stats.insight.daily30": "近 30 天共收到 %s 条提问,日均 %.1f 条。",
"stats.insight.peak": "最忙的一天是 %s,收到 %s 条提问。", "stats.insight.peak": "最忙的一天是 %s,收到 %s 条提问。",
@@ -1033,5 +1032,5 @@ var zhCNDict = map[string]string{
"stats.foot.prevMonth": "最后一条提问:", "stats.foot.prevMonth": "最后一条提问:",
"stats.foot.prevAnswer": "最后一条回答:", "stats.foot.prevAnswer": "最后一条回答:",
"stats.foot.privacy": "所有数字均为聚合结果,不展示单条提问内容、提问者身份或来源地址。", "stats.foot.privacy": "所有数字均为聚合结果,不展示单条提问内容、提问者身份或来源地址。",
"stats.foot.cacheNote": "累计数字与图表是当天重算一次的快照(当天第一次访问时算,服务重启也会重算);「今日」两项为实时数字。", "stats.foot.cacheNote": "累计数字与图表每天更新一次;「今日」两项实时。",
} }
+1 -2
View File
@@ -969,7 +969,6 @@ var zhTWDict = map[string]string{
"stats.kpi.messages": "累計私聊訊息", "stats.kpi.messages": "累計私聊訊息",
"stats.insight.title": "數據速覽", "stats.insight.title": "數據速覽",
"stats.insight.note": "下面幾句是直接從上面的數字讀出來的,沒有推測。",
"stats.insight.empty": "提問數據還太少,暫時看不出什麼。", "stats.insight.empty": "提問數據還太少,暫時看不出什麼。",
"stats.insight.daily30": "近 30 天共收到 %s 則提問,日均 %.1f 則。", "stats.insight.daily30": "近 30 天共收到 %s 則提問,日均 %.1f 則。",
"stats.insight.peak": "最忙的一天是 %s,收到 %s 則提問。", "stats.insight.peak": "最忙的一天是 %s,收到 %s 則提問。",
@@ -1032,5 +1031,5 @@ var zhTWDict = map[string]string{
"stats.foot.prevMonth": "最後一則提問:", "stats.foot.prevMonth": "最後一則提問:",
"stats.foot.prevAnswer": "最後一則回答:", "stats.foot.prevAnswer": "最後一則回答:",
"stats.foot.privacy": "所有數字均為彙總結果,不展示單則提問內容、提問者身分或來源位址。", "stats.foot.privacy": "所有數字均為彙總結果,不展示單則提問內容、提問者身分或來源位址。",
"stats.foot.cacheNote": "累計數字與圖表是當天重算一次的快照(當天第一次造訪時算,服務重啟也會重算);「今日」兩項為即時數字。", "stats.foot.cacheNote": "累計數字與圖表每天更新一次;「今日」兩項即時。",
} }
+4 -5
View File
@@ -100,9 +100,9 @@
color: var(--tb-text-muted); color: var(--tb-text-muted);
} }
/* 洞察 */ /* 洞察:这组卡片没有说明行,靠 margin-top 补出与标题的间隔 */
.tb-insights { .tb-insights {
margin: 0; margin: 8px 0 0;
padding: 0; padding: 0;
list-style: none; list-style: none;
} }
@@ -390,8 +390,8 @@
line-height: 1.8; line-height: 1.8;
color: var(--tb-text-muted); color: var(--tb-text-muted);
} }
/* 四条时间各自成行、标签左列对齐:挤在「A:x · B:y」一行里,两组日期 /* 三条时间各自成行、标签左列对齐:挤在「A:x · B:y」一行里,几个日期
靠一个间隔号分开,扫一眼分不清哪个是基准日哪个是最后提问。 */ 靠间隔号分开,扫一眼分不清哪个是快照时间哪个是最后提问。 */
.tb-foot-times { .tb-foot-times {
display: grid; display: grid;
grid-template-columns: max-content minmax(0, 1fr); grid-template-columns: max-content minmax(0, 1fr);
@@ -458,7 +458,6 @@
{{ if .Insights }} {{ if .Insights }}
<div class="tb-card"> <div class="tb-card">
<h3 class="tb-card-title">{{t $.LangCode "stats.insight.title"}}</h3> <h3 class="tb-card-title">{{t $.LangCode "stats.insight.title"}}</h3>
<p class="tb-card-note">{{t $.LangCode "stats.insight.note"}}</p>
<ul class="tb-insights"> <ul class="tb-insights">
{{ range .Insights }}<li>{{ . }}</li>{{ end }} {{ range .Insights }}<li>{{ . }}</li>{{ end }}
</ul> </ul>