From 64436d9d6183f4f37a06dfb9473e4943c7484183 Mon Sep 17 00:00:00 2001 From: tamakyi Date: Sat, 24 Oct 2026 01:08:50 +0800 Subject: [PATCH] =?UTF-8?q?fix(stats):=20=E6=95=B0=E6=8D=AE=E9=80=9F?= =?UTF-8?q?=E8=A7=88=E7=94=A8=E5=AE=8C=E6=95=B4=E6=98=9F=E6=9C=9F=E5=90=8D?= =?UTF-8?q?=EF=BC=8C=E8=84=9A=E6=B3=A8=E6=97=B6=E9=97=B4=E6=94=B9=E9=80=90?= =?UTF-8?q?=E8=A1=8C=E5=AF=B9=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 「提问最集中在 三 19 点前后」:洞察复用了热力图列头的短标签 stats.wd.*(一…日 / Mon…),那是为窄列头设计的单字词条, 进句子就成了「三」。新增 stats.weekday.1..7 完整名(周一 / Monday / 月曜日),洞察改用该组,stats.wd.* 只留给列头;补测试断言锁住。 - 页脚入口文案「数据」→「统计数据」(四语言同步)。 - 页面脚注四条时间由「基准日 x · 计算于 y」一行改为两列网格逐行排列, 标签统一带冒号,下面两段说明保持独立段落。 --- .workbuddy/memory/2026-10-24.md | 12 ++++++++++++ internal/i18n/dict_en.go | 23 +++++++++++++++-------- internal/i18n/dict_ja.go | 25 ++++++++++++++++--------- internal/i18n/dict_zh_cn.go | 27 +++++++++++++++++---------- internal/i18n/dict_zh_tw.go | 27 +++++++++++++++++---------- internal/stats/overview_test.go | 5 +++++ internal/stats/page.go | 8 ++++++-- templates/stats.html | 33 ++++++++++++++++++++++++++------- 8 files changed, 114 insertions(+), 46 deletions(-) diff --git a/.workbuddy/memory/2026-10-24.md b/.workbuddy/memory/2026-10-24.md index f2dfc53..82f7d0e 100644 --- a/.workbuddy/memory/2026-10-24.md +++ b/.workbuddy/memory/2026-10-24.md @@ -13,3 +13,15 @@ 重新生成预览确认首页无 `tb-stats-entry`,页脚渲染为「社区规则 | 数据 | 找站长」。 - 教训:删掉页面唯一消费方后,服务端的取数调用(`stats.Get`)要同步删, 否则每次访问首页白算一次快照;对应 i18n 词条也要一起清,避免留死条目。 + +## /stats 文案与脚注排版修正 + +- 页脚入口文案「数据」→「统计数据」(`site.footer.stats`,四语言 `Statistics` / `統計データ` / `統計數據`)。 +- **「提问最集中在三 19 点前后」的 bug**:洞察句子复用了热力图列头的 `stats.wd.*` + ("一"…"日" / "Mon"…),那是为窄列头设计的单字标签,进句子就成了"三"、英文是 "Wed"。 + 新增一组完整名 `stats.weekday.1..7`(周一/週一/Monday/月曜日),洞察改用这组; + `stats.wd.*` 只留给热力图列头。已在 `overview_test.go` 加断言锁住,防止回退。 + → 教训:**短标签和完整名必须分两套词条**,别为了少写一组 key 复用。 +- 页面脚注四条时间(基准日 / 计算于 / 最后一条提问 / 最后一条回答)由 + 「A:x · B:y」挤成一行改为 `grid-template-columns: max-content 1fr` 各占一行, + 标签列与值列对齐;下面两段说明仍为独立段落。四个标签统一带冒号。 diff --git a/internal/i18n/dict_en.go b/internal/i18n/dict_en.go index 4905e13..f735eb8 100644 --- a/internal/i18n/dict_en.go +++ b/internal/i18n/dict_en.go @@ -953,7 +953,7 @@ var enDict = map[string]string{ "theme.name.new-sakura": "New Sakura", // Site statistics page (/stats): aggregate numbers only - "site.footer.stats": "Stats", + "site.footer.stats": "Statistics", "stats.title": "Site statistics", "stats.lead": "Only aggregate numbers here: how many questions the site receives, how fast they get answered, and when people tend to show up. No individual question, asker identity, or source address is shown.", "stats.unavailable": "Statistics are temporarily unavailable. Please check back later.", @@ -1014,13 +1014,20 @@ var enDict = map[string]string{ "stats.reply.5": "3–7 days", "stats.reply.6": "over 7 days", - "stats.wd.1": "Mon", - "stats.wd.2": "Tue", - "stats.wd.3": "Wed", - "stats.wd.4": "Thu", - "stats.wd.5": "Fri", - "stats.wd.6": "Sat", - "stats.wd.7": "Sun", + "stats.wd.1": "Mon", + "stats.wd.2": "Tue", + "stats.wd.3": "Wed", + "stats.wd.4": "Thu", + "stats.wd.5": "Fri", + "stats.wd.6": "Sat", + "stats.wd.7": "Sun", + "stats.weekday.1": "Monday", + "stats.weekday.2": "Tuesday", + "stats.weekday.3": "Wednesday", + "stats.weekday.4": "Thursday", + "stats.weekday.5": "Friday", + "stats.weekday.6": "Saturday", + "stats.weekday.7": "Sunday", "stats.foot.dataDate": "Data as of", "stats.foot.updated": "computed at", diff --git a/internal/i18n/dict_ja.go b/internal/i18n/dict_ja.go index a99433e..57dc95f 100644 --- a/internal/i18n/dict_ja.go +++ b/internal/i18n/dict_ja.go @@ -952,7 +952,7 @@ var jaDict = map[string]string{ "theme.name.new-sakura": "新さくら", // サイト統計ページ(/stats):集計値のみ - "site.footer.stats": "統計", + "site.footer.stats": "統計データ", "stats.title": "サイト統計", "stats.lead": "ここにあるのは集計値だけです。サイト全体にどれだけ質問が届き、どれくらいで回答され、いつ人が来るのか。個々の質問内容・質問者の身元・送信元アドレスは一切表示しません。", "stats.unavailable": "統計データを一時的に取得できません。しばらくしてから再度お試しください。", @@ -1013,16 +1013,23 @@ var jaDict = map[string]string{ "stats.reply.5": "3–7 日", "stats.reply.6": "7 日以上", - "stats.wd.1": "月", - "stats.wd.2": "火", - "stats.wd.3": "水", - "stats.wd.4": "木", - "stats.wd.5": "金", - "stats.wd.6": "土", - "stats.wd.7": "日", + "stats.wd.1": "月", + "stats.wd.2": "火", + "stats.wd.3": "水", + "stats.wd.4": "木", + "stats.wd.5": "金", + "stats.wd.6": "土", + "stats.wd.7": "日", + "stats.weekday.1": "月曜日", + "stats.weekday.2": "火曜日", + "stats.weekday.3": "水曜日", + "stats.weekday.4": "木曜日", + "stats.weekday.5": "金曜日", + "stats.weekday.6": "土曜日", + "stats.weekday.7": "日曜日", "stats.foot.dataDate": "集計基準日:", - "stats.foot.updated": "算出時刻", + "stats.foot.updated": "算出時刻:", "stats.foot.prevMonth": "最後の質問:", "stats.foot.prevAnswer": "最後の回答:", "stats.foot.privacy": "数値はすべて集計値です。個々の質問内容・質問者の身元・送信元アドレスは表示しません。", diff --git a/internal/i18n/dict_zh_cn.go b/internal/i18n/dict_zh_cn.go index 2325b26..39ad4a7 100644 --- a/internal/i18n/dict_zh_cn.go +++ b/internal/i18n/dict_zh_cn.go @@ -953,7 +953,7 @@ var zhCNDict = map[string]string{ "form.register.repeatPassword": "两次输入的密码不一致", // 全站数据页(/stats):只展示聚合数字,不含任何单条内容 - "site.footer.stats": "数据", + "site.footer.stats": "统计数据", "stats.title": "全站数据", "stats.lead": "这里只有汇总后的数字:全站收到了多少提问、回答得有多快、大家习惯什么时候来。不含任何一条具体提问的内容、提问者身份或来源地址。", "stats.unavailable": "数据暂时不可用,稍后再刷新看看。", @@ -974,7 +974,7 @@ var zhCNDict = map[string]string{ "stats.insight.empty": "提问数据还太少,暂时看不出什么。", "stats.insight.daily30": "近 30 天共收到 %s 条提问,日均 %.1f 条。", "stats.insight.peak": "最忙的一天是 %s,收到 %s 条提问。", - "stats.insight.busiest": "提问最集中在 %s %d 点前后。", + "stats.insight.busiest": "提问最集中在%s %d 点前后。", "stats.insight.mom": "上月提问量比前一个月 %s%%。", "stats.insight.fastReply": "已回答的提问中,%s%% 在 24 小时内拿到了回复。", "stats.insight.private": "%s%% 的提问选择了私密(不公开)。", @@ -1014,16 +1014,23 @@ var zhCNDict = map[string]string{ "stats.reply.5": "3–7 天", "stats.reply.6": "7 天以上", - "stats.wd.1": "一", - "stats.wd.2": "二", - "stats.wd.3": "三", - "stats.wd.4": "四", - "stats.wd.5": "五", - "stats.wd.6": "六", - "stats.wd.7": "日", + "stats.wd.1": "一", + "stats.wd.2": "二", + "stats.wd.3": "三", + "stats.wd.4": "四", + "stats.wd.5": "五", + "stats.wd.6": "六", + "stats.wd.7": "日", + "stats.weekday.1": "周一", + "stats.weekday.2": "周二", + "stats.weekday.3": "周三", + "stats.weekday.4": "周四", + "stats.weekday.5": "周五", + "stats.weekday.6": "周六", + "stats.weekday.7": "周日", "stats.foot.dataDate": "数据基准日:", - "stats.foot.updated": "计算于", + "stats.foot.updated": "计算于:", "stats.foot.prevMonth": "最后一条提问:", "stats.foot.prevAnswer": "最后一条回答:", "stats.foot.privacy": "所有数字均为聚合结果,不展示单条提问内容、提问者身份或来源地址。", diff --git a/internal/i18n/dict_zh_tw.go b/internal/i18n/dict_zh_tw.go index cef9a9e..e1ae7ff 100644 --- a/internal/i18n/dict_zh_tw.go +++ b/internal/i18n/dict_zh_tw.go @@ -952,7 +952,7 @@ var zhTWDict = map[string]string{ "theme.name.new-sakura": "新·櫻花", // 全站數據頁(/stats):只顯示彙總數字,不含任何單則內容 - "site.footer.stats": "數據", + "site.footer.stats": "統計數據", "stats.title": "全站數據", "stats.lead": "這裡只有彙總後的數字:全站收到多少提問、回答得有多快、大家習慣什麼時候來。不含任何一則具體提問的內容、提問者身分或來源位址。", "stats.unavailable": "數據暫時無法使用,稍後再重新整理看看。", @@ -973,7 +973,7 @@ var zhTWDict = map[string]string{ "stats.insight.empty": "提問數據還太少,暫時看不出什麼。", "stats.insight.daily30": "近 30 天共收到 %s 則提問,日均 %.1f 則。", "stats.insight.peak": "最忙的一天是 %s,收到 %s 則提問。", - "stats.insight.busiest": "提問最集中在 %s %d 點前後。", + "stats.insight.busiest": "提問最集中在%s %d 點前後。", "stats.insight.mom": "上月提問量比前一個月 %s%%。", "stats.insight.fastReply": "已回答的提問中,%s%% 在 24 小時內得到回覆。", "stats.insight.private": "%s%% 的提問選擇了私密(不公開)。", @@ -1013,16 +1013,23 @@ var zhTWDict = map[string]string{ "stats.reply.5": "3–7 天", "stats.reply.6": "7 天以上", - "stats.wd.1": "一", - "stats.wd.2": "二", - "stats.wd.3": "三", - "stats.wd.4": "四", - "stats.wd.5": "五", - "stats.wd.6": "六", - "stats.wd.7": "日", + "stats.wd.1": "一", + "stats.wd.2": "二", + "stats.wd.3": "三", + "stats.wd.4": "四", + "stats.wd.5": "五", + "stats.wd.6": "六", + "stats.wd.7": "日", + "stats.weekday.1": "週一", + "stats.weekday.2": "週二", + "stats.weekday.3": "週三", + "stats.weekday.4": "週四", + "stats.weekday.5": "週五", + "stats.weekday.6": "週六", + "stats.weekday.7": "週日", "stats.foot.dataDate": "數據基準日:", - "stats.foot.updated": "計算於", + "stats.foot.updated": "計算於:", "stats.foot.prevMonth": "最後一則提問:", "stats.foot.prevAnswer": "最後一則回答:", "stats.foot.privacy": "所有數字均為彙總結果,不展示單則提問內容、提問者身分或來源位址。", diff --git a/internal/stats/overview_test.go b/internal/stats/overview_test.go index ffbc74b..c316722 100644 --- a/internal/stats/overview_test.go +++ b/internal/stats/overview_test.go @@ -5,6 +5,7 @@ package stats import ( + "strings" "testing" "time" @@ -104,4 +105,8 @@ func TestOverviewDerived(t *testing.T) { require.Len(t, page.WeekdayLabels, 7) require.Len(t, page.LengthLabels, 6) require.Len(t, page.ReplyLabels, 6) + + // 句子里必须是完整星期名:热力图列头的 stats.wd.* 是单字("一"/"三"), + // 复用到句子里会读成「提问最集中在三 8 点前后」。 + require.Contains(t, strings.Join(page.Insights, "\n"), "提问最集中在周一 8 点前后。") } diff --git a/internal/stats/page.go b/internal/stats/page.go index 453129b..98767cd 100644 --- a/internal/stats/page.go +++ b/internal/stats/page.go @@ -108,10 +108,14 @@ func insights(lang string, ov *Overview) []string { } if ov.HeatmapMax > 0 { - // DOW 以周日为 0,展示顺序以周一为 1,换算到 stats.wd.N。 + // DOW 以周日为 0,展示顺序以周一为 1,换算到 stats.weekday.N。 + // + // 句子里必须用完整星期名(stats.weekday.*),不能用热力图列头那套 + // 单字标签(stats.wd.* = "一"/"Mon"):列头要窄,句子里"三 19 点" + // 会被读成数字 3,英文那边则直接是 "Wed 19:00"。 wd := (ov.BusiestWD+6)%7 + 1 out = append(out, t("stats.insight.busiest", - i18n.T(lang, fmt.Sprintf("stats.wd.%d", wd)), ov.BusiestHr)) + i18n.T(lang, fmt.Sprintf("stats.weekday.%d", wd)), ov.BusiestHr)) } if ov.HasPrevMonth { diff --git a/templates/stats.html b/templates/stats.html index 0e547d0..2c435da 100644 --- a/templates/stats.html +++ b/templates/stats.html @@ -350,6 +350,18 @@ line-height: 1.8; color: var(--tb-text-muted); } + /* 四条时间各自成行、标签左列对齐:挤在「A:x · B:y」一行里,两组日期 + 靠一个间隔号分开,扫一眼分不清哪个是基准日哪个是最后提问。 */ + .tb-foot-times { + display: grid; + grid-template-columns: max-content minmax(0, 1fr); + column-gap: 8px; + row-gap: 2px; + margin-bottom: 8px; + } + .tb-foot-note { + margin: 0; + } .tb-dash-empty { padding: 40px 16px; text-align: center; @@ -546,13 +558,20 @@ -

