From 17acf5ec80a0c0b0acc44a6a8373e154c68d4748 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Wed, 24 Jun 2026 08:59:31 -0700 Subject: 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/.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/.json. - Deploy hook publishes the locales/ subdir. Co-Authored-By: Claude Opus 4.8 (1M context) --- build.js | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'build.js') diff --git a/build.js b/build.js index e99b0a4..52b29af 100644 --- a/build.js +++ b/build.js @@ -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`); } -- cgit v1.2.3