playtranslate
Android 실시간 화면 번역 앱, 언어 학습자와 캐주얼 게이머용
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기VOA Let's Learn English 非官方静态自学页
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A small, static self-study shell for Let's Learn English.
This is frontend-only. There is no login, backend, or crawler. It is not an official VOA product.README 전문 보기 ↗A small, static self-study shell for Let's Learn English.
This is frontend-only. There is no login, backend, or crawler. It is not an official VOA product.
lle1-01 through lle1-52lle2-01 through lle2-30 (Budget Cuts, The Interview, He Said - She Said, Run Away With the Circus!, Greatest Vacation of All Time, Will It Float?, Tip Your Tour Guide, The Best Barbecue, Pets Are Family, Too!, Visit to Peru, The Big Snow, Run! Bees!, Save the Bees!, Made for Each Other, Before and After, Find Your Joy!, Flour Baby, Part 1, Flour Baby, Part 2, Movie Night, The Test Drive, Trash to Treasure, Part 1, Trash to Treasure, Part 2, Rock Star, I Feel Super!, Only Human, Look-alikes, Fish out of Water, For the Birds, Where There's Smoke..., Dream a Little Dream)localStorage under voa-lle-progress (lessonId → { score, total, completed, savedAt })voa-lle-checkins) after each quiz submitvoa-lle-wrongbook) for missed quiz questions| Page | File | Notes |
|---|---|---|
| 课表 | index.html | Lesson cards plus a month check-in calendar. Locked cards show a lock badge |
| 打卡 | progress.html | Dedicated streak + month grid |
| 错题本 | wrongbook.html | Missed questions grouped by lesson |
| 开通 | pricing.html | Plans, manual-payment note, redeem-code unlock |
| Lesson | lessons/lle1-01.html | One static page per lesson. Video, dialogue, quiz. Paid lessons show a paywall until unlocked. Prev/next stay inside the current level. Old lesson.html?id=lle1-01 redirects here |
Shared render, quiz, progress, check-in, unlock, and wrong-book logic lives in js/study.js, js/unlock.js, and js/app.js.
Dates use Asia/Shanghai (YYYY-MM-DD). There is no account; clearing site data clears study history.
| Key | Shape | When it updates |
|---|---|---|
voa-lle-progress | { [lessonId]: { score, total, completed, savedAt, answers, resultText } } | Opening a lesson marks it in progress; submitting a quiz stores the score |
voa-lle-checkins | string[] of YYYY-MM-DD | Any lesson quiz submit records today (deduped) |
voa-lle-wrongbook | { lessonId, lessonTitle, questionId, prompt, choices, correctIndex, chosenIndex, savedAt }[] | Wrong answers are upserted by lessonId + questionId. A later correct answer removes that item |
voa-lle-unlock | { active, code, plan, unlockedAt, expiresAt } | Set after a valid redeem code. While active and expiresAt is in the future, paid lessons open. Missing expiresAt counts as expired. |
voa-lle-script | zh-Hans or zh-Hant | Set only when the reader uses the 简/繁 switch. The next visit opens the same page in that script. Progress keys above are shared by both scripts. |
A day counts as checked-in when the learner submits a lesson quiz that day. Current streak is consecutive Shanghai dates ending today, or yesterday if today is not yet checked in.
lle2-* lesson show a lock badge until unlocked.lessons/<id>.html#quiz. Clear one item or clear all. Answer the same question correctly on retry and it disappears. Empty state: 「暂无错题」.pricing.html): pick 月付 US$5.99(30 天) or 季卡 US$13.99(90 天). The buttons call the Cloudflare Worker in worker/ only after payment.worker.baseUrl, payment.worker.unlockPublicKey, and both Pancake product ids in site.config.json are set and the pages are rebuilt. While any of those is empty, the buttons stay disabled and say 即将开放 / 即將開放, and the page does not call the network. The live product is Waffo Pancake (merchant of record), not the old acquiring gateway. Setup steps are in docs/waffo-cloudflare-setup.md. Nothing here is deployed. Use 退出解锁 on the pricing page to reset this browser. Unlock is kept only when localStorage holds a signed, unexpired credential. The public site origin is only site.config.json origin (still the GitHub Pages URL). A later move to Cloudflare Pages is prepared in .github/workflows/cloudflare-pages.yml; do not change origin until that host is checked. Purchases are one-time and do not auto-renew. Terms, privacy, and refund pages are linked from the footer. The contact mailbox is only contactEmail in site.config.json.Checkout is a Cloudflare Worker against Waffo Pancake. It is not deployed, and this repo has no API keys. payment.waffo.monthlyUrl and quarterlyUrl stay empty; the buttons use the Worker, and they stay disabled until the Worker address, unlock public key, and Pancake product ids are filled in. payment.waffo.returnUrl is unused by Pancake (the cashier does not auto-redirect). While it is empty, pricing-return.html stays out of the sitemap and is marked noindex. The intended flow is pay on the Pancake cashier, return to the site, and unlock automatically. See docs/waffo-cloudflare-setup.md. docs/payment-waffo-plan.md describes the abandoned acquiring-gateway plan. docs/ops-redeem.md is obsolete history.
data/codes.json in this public repo must stay {"codes":[]}. Do not commit unused codes. When that allowlist is empty, the browser accepts codes matching LLE-M-XXXXXX (monthly, 30 days) or LLE-Q-XXXXXX (quarterly, 90 days). That format check is not security. Anyone who can guess the pattern or skip the lock in DevTools can still open paid lesson pages.
Operators: do not collect payment by hand. The old redeem-code runbook is marked obsolete in docs/ops-redeem.md. Before merge, run bash scripts/check-codes-json.sh (fails if codes.length > 0).
Site copy: 免费试学仅 Level 1 第 1–5 课 · 打卡日历与错题本免费使用 · 开通解锁全部已上线课程(含 Level 1 + Level 2 已发布课)· 月付 US$5.99 / 30 天 · 季卡 US$13.99 / 90 天 · 一次性付款,不自动续费 · Waffo 付款按钮(链接为空时显示即将开放)· 付款确认后自动开通。
All lesson content lives in one file:
data/lessons.json
Shape:
{
"course": { "title": "...", "pitch": "...", "disclaimer": "..." },
"levels": [
{ "id": "lle1", "title": "Let's Learn English · Level 1", "lessons": [/* 52 */] },
{ "id": "lle2", "title": "Let's Learn English · Level 2", "lessons": [/* 30 */] }
]
}
Each lesson uses { id, number, title, subtitle, videoUrl, sourceUrl, dialogue, quiz, attribution }. Level 2 lessons also set "level": 2. Unlock treats a lesson as free only when level === 1 and number <= 5 (lle2-* is always paid). The Level 1 通关 banner still uses the 52 lle1 lessons only.
Serve the repo root over HTTP so the browser can fetch data/lessons.json:
python3 -m http.server 8080
Then open http://localhost:8080.
Opening index.html as a file URL will not load the JSON.
Install the Traditional Chinese converter, then run checks:
npm install
node --test js/*.test.js
bash scripts/check-codes-json.sh
bash scripts/build-mp-data.sh --check
bash scripts/sync-voa-videos.sh --dry-run
node scripts/build-pages.js --check
Simplified Chinese lives at the site root. Traditional Chinese (Taiwan wording) is a full mirror under zh-hant/: catalog, every lesson, progress, wrong-answer book, pricing, and the old lesson.html redirect. English lesson lines stay English. A 简/繁 control on each page links to the same screen in the other script.
Both versions share one origin, so localStorage progress, check-in, wrong-book, and unlock carry across. js/app.js loads data/web/ or data/web/zh-hant/ from the site root, not from the zh-hant/ folder.
The published origin is the single origin field in site.config.json. Canonical links, hreflang, Open Graph URLs, JSON-LD, sitemap.xml, robots.txt, and the Worker ALLOWED_ORIGIN all read that value at build time. It is https://eachsound.com. The Cloudflare Pages project name is eachsound (preview https://eachsound.pages.dev). The product name is 声声慢 / 聲聲慢, English Eachsound. The Worker name stays voa-lle-unlock. To switch origin:
origin in site.config.json (no trailing slash).npm install if needed, then node scripts/build-pages.js. That rewrite also copies origin into worker/wrangler.toml.Do not paste the origin into HTML or JS. The generator test fails if js/seo-pages.js hardcodes the host. Setup for the Pages project is in docs/waffo-cloudflare-setup.md.
URL pattern, prefixed with that origin:
| Script | Example |
|---|---|
| zh-Hans catalog | / |
| zh-Hant catalog | /zh-hant/ |
| zh-Hans lesson | /lessons/lle1-01.html |
| zh-Hant lesson | /zh-hant/lessons/lle1-01.html |
| Locked lesson | /lessons/lle1-06.html and /zh-hant/lessons/lle1-06.html |
| Level 2 | /lessons/lle2-01.html and /zh-hant/lessons/lle2-01.html |
| Sitemap | /sitemap.xml |
Each page sets <html lang> to zh-CN or zh-Hant, points its canonical at itself, and lists zh-Hans, zh-Hant, and x-default (the Simplified URL). The sitemap lists both sets with xhtml:link alternates.
Each lesson has a static HTML page generated from data/lessons.json. Traditional copy is produced in the same build with OpenCC s2twp (Simplified to Taiwan phrases), then a short override list in js/seo-pages.js (OVERRIDES): 腳本, 公共領域, 點擊播放, 網路, and 打開打卡/課表/頁面. Regenerate after editing lesson content or site.config.json:
npm install
node scripts/build-pages.js
node scripts/build-pages.js --check
--check fails when lessons/*.html, zh-hant/**/*.html, data/web/, js/i18n.js, sitemap.xml, or robots.txt drift from data/lessons.json. The same check runs inside node --test js/*.test.js.
The catalog and lesson pages do not download the full data/lessons.json. The home page embeds a slim catalog (data/web/catalog.json, or data/web/zh-hant/catalog.json on the Traditional site). Opening a lesson reads that page, and loads the matching per-lesson JSON only when the lesson is free or already unlocked.
Locked lesson HTML includes the Chinese title, a short intro, and the first two dialogue lines. The video URL, the rest of the dialogue, and the quiz stay out of the HTML until unlock. The per-lesson JSON files are still public, same as the old single JSON file: the paywall is a frontend gate, not a secret.
robots.txt in this repo allows crawling and points at {origin}/sitemap.xml. On a GitHub project page, crawlers read robots from the host root ({scheme}://{host}/robots.txt), not from the project path. The generated file names that host-root URL from site.config.json. Submit the sitemap URL in webmaster tools.
Local testing note: there are no public demo codes, and data/codes.json stays empty in git. Unlock unit tests use their own fixtures. To try the redeem form locally, generate a code with python3 scripts/gen-codes.py --monthly 1 --quarterly 0 and type it into the form (empty allowlist accepts LLE-M-* / LLE-Q-* format). Do not commit that code. Do not advertise codes to end users.
GitHub Pages still serves this repo from main at the origin in site.config.json. Open / for Simplified Chinese and /zh-hant/ for Traditional Chinese. A newly published site can take about 30 seconds to stabilize; if it does not load, refresh once. Cloudflare Pages is a separate workflow and does not replace this until the checklist in docs/waffo-cloudflare-setup.md is done.
The public launch is this website on GitHub Pages. The native mp-weixin app in miniprogram/ is paused; leave that folder in place. Open it in WeChat DevTools with placeholder AppID touristappid only if you resume that track.
Lesson <video> reads miniprogram/data/video-map.json (D1 placeholder: L1 lessons 1–5 on media.example.com). Do not point the player at Akamai. COS keys stay off-repo (.env.cos / mirror/ are gitignored). See the miniprogram README for the DevTools “don’t verify legal domain” switch.
Generate split lesson JSON (catalog + per-lesson, no videoUrl):
bash scripts/build-mp-data.sh
bash scripts/sync-voa-videos.sh --dry-run
node --test js/study.test.js js/unlock.test.js js/mp-scaffold.test.js js/video.test.js
In scope: catalog + fifty-two Level 1 lessons and thirty Level 2 lessons, with local progress, check-in, a wrong-answer book, and a frontend redeem-code paywall.
Out of scope: login, accounts, a real payment gateway, a crawler, or a backend.