- {{t $.LangCode "stats.foot.dataDate"}} {{ .DataDate }} · {{t $.LangCode "stats.foot.updated"}} {{ .UpdatedAt }} -
{{t $.LangCode "stats.foot.prevMonth"}}{{ if .LastQuestionAt }}{{ .LastQuestionAt }}{{ else }}-{{ end }} - · {{t $.LangCode "stats.foot.prevAnswer"}}{{ if .LastAnswerAt }}{{ .LastAnswerAt }}{{ else }}-{{ end }} -
{{t $.LangCode "stats.foot.cacheNote"}} -
{{t $.LangCode "stats.foot.privacy"}} -

+
+
+ {{t $.LangCode "stats.foot.dataDate"}} + {{ .DataDate }} + {{t $.LangCode "stats.foot.updated"}} + {{ .UpdatedAt }} + {{t $.LangCode "stats.foot.prevMonth"}} + {{ if .LastQuestionAt }}{{ .LastQuestionAt }}{{ else }}-{{ end }} + {{t $.LangCode "stats.foot.prevAnswer"}} + {{ if .LastAnswerAt }}{{ .LastAnswerAt }}{{ else }}-{{ end }} +
+

{{t $.LangCode "stats.foot.cacheNote"}}

+

{{t $.LangCode "stats.foot.privacy"}}

+
{{ else }}
{{t .LangCode "stats.unavailable"}}