diff options
| author | Adam Soltys <[email protected]> | 2026-06-24 08:59:31 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-06-24 08:59:31 -0700 |
| commit | 17acf5ec80a0c0b0acc44a6a8373e154c68d4748 (patch) | |
| tree | a3fb56b85cc9cd21962ed64b084294096948d75b /build.js | |
| parent | f59cca61111f38963099190bdbbce9a76bc3fafe (diff) | |
| download | coinosv3-17acf5ec80a0c0b0acc44a6a8373e154c68d4748.tar.gz coinosv3-17acf5ec80a0c0b0acc44a6a8373e154c68d4748.zip | |
Split translations into per-language files, lazy-loaded
Only English is bundled in index.html now; the other 19 languages are
emitted as dist/locales/<code>.json and fetched on demand when a visitor
actually uses one (via auto-detect, the picker, or ?lang=xx). This drops
index.html from ~511KB to ~318KB; a non-English visitor fetches just
their one ~8-10KB locale, which the service worker then caches for
offline use. t() falls back to English until a locale resolves.
- i18n.js keeps only en + a loadLocale() fetcher; locale data moved to
src/locales/*.json.
- build.js emits dist/locales/; dev.js serves /locales/<code>.json.
- Deploy hook publishes the locales/ subdir.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Diffstat (limited to 'build.js')
| -rw-r--r-- | build.js | 25 |
1 files changed, 22 insertions, 3 deletions
@@ -9,7 +9,7 @@ // installed. index.html stays self-contained; these are optional extras that // simply 404 (harmlessly) when the file is opened on its own from file://. -import { mkdir } from 'node:fs/promises'; +import { mkdir, readdir } from 'node:fs/promises'; const FAVICON = 'data:image/svg+xml,' + @@ -77,7 +77,21 @@ self.addEventListener('fetch', (e) => { ); return; } - e.respondWith(caches.match(req).then((hit) => hit || fetch(req))); + // Cache-first, but cache same-origin assets (e.g. the chosen locale, icons) + // on first fetch so they're available offline without precaching them all. + e.respondWith( + caches.match(req).then( + (hit) => + hit || + fetch(req).then((res) => { + if (res.ok) { + const copy = res.clone(); + caches.open(CACHE).then((c) => c.put(req, copy)); + } + return res; + }) + ) + ); }); `; @@ -148,6 +162,11 @@ if (import.meta.main) { // browser without BarcodeDetector opens the scanner. await Bun.write('dist/jsqr.js', await buildJsQr()); + // Per-language strings — fetched on demand so visitors only download theirs. + await mkdir('dist/locales', { recursive: true }); + const locales = await readdir('src/locales'); + for (const f of locales) await Bun.write('dist/locales/' + f, Bun.file('src/locales/' + f)); + // PWA sidecars. await Bun.write('dist/manifest.webmanifest', JSON.stringify(MANIFEST, null, 2)); const version = Bun.hash(html).toString(36); @@ -156,5 +175,5 @@ if (import.meta.main) { const kb = (Buffer.byteLength(html) / 1024).toFixed(0); console.log(`✓ dist/index.html written (${kb} KB) — open it offline, no server needed`); - console.log(`✓ PWA: manifest.webmanifest, sw.js (cold-${version}), ${STATIC.length} icons, jsqr.js`); + console.log(`✓ PWA: manifest.webmanifest, sw.js (cold-${version}), ${STATIC.length} icons, jsqr.js, ${locales.length} locales`); } |
