From be76efb59d703cd5b16586fd090ff9edee039593 Mon Sep 17 00:00:00 2001 From: Eason WaveKat Date: Sun, 6 Sep 2026 21:46:04 +1200 Subject: [PATCH] feat: show latest version on download page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /voice/download/ listed five installers and never said which release they were. The version only appeared on /voice/ and in the changelog, so the one page a visitor opens to choose a build was the page that couldn't tell them what they were about to get. One line above the grid, in all nine locales: Latest WaveKat Voice release v0.0.52 · What's New → It lives in VoiceDownloadAll.astro rather than in the nine download.astro pages. All nine already render that component under their own localized heading, and it already fetches every release and renders VoiceDownloadScripts, so this is one edit instead of nine and the locales cannot drift apart. Keyed to `mac` and nothing else. The printed value and the browser-side correction have to name the same target or they can disagree the first time a release skips one, and mac is the target that has never been null -- it is in the offline FALLBACK table and the App Store build is cut from every release. So the SSR value, the `data-dl-version` the existing refresh writes into, and the `data-dl-row` that withdraws the whole line if the platform ever reports mac unpublished are one key. No new script, no second fetch: VoiceDownloadScripts already does both for the rows, which means a release landing between site deploys updates this line without a rebuild. Page-level rather than per-row because half the grid could never carry a version: Apple and Microsoft resolve their own builds and report nothing back here, which is why the store rows have no size either. The label names the product even though the

two lines above already does. A passage extractor lifts this

on its own, and "Latest release v0.0.52" alone states a version of nothing; with the entity in it the line survives being quoted. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_0147ECxA2yZDU2eei8RHhtMj --- src/components/VoiceDownloadAll.astro | 32 +++++++++++++++++++++++++++ src/lib/i18n.ts | 15 +++++++++++++ src/lib/ui/de.ts | 1 + src/lib/ui/es.ts | 1 + src/lib/ui/fr.ts | 1 + src/lib/ui/it.ts | 1 + src/lib/ui/ja.ts | 1 + src/lib/ui/ko.ts | 1 + src/lib/ui/zh-hant.ts | 1 + 9 files changed, 54 insertions(+) diff --git a/src/components/VoiceDownloadAll.astro b/src/components/VoiceDownloadAll.astro index 3d806ff..c36a2a2 100644 --- a/src/components/VoiceDownloadAll.astro +++ b/src/components/VoiceDownloadAll.astro @@ -24,6 +24,11 @@ const ui = t(lang); // the SmartScreen walkthrough is a section further down it. const unsignedHref = `${localizedPath('/voice/download/', lang)}#windows`; +// The changelog is where the version number leads: a reader who wants to +// know what they are about to install wants the release notes, not the +// number on its own. +const changelogHref = localizedPath('/voice/changelog/', lang); + const [mac, linuxX64, linuxArm, winX64, winArm] = await Promise.all([ getDownload('mac'), getDownload('linux-x64'), @@ -39,6 +44,33 @@ const groups = buildGroups( ); --- +{/* The version, once, above the whole grid — the page states five + targets and this states the release they all come from. + + Keyed to `mac` and to nothing else, deliberately. The printed value and + the browser-side correction have to name the SAME target or they can + disagree the first time a release skips one, and `mac` is the target + that has never been null: it is in the offline FALLBACK table, and the + App Store build is cut from every release. So the SSR value, the + `data-dl-version` the refresh writes into, and the `data-dl-row` that + withdraws the whole line if the platform ever reports mac unpublished + are one key. No new script and no second fetch — `VoiceDownloadScripts` + below already does both for the rows. + + The store rows are the reason this is a page-level line rather than a + per-row one: Apple and Microsoft resolve their own builds and report no + version back here, so half the grid could never carry one. */} +

+ {ui.dlLatestRelease} v{mac?.version} · {ui.subChangelog} → +

