diff --git a/docs/project-state.md b/docs/project-state.md index c84696d..2eb421e 100644 --- a/docs/project-state.md +++ b/docs/project-state.md @@ -82,6 +82,17 @@ Living list of what's done and what's next for blog.hoelee.com. Work through the - **Desktop banner height** capped (720px, centred): 67% → **45%** of a 900px viewport, art intact rather than cropped. - Measured post page: images **277 KB → 38 KB**, height **5,758 → 5,065px** desktop / 9,423 → 8,807px mobile. Script: `scripts/optimize-images.py`; format policy: `design-guide.md` §3; runbook step in `ops-runbook.md`. +- ✅ **B7 review-response batch, 2026-10-19** — an outside content/UX review of the live site was reconciled claim-by-claim against the repo; only the measured defects were acted on. + - **About counter was hand-typed and had drifted**: `about.astro` said **71** write-ups while the homepage rendered **72**. Both About pages now count from the collection (`getCollection('posts', isEn|isZh).length`), so the number cannot drift again. + - **Reading time was English-only maths**: `minutesOf` split the body on whitespace and divided by 200, so a Chinese post (no interior spaces) showed **1 min** against **5 min** for its English twin. New `src/lib/reading-time.ts` counts CJK ideographs at ~400/min plus Latin words at 200/min; pure-ASCII bodies are unchanged (same formula), the ZH post now reads **5 分钟**, and the ZH article header no longer says "min read". + - **ZH copy defects**: `主陆开发者` → `中国大陆开发者`; `基元律动值得麻烦的原因` → `为什么值得考虑基元律动` (the English twin already said *why it is worth the hassle*). + - **`hello-world` contradicted the site**: it still declared English primary with selective Chinese, and quoted a `~140-container homelab`. Both language twins now say every post exists in both languages and quote **162 containers across three self-managed hosts**. + - **`0 hosting companies between me and the servers` was loose** (the VPS is rented from a provider) → `0 managed hosting layers — I run all three hosts myself` (EN + ZH). + - **Hero sub-paragraph now leads with the capability**, not the container count; the number follows as evidence. `

` and `title`/JSON-LD were left alone (A2 identity alignment). + - **Featured bento cards got the `srcset`/`sizes` they were missing** (`-400`/`-800` webp) — they were downloading the 800px file for a 226–341px slot. At DPR1 all widths now pick `-400`; card geometry measured unchanged (226/227/310/341 px). + - **Measured, not assumed**: 12 routes × 4 widths (360/390/414/768) = **48/48 with zero horizontal overflow** (`scrollWidth − clientWidth == 0`); art uniformity re-surveyed — 72/72 banners 1600×900 WebP, 72/72 `-800` thumbs 800×420, 72/72 `-400` thumbs 400×210, 144/144 share cards 1200×630 JPEG. Build **251 pages**, unchanged. + - Deliberately **not** done, because the review's premise was stale or wrong: `hreflang`/canonical/`og:locale` and per-post OG images already ship (72/72 + 72/72), and the art system was already unified by B6b (round 7) — the reviewer was reading screenshots from before that rollout for the *visual* half. + --- ## 2. Execution Plan (work top → bottom, one step at a time) diff --git a/src/content/posts/hello-world.md b/src/content/posts/hello-world.md index 7e545a6..de11aa1 100644 --- a/src/content/posts/hello-world.md +++ b/src/content/posts/hello-world.md @@ -15,9 +15,9 @@ experiments mixed in. ## What to expect -English is the primary language here. I'll write selectively in Chinese -(简体中文) for topics where a Chinese-speaking audience is better served — -those posts will live under `/posts/zh/`. +Every post here exists in two languages. I write the English version first, +and the Chinese (简体中文) version is a full translation rather than a summary — +the same slug, served under `/posts/zh/`. Most posts will be one of a few shapes: @@ -35,8 +35,8 @@ Most posts will be one of a few shapes: ## About me I'm Lee Teong Hoe (Mr Hoelee), a full-stack developer and DevOps engineer -based in Malaysia. I build web applications, self-host a ~140-container -homelab, and run Hoelee Enterprise — a website design & development business +based in Malaysia. I build web applications, run 162 containers +across three self-managed hosts, and run Hoelee Enterprise — a website design & development business for Malaysian SMEs (with email hosting as a side offering, not the focus). If you're a recruiter, a client, or a fellow builder — the posts here are my diff --git a/src/content/posts/zh/hello-world.md b/src/content/posts/zh/hello-world.md index 7a9b155..9725e64 100644 --- a/src/content/posts/zh/hello-world.md +++ b/src/content/posts/zh/hello-world.md @@ -14,8 +14,7 @@ banner: /banners/hello-world.jpg ## 这里会有什么 -英语是这里的主要语言。对于中文读者能得到更好服务的主题,我会选择性地用 -简体中文写作——这些文章会放在 `/posts/zh/` 下面。 +这里的每篇文章都有两种语言。英文版先写,中文版是完整翻译,而不是摘要——同一个 slug,放在 `/posts/zh/` 下面。 大部分文章会是几种形态之一: @@ -33,7 +32,7 @@ banner: /banners/hello-world.jpg ## 关于我 我是 Lee Teong Hoe(Mr Hoelee),一名常驻马来西亚的全栈开发者兼 DevOps 工程师。 -我做 Web 应用、自托管一个约 140 个容器的家用实验室,并经营 Hoelee Enterprise +我做 Web 应用,运维三台自管主机上的 162 个容器,并经营 Hoelee Enterprise ——一家面向马来西亚中小企业的网站设计与开发公司(邮箱托管是附带的服务, 不是主业)。 diff --git a/src/content/posts/zh/using-chinese-llm-apis-from-malaysia.md b/src/content/posts/zh/using-chinese-llm-apis-from-malaysia.md index 37eddf3..076ce3e 100644 --- a/src/content/posts/zh/using-chinese-llm-apis-from-malaysia.md +++ b/src/content/posts/zh/using-chinese-llm-apis-from-malaysia.md @@ -24,7 +24,7 @@ V4.1 Flash 大约是**每百万输入 tokens 0.15–0.30 美元、每百万输 0.60–1.20 美元**(非高峰时段半价;缓存命中的输入便宜约 20 倍)。充 10 美元 能用很久——只要合理路由,agent 工作负载能跑数千万 tokens。 -问题在于:这些平台大多是为主陆开发者设计的。这意味着中国的支付渠道, +问题在于:这些平台大多是面向中国大陆开发者设计的。这意味着中国的支付渠道, 而且经常要求大陆手机号接收短信验证码。 ## 第一道墙——用人民币付款 @@ -44,7 +44,7 @@ V4.1 Flash 大约是**每百万输入 tokens 0.15–0.30 美元、每百万输 如果你没有大陆号码,DeepSeek(邮箱注册)或 BytePlus ModelArk(新加坡、 美元结算)可以完全跳过这道墙——下面会详述。 -基元律动值得麻烦的原因: +为什么值得考虑基元律动: - **一个 API Key,OpenAI 兼容。** 聚合了 GLM-5.3、Qwen3.8-Max、DeepSeek V4、 Kimi K2.7-Code、MiniMax、Seed 等模型,统一走标准 diff --git a/src/lib/reading-time.ts b/src/lib/reading-time.ts new file mode 100644 index 0000000..a3eaa31 --- /dev/null +++ b/src/lib/reading-time.ts @@ -0,0 +1,35 @@ +/** + * Reading time and word count that also work for Chinese. + * + * A whitespace split is meaningless for CJK prose: an entire Chinese article has + * almost no interior spaces, so it counted as one "word" and every ZH post showed + * "1 min read" while its English twin showed five. CJK ideographs are counted as + * characters (~400/min); everything else keeps the existing ~200 words/min. + * + * For pure-ASCII bodies the result is identical to the old whitespace/200 maths. + */ + +const CJK_CHAR = /[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\uac00-\ud7af]/; + +function counts(body?: string): { cjk: number; latin: number } { + const text = body ?? ''; + const cjk = (text.match(new RegExp(CJK_CHAR.source, 'g')) ?? []).length; + const latin = text + .replace(new RegExp(CJK_CHAR.source, 'g'), ' ') + .trim() + .split(/\s+/) + .filter(Boolean).length; + return { cjk, latin }; +} + +/** Total count used for schema.org `wordCount` (CJK counts per character). */ +export function countWords(body?: string): number { + const { cjk, latin } = counts(body); + return cjk + latin; +} + +/** Minutes of reading time, never below 1. */ +export function readingMinutes(body?: string): number { + const { cjk, latin } = counts(body); + return Math.max(1, Math.round(cjk / 400 + latin / 200)); +} diff --git a/src/pages/about.astro b/src/pages/about.astro index eafbeab..61748ac 100644 --- a/src/pages/about.astro +++ b/src/pages/about.astro @@ -1,7 +1,13 @@ --- import BaseLayout from '../layouts/BaseLayout.astro'; import AuthorCard from '../components/AuthorCard.astro'; +import { getCollection } from 'astro:content'; +import { isEn } from '../lib/lang'; import { SITE } from '../config'; + +// Counted from the content collection, never typed by hand — a hardcoded number +// here already drifted once (71 while the homepage rendered 72). +const writeUps = (await getCollection('posts', isEn)).length; ---
162containers in production
3self-managed hosts (78 compose stacks)
-
71write-ups, each in English and 中文
-
0hosting companies between me and the servers
+
{writeUps}write-ups, each in English and 中文
+
0managed hosting layers — I run all three hosts myself

