fix(stats): 数据速览用完整星期名,脚注时间改逐行对齐
- 「提问最集中在 三 19 点前后」:洞察复用了热力图列头的短标签 stats.wd.*(一…日 / Mon…),那是为窄列头设计的单字词条, 进句子就成了「三」。新增 stats.weekday.1..7 完整名(周一 / Monday / 月曜日),洞察改用该组,stats.wd.* 只留给列头;补测试断言锁住。 - 页脚入口文案「数据」→「统计数据」(四语言同步)。 - 页面脚注四条时间由「基准日 x · 计算于 y」一行改为两列网格逐行排列, 标签统一带冒号,下面两段说明保持独立段落。
This commit is contained in:
@@ -13,3 +13,15 @@
|
|||||||
重新生成预览确认首页无 `tb-stats-entry`,页脚渲染为「社区规则 | 数据 | 找站长」。
|
重新生成预览确认首页无 `tb-stats-entry`,页脚渲染为「社区规则 | 数据 | 找站长」。
|
||||||
- 教训:删掉页面唯一消费方后,服务端的取数调用(`stats.Get`)要同步删,
|
- 教训:删掉页面唯一消费方后,服务端的取数调用(`stats.Get`)要同步删,
|
||||||
否则每次访问首页白算一次快照;对应 i18n 词条也要一起清,避免留死条目。
|
否则每次访问首页白算一次快照;对应 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` 各占一行,
|
||||||
|
标签列与值列对齐;下面两段说明仍为独立段落。四个标签统一带冒号。
|
||||||
|
|||||||
@@ -953,7 +953,7 @@ var enDict = map[string]string{
|
|||||||
"theme.name.new-sakura": "New Sakura",
|
"theme.name.new-sakura": "New Sakura",
|
||||||
|
|
||||||
// Site statistics page (/stats): aggregate numbers only
|
// Site statistics page (/stats): aggregate numbers only
|
||||||
"site.footer.stats": "Stats",
|
"site.footer.stats": "Statistics",
|
||||||
"stats.title": "Site 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.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.",
|
"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.5": "3–7 days",
|
||||||
"stats.reply.6": "over 7 days",
|
"stats.reply.6": "over 7 days",
|
||||||
|
|
||||||
"stats.wd.1": "Mon",
|
"stats.wd.1": "Mon",
|
||||||
"stats.wd.2": "Tue",
|
"stats.wd.2": "Tue",
|
||||||
"stats.wd.3": "Wed",
|
"stats.wd.3": "Wed",
|
||||||
"stats.wd.4": "Thu",
|
"stats.wd.4": "Thu",
|
||||||
"stats.wd.5": "Fri",
|
"stats.wd.5": "Fri",
|
||||||
"stats.wd.6": "Sat",
|
"stats.wd.6": "Sat",
|
||||||
"stats.wd.7": "Sun",
|
"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.dataDate": "Data as of",
|
||||||
"stats.foot.updated": "computed at",
|
"stats.foot.updated": "computed at",
|
||||||
|
|||||||
@@ -952,7 +952,7 @@ var jaDict = map[string]string{
|
|||||||
"theme.name.new-sakura": "新さくら",
|
"theme.name.new-sakura": "新さくら",
|
||||||
|
|
||||||
// サイト統計ページ(/stats):集計値のみ
|
// サイト統計ページ(/stats):集計値のみ
|
||||||
"site.footer.stats": "統計",
|
"site.footer.stats": "統計データ",
|
||||||
"stats.title": "サイト統計",
|
"stats.title": "サイト統計",
|
||||||
"stats.lead": "ここにあるのは集計値だけです。サイト全体にどれだけ質問が届き、どれくらいで回答され、いつ人が来るのか。個々の質問内容・質問者の身元・送信元アドレスは一切表示しません。",
|
"stats.lead": "ここにあるのは集計値だけです。サイト全体にどれだけ質問が届き、どれくらいで回答され、いつ人が来るのか。個々の質問内容・質問者の身元・送信元アドレスは一切表示しません。",
|
||||||
"stats.unavailable": "統計データを一時的に取得できません。しばらくしてから再度お試しください。",
|
"stats.unavailable": "統計データを一時的に取得できません。しばらくしてから再度お試しください。",
|
||||||
@@ -1013,16 +1013,23 @@ var jaDict = map[string]string{
|
|||||||
"stats.reply.5": "3–7 日",
|
"stats.reply.5": "3–7 日",
|
||||||
"stats.reply.6": "7 日以上",
|
"stats.reply.6": "7 日以上",
|
||||||
|
|
||||||
"stats.wd.1": "月",
|
"stats.wd.1": "月",
|
||||||
"stats.wd.2": "火",
|
"stats.wd.2": "火",
|
||||||
"stats.wd.3": "水",
|
"stats.wd.3": "水",
|
||||||
"stats.wd.4": "木",
|
"stats.wd.4": "木",
|
||||||
"stats.wd.5": "金",
|
"stats.wd.5": "金",
|
||||||
"stats.wd.6": "土",
|
"stats.wd.6": "土",
|
||||||
"stats.wd.7": "日",
|
"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.dataDate": "集計基準日:",
|
||||||
"stats.foot.updated": "算出時刻",
|
"stats.foot.updated": "算出時刻:",
|
||||||
"stats.foot.prevMonth": "最後の質問:",
|
"stats.foot.prevMonth": "最後の質問:",
|
||||||
"stats.foot.prevAnswer": "最後の回答:",
|
"stats.foot.prevAnswer": "最後の回答:",
|
||||||
"stats.foot.privacy": "数値はすべて集計値です。個々の質問内容・質問者の身元・送信元アドレスは表示しません。",
|
"stats.foot.privacy": "数値はすべて集計値です。個々の質問内容・質問者の身元・送信元アドレスは表示しません。",
|
||||||
|
|||||||
+17
-10
@@ -953,7 +953,7 @@ var zhCNDict = map[string]string{
|
|||||||
"form.register.repeatPassword": "两次输入的密码不一致",
|
"form.register.repeatPassword": "两次输入的密码不一致",
|
||||||
|
|
||||||
// 全站数据页(/stats):只展示聚合数字,不含任何单条内容
|
// 全站数据页(/stats):只展示聚合数字,不含任何单条内容
|
||||||
"site.footer.stats": "数据",
|
"site.footer.stats": "统计数据",
|
||||||
"stats.title": "全站数据",
|
"stats.title": "全站数据",
|
||||||
"stats.lead": "这里只有汇总后的数字:全站收到了多少提问、回答得有多快、大家习惯什么时候来。不含任何一条具体提问的内容、提问者身份或来源地址。",
|
"stats.lead": "这里只有汇总后的数字:全站收到了多少提问、回答得有多快、大家习惯什么时候来。不含任何一条具体提问的内容、提问者身份或来源地址。",
|
||||||
"stats.unavailable": "数据暂时不可用,稍后再刷新看看。",
|
"stats.unavailable": "数据暂时不可用,稍后再刷新看看。",
|
||||||
@@ -974,7 +974,7 @@ var zhCNDict = map[string]string{
|
|||||||
"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 条提问。",
|
||||||
"stats.insight.busiest": "提问最集中在 %s %d 点前后。",
|
"stats.insight.busiest": "提问最集中在%s %d 点前后。",
|
||||||
"stats.insight.mom": "上月提问量比前一个月 %s%%。",
|
"stats.insight.mom": "上月提问量比前一个月 %s%%。",
|
||||||
"stats.insight.fastReply": "已回答的提问中,%s%% 在 24 小时内拿到了回复。",
|
"stats.insight.fastReply": "已回答的提问中,%s%% 在 24 小时内拿到了回复。",
|
||||||
"stats.insight.private": "%s%% 的提问选择了私密(不公开)。",
|
"stats.insight.private": "%s%% 的提问选择了私密(不公开)。",
|
||||||
@@ -1014,16 +1014,23 @@ var zhCNDict = map[string]string{
|
|||||||
"stats.reply.5": "3–7 天",
|
"stats.reply.5": "3–7 天",
|
||||||
"stats.reply.6": "7 天以上",
|
"stats.reply.6": "7 天以上",
|
||||||
|
|
||||||
"stats.wd.1": "一",
|
"stats.wd.1": "一",
|
||||||
"stats.wd.2": "二",
|
"stats.wd.2": "二",
|
||||||
"stats.wd.3": "三",
|
"stats.wd.3": "三",
|
||||||
"stats.wd.4": "四",
|
"stats.wd.4": "四",
|
||||||
"stats.wd.5": "五",
|
"stats.wd.5": "五",
|
||||||
"stats.wd.6": "六",
|
"stats.wd.6": "六",
|
||||||
"stats.wd.7": "日",
|
"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.dataDate": "数据基准日:",
|
||||||
"stats.foot.updated": "计算于",
|
"stats.foot.updated": "计算于:",
|
||||||
"stats.foot.prevMonth": "最后一条提问:",
|
"stats.foot.prevMonth": "最后一条提问:",
|
||||||
"stats.foot.prevAnswer": "最后一条回答:",
|
"stats.foot.prevAnswer": "最后一条回答:",
|
||||||
"stats.foot.privacy": "所有数字均为聚合结果,不展示单条提问内容、提问者身份或来源地址。",
|
"stats.foot.privacy": "所有数字均为聚合结果,不展示单条提问内容、提问者身份或来源地址。",
|
||||||
|
|||||||
+17
-10
@@ -952,7 +952,7 @@ var zhTWDict = map[string]string{
|
|||||||
"theme.name.new-sakura": "新·櫻花",
|
"theme.name.new-sakura": "新·櫻花",
|
||||||
|
|
||||||
// 全站數據頁(/stats):只顯示彙總數字,不含任何單則內容
|
// 全站數據頁(/stats):只顯示彙總數字,不含任何單則內容
|
||||||
"site.footer.stats": "數據",
|
"site.footer.stats": "統計數據",
|
||||||
"stats.title": "全站數據",
|
"stats.title": "全站數據",
|
||||||
"stats.lead": "這裡只有彙總後的數字:全站收到多少提問、回答得有多快、大家習慣什麼時候來。不含任何一則具體提問的內容、提問者身分或來源位址。",
|
"stats.lead": "這裡只有彙總後的數字:全站收到多少提問、回答得有多快、大家習慣什麼時候來。不含任何一則具體提問的內容、提問者身分或來源位址。",
|
||||||
"stats.unavailable": "數據暫時無法使用,稍後再重新整理看看。",
|
"stats.unavailable": "數據暫時無法使用,稍後再重新整理看看。",
|
||||||
@@ -973,7 +973,7 @@ var zhTWDict = map[string]string{
|
|||||||
"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 則提問。",
|
||||||
"stats.insight.busiest": "提問最集中在 %s %d 點前後。",
|
"stats.insight.busiest": "提問最集中在%s %d 點前後。",
|
||||||
"stats.insight.mom": "上月提問量比前一個月 %s%%。",
|
"stats.insight.mom": "上月提問量比前一個月 %s%%。",
|
||||||
"stats.insight.fastReply": "已回答的提問中,%s%% 在 24 小時內得到回覆。",
|
"stats.insight.fastReply": "已回答的提問中,%s%% 在 24 小時內得到回覆。",
|
||||||
"stats.insight.private": "%s%% 的提問選擇了私密(不公開)。",
|
"stats.insight.private": "%s%% 的提問選擇了私密(不公開)。",
|
||||||
@@ -1013,16 +1013,23 @@ var zhTWDict = map[string]string{
|
|||||||
"stats.reply.5": "3–7 天",
|
"stats.reply.5": "3–7 天",
|
||||||
"stats.reply.6": "7 天以上",
|
"stats.reply.6": "7 天以上",
|
||||||
|
|
||||||
"stats.wd.1": "一",
|
"stats.wd.1": "一",
|
||||||
"stats.wd.2": "二",
|
"stats.wd.2": "二",
|
||||||
"stats.wd.3": "三",
|
"stats.wd.3": "三",
|
||||||
"stats.wd.4": "四",
|
"stats.wd.4": "四",
|
||||||
"stats.wd.5": "五",
|
"stats.wd.5": "五",
|
||||||
"stats.wd.6": "六",
|
"stats.wd.6": "六",
|
||||||
"stats.wd.7": "日",
|
"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.dataDate": "數據基準日:",
|
||||||
"stats.foot.updated": "計算於",
|
"stats.foot.updated": "計算於:",
|
||||||
"stats.foot.prevMonth": "最後一則提問:",
|
"stats.foot.prevMonth": "最後一則提問:",
|
||||||
"stats.foot.prevAnswer": "最後一則回答:",
|
"stats.foot.prevAnswer": "最後一則回答:",
|
||||||
"stats.foot.privacy": "所有數字均為彙總結果,不展示單則提問內容、提問者身分或來源位址。",
|
"stats.foot.privacy": "所有數字均為彙總結果,不展示單則提問內容、提問者身分或來源位址。",
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
package stats
|
package stats
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -104,4 +105,8 @@ func TestOverviewDerived(t *testing.T) {
|
|||||||
require.Len(t, page.WeekdayLabels, 7)
|
require.Len(t, page.WeekdayLabels, 7)
|
||||||
require.Len(t, page.LengthLabels, 6)
|
require.Len(t, page.LengthLabels, 6)
|
||||||
require.Len(t, page.ReplyLabels, 6)
|
require.Len(t, page.ReplyLabels, 6)
|
||||||
|
|
||||||
|
// 句子里必须是完整星期名:热力图列头的 stats.wd.* 是单字("一"/"三"),
|
||||||
|
// 复用到句子里会读成「提问最集中在三 8 点前后」。
|
||||||
|
require.Contains(t, strings.Join(page.Insights, "\n"), "提问最集中在周一 8 点前后。")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,10 +108,14 @@ func insights(lang string, ov *Overview) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ov.HeatmapMax > 0 {
|
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
|
wd := (ov.BusiestWD+6)%7 + 1
|
||||||
out = append(out, t("stats.insight.busiest",
|
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 {
|
if ov.HasPrevMonth {
|
||||||
|
|||||||
+26
-7
@@ -350,6 +350,18 @@
|
|||||||
line-height: 1.8;
|
line-height: 1.8;
|
||||||
color: var(--tb-text-muted);
|
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 {
|
.tb-dash-empty {
|
||||||
padding: 40px 16px;
|
padding: 40px 16px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -546,13 +558,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="tb-dash-foot">
|
<div class="tb-dash-foot">
|
||||||
{{t $.LangCode "stats.foot.dataDate"}} {{ .DataDate }} · {{t $.LangCode "stats.foot.updated"}} {{ .UpdatedAt }}
|
<div class="tb-foot-times">
|
||||||
<br>{{t $.LangCode "stats.foot.prevMonth"}}{{ if .LastQuestionAt }}{{ .LastQuestionAt }}{{ else }}-{{ end }}
|
<span>{{t $.LangCode "stats.foot.dataDate"}}</span>
|
||||||
· {{t $.LangCode "stats.foot.prevAnswer"}}{{ if .LastAnswerAt }}{{ .LastAnswerAt }}{{ else }}-{{ end }}
|
<span>{{ .DataDate }}</span>
|
||||||
<br>{{t $.LangCode "stats.foot.cacheNote"}}
|
<span>{{t $.LangCode "stats.foot.updated"}}</span>
|
||||||
<br>{{t $.LangCode "stats.foot.privacy"}}
|
<span>{{ .UpdatedAt }}</span>
|
||||||
</p>
|
<span>{{t $.LangCode "stats.foot.prevMonth"}}</span>
|
||||||
|
<span>{{ if .LastQuestionAt }}{{ .LastQuestionAt }}{{ else }}-{{ end }}</span>
|
||||||
|
<span>{{t $.LangCode "stats.foot.prevAnswer"}}</span>
|
||||||
|
<span>{{ if .LastAnswerAt }}{{ .LastAnswerAt }}{{ else }}-{{ end }}</span>
|
||||||
|
</div>
|
||||||
|
<p class="tb-foot-note">{{t $.LangCode "stats.foot.cacheNote"}}</p>
|
||||||
|
<p class="tb-foot-note">{{t $.LangCode "stats.foot.privacy"}}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<div class="tb-dash-empty">{{t .LangCode "stats.unavailable"}}</div>
|
<div class="tb-dash-empty">{{t .LangCode "stats.unavailable"}}</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user