+ {/* Three equal columns that stack on a phone. `items-start` so a column with fewer rows doesn't stretch its card to match the tallest. */}
diff --git a/src/lib/i18n.ts b/src/lib/i18n.ts index 7e38d21..44714f4 100644 --- a/src/lib/i18n.ts +++ b/src/lib/i18n.ts @@ -261,6 +261,19 @@ export interface UIStrings { * label has to promise the whole set rather than "more platforms". */ dlAllDownloads: string; + /** + * Precedes the version number above the download grid, e.g. "Latest + * WaveKat Voice release v0.0.52". A label, not a sentence — the version + * and the changelog link follow it. + * + * It names the product on purpose, even though the

two lines above + * already does. A passage extractor lifts this

on its own, and + * "Latest release v0.0.52" alone states a version of nothing; with the + * entity in it the line is a fact that survives being quoted. The + * product name is never translated (CLAUDE.md), so only the words + * around it change per locale. + */ + dlLatestRelease: string; talkHeading: string; talkBody: string; talkButton: string; @@ -334,6 +347,7 @@ const strings: Record = { "The direct Windows downloads aren't code-signed, so Windows warns on first launch. The Microsoft Store version is signed.", dlWindowsUnsignedLink: 'How to get past it', dlAllDownloads: 'All downloads and installers', + dlLatestRelease: 'Latest WaveKat Voice release', talkHeading: 'Talk to us', talkBody: 'Questions, feedback, or a device you wish we supported? Email is the best way to reach us — we read every message and reply as soon as we can.', @@ -388,6 +402,7 @@ const strings: Record = { '直接下载的 Windows 版尚未进行代码签名,首次启动时系统会弹出提醒;Microsoft Store 版本已签名。', dlWindowsUnsignedLink: '如何继续安装', dlAllDownloads: '所有下载与安装程序', + dlLatestRelease: 'WaveKat Voice 最新版本', talkHeading: '联系我们', talkBody: '有疑问、建议,或希望我们支持某种设备?邮件是联系我们的最佳方式——我们会阅读每一条消息,并尽快回复。', diff --git a/src/lib/ui/de.ts b/src/lib/ui/de.ts index 5e37a28..a1bfc0e 100644 --- a/src/lib/ui/de.ts +++ b/src/lib/ui/de.ts @@ -34,6 +34,7 @@ export const ui: UIStrings = { 'Die direkten Windows-Downloads sind nicht signiert, deshalb warnt Windows beim ersten Start. Die Version aus dem Microsoft Store ist signiert.', dlWindowsUnsignedLink: 'So kommen Sie daran vorbei', dlAllDownloads: 'Alle Downloads und Installer', + dlLatestRelease: 'Neueste Version von WaveKat Voice', talkHeading: 'Kontakt', talkBody: 'Fragen, Feedback oder ein Gerät, das Sie sich von uns wünschen? Am besten erreichen Sie uns per E-Mail — wir lesen jede Nachricht und antworten, so schnell wir können.', diff --git a/src/lib/ui/es.ts b/src/lib/ui/es.ts index 5bfd4ab..7da4254 100644 --- a/src/lib/ui/es.ts +++ b/src/lib/ui/es.ts @@ -34,6 +34,7 @@ export const ui: UIStrings = { 'Las descargas directas para Windows no están firmadas, así que Windows avisa al abrirlas por primera vez. La versión de Microsoft Store sí está firmada.', dlWindowsUnsignedLink: 'Cómo continuar', dlAllDownloads: 'Todas las descargas e instaladores', + dlLatestRelease: 'Última versión de WaveKat Voice', talkHeading: 'Contacto', talkBody: '¿Preguntas, comentarios o un dispositivo que le gustaría que admitiéramos? El correo electrónico es la mejor forma de contactarnos: leemos cada mensaje y respondemos en cuanto podemos.', diff --git a/src/lib/ui/fr.ts b/src/lib/ui/fr.ts index e1c3248..8574e5c 100644 --- a/src/lib/ui/fr.ts +++ b/src/lib/ui/fr.ts @@ -34,6 +34,7 @@ export const ui: UIStrings = { "Les téléchargements directs pour Windows ne sont pas signés : Windows affiche un avertissement au premier lancement. La version du Microsoft Store, elle, est signée.", dlWindowsUnsignedLink: 'Comment passer outre', dlAllDownloads: 'Tous les téléchargements et installateurs', + dlLatestRelease: 'Dernière version de WaveKat Voice', talkHeading: 'Nous contacter', talkBody: 'Une question, un retour, ou un appareil que vous aimeriez nous voir prendre en charge ? L’e-mail est le meilleur moyen de nous joindre — nous lisons chaque message et répondons dès que possible.', diff --git a/src/lib/ui/it.ts b/src/lib/ui/it.ts index e7dfb58..aec2da3 100644 --- a/src/lib/ui/it.ts +++ b/src/lib/ui/it.ts @@ -34,6 +34,7 @@ export const ui: UIStrings = { 'I download diretti per Windows non sono firmati, quindi Windows avvisa al primo avvio. La versione di Microsoft Store è firmata.', dlWindowsUnsignedLink: 'Come procedere', dlAllDownloads: 'Tutti i download e gli installer', + dlLatestRelease: 'Ultima versione di WaveKat Voice', talkHeading: 'Contattaci', talkBody: 'Domande, suggerimenti o un dispositivo che vorresti vedere supportato? L’email è il modo migliore per raggiungerci: leggiamo ogni messaggio e rispondiamo il prima possibile.', diff --git a/src/lib/ui/ja.ts b/src/lib/ui/ja.ts index dca7e5c..f241e31 100644 --- a/src/lib/ui/ja.ts +++ b/src/lib/ui/ja.ts @@ -34,6 +34,7 @@ export const ui: UIStrings = { '直接ダウンロードした Windows 版はコード署名されていないため、初回起動時に警告が表示されます。Microsoft Store 版は署名済みです。', dlWindowsUnsignedLink: '進める方法', dlAllDownloads: 'すべてのダウンロードとインストーラー', + dlLatestRelease: 'WaveKat Voice の最新バージョン', talkHeading: 'お問い合わせ', talkBody: diff --git a/src/lib/ui/ko.ts b/src/lib/ui/ko.ts index 5951dc3..35d233f 100644 --- a/src/lib/ui/ko.ts +++ b/src/lib/ui/ko.ts @@ -34,6 +34,7 @@ export const ui: UIStrings = { '직접 내려받은 Windows 빌드는 코드 서명이 되어 있지 않아 첫 실행 시 경고가 표시됩니다. Microsoft Store 버전은 서명되어 있습니다.', dlWindowsUnsignedLink: '넘어가는 방법', dlAllDownloads: '모든 다운로드 및 설치 파일', + dlLatestRelease: 'WaveKat Voice 최신 버전', talkHeading: '문의하기', talkBody: '궁금한 점이나 의견이 있으신가요? 지원했으면 하는 기기가 있으신가요? 이메일이 저희에게 연락하는 가장 좋은 방법입니다. 모든 메시지를 읽고 가능한 한 빨리 답변드립니다.', diff --git a/src/lib/ui/zh-hant.ts b/src/lib/ui/zh-hant.ts index 993e2fb..3c3604b 100644 --- a/src/lib/ui/zh-hant.ts +++ b/src/lib/ui/zh-hant.ts @@ -34,6 +34,7 @@ export const ui: UIStrings = { '直接下載的 Windows 版尚未進行程式碼簽章,首次啟動時系統會跳出提醒;Microsoft Store 版本已簽章。', dlWindowsUnsignedLink: '如何繼續安裝', dlAllDownloads: '所有下載與安裝程式', + dlLatestRelease: 'WaveKat Voice 最新版本', talkHeading: '聯絡我們', talkBody: '有疑問、建議,或希望我們支援某款裝置?電子郵件是聯絡我們的最佳方式——我們會閱讀每一則訊息,並盡快回覆。',