That footprint is client websites, a mail platform, CI/CD, workflow automation, diff --git a/src/pages/index.astro b/src/pages/index.astro index de424be..1c0a537 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -47,9 +47,10 @@ const featuredMetrics: Record = {

{SITE.author} — full-stack developer & DevOps engineer

- I run 162 containers across three self-managed hosts — client sites, a - mail platform, CI/CD, workflow automation and this blog — and I build web systems for - Malaysian SMEs. I write up what broke, what the evidence said, and how I fixed it. + I build and operate production web systems — from the code down to the servers. + 162 containers across three self-managed hosts run client sites, a mail + platform, CI/CD, workflow automation and this blog. I write up what broke, what the + evidence said, and how I fixed it.

Read the engineering case studies ↓ @@ -72,6 +73,8 @@ const featuredMetrics: Record = {

这套环境里有客户网站、邮件平台、CI/CD、流程自动化、统计和这个博客,全靠手工拴在一起。 diff --git a/src/pages/zh/index.astro b/src/pages/zh/index.astro index 6af5219..7b0af18 100644 --- a/src/pages/zh/index.astro +++ b/src/pages/zh/index.astro @@ -51,9 +51,7 @@ const featuredMetrics: Record = {

{SITE.author} —— 全栈开发者兼 DevOps 工程师

- 我独自运维 三台自管服务器上的 162 个容器(客户站点、邮件平台、 - CI/CD、流程自动化与这个博客),并为马来西亚中小企业构建 Web 系统。这里写的是 - 「坏了什么、证据在哪、怎么修好」。 + 从代码到服务器,生产环境的 Web 系统我自己从头做到尾。三台自管服务器上跑着 162 个容器:客户站点、邮件平台、CI/CD、流程自动化,还有这个博客。这里写的是「坏了什么、证据在哪、怎么修好」。

先看工程案例 ↓ @@ -76,6 +74,8 @@ const featuredMetrics: Record = {