TutorLoom
기업·대학·교육기관을 위한 학습 플랫폼, 무료 오픈소스이며 엔터프라이즈 버전 제공
여행자를 위한 스페인·스웨덴·독일어 기본 문구 무료 제공(택시·바·속어·약국 등), 500줄 이상, 계정 필요 없음
Free Spanish, Swedish and German phrases for travelers: taxis, bars, slang, pharmacies, each with what people say back. over 500 lines, no account needed.
교육TypeScript · PythonGitHub @gabrielgilles11-arch온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기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.