尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Language learning platform to learn like a local
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Conversational Spanish, Swedish and German for people who are about to be in the country. The lines you actually need at a bar, in a taxi, or looking for the metro, rather than the ones a textbook opens with.
Live: tryconvi.com
Three editions ship: Spanish as spoken in Spain, Swedish as spoken in Stockholm, and German. Every scenario is a real exchange — what you say, and what comes back at you — and every target-language line can be played aloud.
Not the CRUD. These are the problems that took real work:
A multiple-choice question with two right answers cannot be answered.
Distractors are drawn from the same deck as the answer, so sooner or later one
of them is the answer in different words: "A beer, please" against "A small
beer, please" are both what ¿Una caña porfa? means. String equality does not
catch that. tooAlike tests two shapes of the failure —
one line containing all of another's words, and heavy overlap without
containment — while leaving genuine contrasts ("Draft, thanks" / "Bottle,
thanks") alone. Options are then built under tiered guards that are given up
one at a time, because a stage with three items still has to fill four boxes.
Analytics that can count returning visitors without knowing who they are.
The only thing every page view carries is an IP address, which is personal
data. It is hashed with a secret salt on arrival and only the hash is stored,
under a key that deletes itself after thirty quiet days. The salt is the part
that matters: unsalted, an IP hash is not anonymous at all — there are only
four billion addresses, so anyone holding the hashes could reverse them in an
afternoon. See src/lib/visitor.ts.
A paywall that can be switched off without being deleted.
PAYWALL_ENABLED is one boolean in a dependency-free
module that both the server and the browser can read. Turning it off left the
Gumroad product, the signed entitlement cookie, the device limit and the
webhook entirely intact — so the decision is reversible in one line rather than
in a revert of forty commits.
Speech that survives a network that cannot reach the model host.
Lines are spoken from generated clips where they exist and the device's own
voice where they do not. Generating them means reaching a speech endpoint
(Edge TTS by default), which the development sandbox cannot do — so a GitHub
Actions workflow does it on a runner:
audition attaches samples of every candidate voice to the run, and record
re-records a whole edition in the one you pick and commits it. The voice picker is scored by name, after discovering that
preferring localService voices reliably selected the worst voice on the
machine: on every desktop platform the local voices are the old ones.
Scenario pages are prerendered at build time, because they are what search
engines and answer engines read. Practice is server-rendered and pulls its
deck from /api/quiz-items, so the content never ships in the client bundle.
Content is authored as JSON per locale rather than in a CMS — it is edited far
less often than it is read, and keeping it in the repository means a content
change is reviewed like a code change and tested by the same suite.
Fuller notes in ARCHITECTURE.md.
npm install
cp .env.example .env # every value is optional for local development
npm run dev # http://localhost:4321
| Command | What it does |
|---|---|
npm run dev | Dev server |
npm test | 57 tests over the quiz engine |
npm run test:watch | The same, on change |
npx astro check | Typecheck, including .astro files |
npm run build | Production build |
Nothing needs a key to run. Without Redis the rate limiter and the usage counters both fail open; without a TTS key the device voice reads the lines. Every variable is listed with no values in .env.example.
npm test
They run against the decks that actually ship, so a content edit that
reintroduces two interchangeable answers fails here rather than reaching a
learner — which is how the ¿Una caña porfa? question got in front of someone
in the first place.
Each test was checked by breaking the thing it guards and confirming it went red. Two of them turned out to be decorative and were rewritten: one compared answers with an equality that could never catch a near-duplicate, and another allowed a 2% clash rate against a real rate of 0.00%, so deleting the entire separation pass still passed. An allowance nobody measured is indistinguishable from no test at all.
Astro 7 (SSR + prerendering, @astrojs/vercel) · React 19 islands ·
TypeScript, strict, no any · Upstash Redis · Vercel · Edge TTS / Google / Azure / Piper
TTS · Vitest
Written solo, using Claude Code as a pair-programmer. I chose the architecture, reviewed every change and rejected plenty; the commit history is unedited and shows exactly who wrote what. The interesting parts above are the ones where the first answer was wrong and the second was found by testing it.
Two licences, because there are two kinds of work here.
content/ and
public/audio/. Take it and build on it.